Compare commits
	
		
			5 Commits
		
	
	
		
			v4.0.0-rc2
			...
			singlestep
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					34370ee6ff | ||
| 
						 | 
					9953b32e77 | ||
| 
						 | 
					0da2fd84e1 | ||
| 
						 | 
					b82417e8bf | ||
| 
						 | 
					0b72719af8 | 
							
								
								
									
										15
									
								
								.cirrus.yml
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								.cirrus.yml
									
									
									
									
									
								
							@@ -1,11 +1,10 @@
 | 
			
		||||
env:
 | 
			
		||||
  CIRRUS_CLONE_DEPTH: 1
 | 
			
		||||
 | 
			
		||||
freebsd_12_task:
 | 
			
		||||
  freebsd_instance:
 | 
			
		||||
    image: freebsd-12-0-release-amd64
 | 
			
		||||
    cpu: 8
 | 
			
		||||
    memory: 8G
 | 
			
		||||
  env:
 | 
			
		||||
    CIRRUS_CLONE_DEPTH: 1
 | 
			
		||||
  install_script: pkg install -y
 | 
			
		||||
    bison curl cyrus-sasl git glib gmake gnutls
 | 
			
		||||
    nettle perl5 pixman pkgconf png usbredir
 | 
			
		||||
@@ -15,13 +14,3 @@ freebsd_12_task:
 | 
			
		||||
    - ../configure || { cat config.log; exit 1; }
 | 
			
		||||
    - gmake -j8
 | 
			
		||||
    - gmake -j8 V=1 check
 | 
			
		||||
 | 
			
		||||
macos_task:
 | 
			
		||||
  osx_instance:
 | 
			
		||||
    image: mojave-base
 | 
			
		||||
  install_script:
 | 
			
		||||
    - brew install pkg-config python glib pixman make sdl2
 | 
			
		||||
  script:
 | 
			
		||||
    - ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
 | 
			
		||||
    - gmake -j$(sysctl -n hw.ncpu)
 | 
			
		||||
    - gmake check -j$(sysctl -n hw.ncpu)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,3 @@
 | 
			
		||||
/.doctrees
 | 
			
		||||
/config-devices.*
 | 
			
		||||
/config-all-devices.*
 | 
			
		||||
/config-all-disas.*
 | 
			
		||||
@@ -6,7 +5,6 @@
 | 
			
		||||
/config-target.*
 | 
			
		||||
/config.status
 | 
			
		||||
/config-temp
 | 
			
		||||
/elf2dmp
 | 
			
		||||
/trace-events-all
 | 
			
		||||
/trace/generated-events.h
 | 
			
		||||
/trace/generated-events.c
 | 
			
		||||
@@ -120,7 +118,6 @@
 | 
			
		||||
/pc-bios/optionrom/kvmvapic.img
 | 
			
		||||
/pc-bios/s390-ccw/s390-ccw.elf
 | 
			
		||||
/pc-bios/s390-ccw/s390-ccw.img
 | 
			
		||||
/docs/built
 | 
			
		||||
/docs/interop/qemu-ga-qapi.texi
 | 
			
		||||
/docs/interop/qemu-ga-ref.html
 | 
			
		||||
/docs/interop/qemu-ga-ref.info*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -49,6 +49,3 @@
 | 
			
		||||
[submodule "tests/fp/berkeley-softfloat-3"]
 | 
			
		||||
	path = tests/fp/berkeley-softfloat-3
 | 
			
		||||
	url = https://github.com/cota/berkeley-softfloat-3
 | 
			
		||||
[submodule "roms/edk2"]
 | 
			
		||||
	path = roms/edk2
 | 
			
		||||
	url = https://github.com/tianocore/edk2.git
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -61,8 +61,7 @@ env:
 | 
			
		||||
    - BUILD_DIR="."
 | 
			
		||||
    - BASE_CONFIG="--disable-docs --disable-tools"
 | 
			
		||||
    - TEST_CMD="make check -j3 V=1"
 | 
			
		||||
    # This is broadly a list of "mainline" softmmu targets which have support across the major distros
 | 
			
		||||
    - MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
git:
 | 
			
		||||
  # we want to do this ourselves
 | 
			
		||||
@@ -82,21 +81,10 @@ matrix:
 | 
			
		||||
        - CONFIG="--disable-system"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # we split the system builds as it takes a while to build them all
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
        - CONFIG="--disable-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Just build tools and run minimal unit and softfloat checks
 | 
			
		||||
    - env:
 | 
			
		||||
        - BASE_CONFIG="--enable-tools"
 | 
			
		||||
        - CONFIG="--disable-user --disable-system"
 | 
			
		||||
        - TEST_CMD="make check-unit check-softfloat -j3"
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-debug --enable-debug-tcg --disable-user"
 | 
			
		||||
 | 
			
		||||
@@ -107,12 +95,11 @@ matrix:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
        - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-user --disable-replication"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Module builds are mostly of interest to major distros
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
        - CONFIG="--enable-modules --disable-linux-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Alternate coroutines implementations are only really of interest to KVM users
 | 
			
		||||
@@ -127,9 +114,8 @@ matrix:
 | 
			
		||||
        - TEST_CMD="make check-unit -j3 V=1"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Check we can build docs and tools (out of tree)
 | 
			
		||||
    # Check we can build docs and tools
 | 
			
		||||
    - env:
 | 
			
		||||
        - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
 | 
			
		||||
        - BASE_CONFIG="--enable-tools --enable-docs"
 | 
			
		||||
        - CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
 | 
			
		||||
      addons:
 | 
			
		||||
@@ -139,6 +125,11 @@ matrix:
 | 
			
		||||
            - texinfo
 | 
			
		||||
            - perl
 | 
			
		||||
 | 
			
		||||
    # Test out-of-tree builds
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-debug --enable-debug-tcg"
 | 
			
		||||
        - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Test with Clang for compile portability (Travis uses clang-5.0)
 | 
			
		||||
    - env:
 | 
			
		||||
@@ -147,28 +138,17 @@ matrix:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
        - CONFIG="--disable-user"
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # gprof/gcov are GCC features
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
        - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
 | 
			
		||||
      after_success:
 | 
			
		||||
        - ${SRC_DIR}/scripts/travis/coverage-summary.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # We manually include builds which we disable "make check" for
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--without-default-devices --disable-user"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # We manually include builds which we disable "make check" for
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-debug --enable-tcg-interpreter"
 | 
			
		||||
@@ -193,19 +173,12 @@ matrix:
 | 
			
		||||
 | 
			
		||||
    # MacOSX builds
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
 | 
			
		||||
        - CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
 | 
			
		||||
      os: osx
 | 
			
		||||
      osx_image: xcode9.4
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
 | 
			
		||||
      os: osx
 | 
			
		||||
      osx_image: xcode10.2
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Python builds
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=x86_64-softmmu"
 | 
			
		||||
@@ -276,12 +249,6 @@ matrix:
 | 
			
		||||
        - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Run check-tcg against linux-user
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-system"
 | 
			
		||||
        - TEST_CMD="make -j3 check-tcg V=1"
 | 
			
		||||
 | 
			
		||||
    # Run check-tcg against softmmu targets
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=xtensa-softmmu,arm-softmmu"
 | 
			
		||||
        - TEST_CMD="make -j3 check-tcg V=1"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								Kconfig.host
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								Kconfig.host
									
									
									
									
									
								
							@@ -1,36 +0,0 @@
 | 
			
		||||
# These are "proxy" symbols used to pass config-host.mak values
 | 
			
		||||
# down to Kconfig.  See also MINIKCONF_ARGS in the Makefile:
 | 
			
		||||
# these two need to be kept in sync.
 | 
			
		||||
 | 
			
		||||
config KVM
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config LINUX
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config OPENGL
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config X11
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config SPICE
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config IVSHMEM
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config TPM
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config VHOST_USER
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config XEN
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config VIRTFS
 | 
			
		||||
    bool
 | 
			
		||||
 | 
			
		||||
config PVRDMA
 | 
			
		||||
    bool
 | 
			
		||||
							
								
								
									
										98
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								MAINTAINERS
									
									
									
									
									
								
							@@ -117,8 +117,6 @@ F: cpus.c
 | 
			
		||||
F: exec.c
 | 
			
		||||
F: accel/tcg/
 | 
			
		||||
F: accel/stubs/tcg-stub.c
 | 
			
		||||
F: scripts/decodetree.py
 | 
			
		||||
F: docs/devel/decodetree.rst
 | 
			
		||||
F: include/exec/cpu*.h
 | 
			
		||||
F: include/exec/exec-all.h
 | 
			
		||||
F: include/exec/helper*.h
 | 
			
		||||
@@ -340,7 +338,6 @@ F: include/hw/tricore/
 | 
			
		||||
 | 
			
		||||
Multiarch Linux User Tests
 | 
			
		||||
M: Alex Bennée <alex.bennee@linaro.org>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: tests/tcg/multiarch/
 | 
			
		||||
 | 
			
		||||
Guest CPU Cores (KVM):
 | 
			
		||||
@@ -428,20 +425,15 @@ Hosts:
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
LINUX
 | 
			
		||||
M: Michael S. Tsirkin <mst@redhat.com>
 | 
			
		||||
M: Cornelia Huck <cohuck@redhat.com>
 | 
			
		||||
M: Paolo Bonzini <pbonzini@redhat.com>
 | 
			
		||||
L: qemu-devel@nongnu.org
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: linux-*
 | 
			
		||||
F: linux-headers/
 | 
			
		||||
F: scripts/update-linux-headers.sh
 | 
			
		||||
 | 
			
		||||
POSIX
 | 
			
		||||
M: Paolo Bonzini <pbonzini@redhat.com>
 | 
			
		||||
L: qemu-devel@nongnu.org
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: os-posix.c
 | 
			
		||||
F: include/sysemu/os-posix.h
 | 
			
		||||
F: util/*posix*.c
 | 
			
		||||
F: include/qemu/*posix*.h
 | 
			
		||||
F: *posix*
 | 
			
		||||
 | 
			
		||||
NETBSD
 | 
			
		||||
L: qemu-devel@nongnu.org
 | 
			
		||||
@@ -641,8 +633,6 @@ F: hw/misc/iotkit-sysinfo.c
 | 
			
		||||
F: include/hw/misc/iotkit-sysinfo.h
 | 
			
		||||
F: hw/misc/armsse-cpuid.c
 | 
			
		||||
F: include/hw/misc/armsse-cpuid.h
 | 
			
		||||
F: hw/misc/armsse-mhu.c
 | 
			
		||||
F: include/hw/misc/armsse-mhu.h
 | 
			
		||||
 | 
			
		||||
Musca
 | 
			
		||||
M: Peter Maydell <peter.maydell@linaro.org>
 | 
			
		||||
@@ -861,15 +851,6 @@ S: Maintained
 | 
			
		||||
F: hw/cris/axis_dev88.c
 | 
			
		||||
F: hw/*/etraxfs_*.c
 | 
			
		||||
 | 
			
		||||
HP-PARISC Machines
 | 
			
		||||
------------------
 | 
			
		||||
Dino
 | 
			
		||||
M: Richard Henderson <rth@twiddle.net>
 | 
			
		||||
R: Helge Deller <deller@gmx.de>
 | 
			
		||||
S: Odd Fixes
 | 
			
		||||
F: hw/hppa/
 | 
			
		||||
F: pc-bios/hppa-firmware.img
 | 
			
		||||
 | 
			
		||||
LM32 Machines
 | 
			
		||||
-------------
 | 
			
		||||
EVR32 and uclinux BSP
 | 
			
		||||
@@ -986,7 +967,6 @@ L: qemu-ppc@nongnu.org
 | 
			
		||||
S: Odd Fixes
 | 
			
		||||
F: hw/ppc/e500*
 | 
			
		||||
F: hw/gpio/mpc8xxx.c
 | 
			
		||||
F: hw/i2c/mpc_i2c.c
 | 
			
		||||
F: hw/net/fsl_etsec/
 | 
			
		||||
F: hw/pci-host/ppce500.c
 | 
			
		||||
F: include/hw/ppc/ppc_e500.h
 | 
			
		||||
@@ -1061,6 +1041,7 @@ F: include/hw/*/xics*
 | 
			
		||||
F: pc-bios/spapr-rtas/*
 | 
			
		||||
F: pc-bios/spapr-rtas.bin
 | 
			
		||||
F: pc-bios/slof.bin
 | 
			
		||||
F: pc-bios/skiboot.lid
 | 
			
		||||
F: docs/specs/ppc-spapr-hcalls.txt
 | 
			
		||||
F: docs/specs/ppc-spapr-hotplug.txt
 | 
			
		||||
F: tests/spapr*
 | 
			
		||||
@@ -1068,18 +1049,6 @@ F: tests/libqos/*spapr*
 | 
			
		||||
F: tests/rtas*
 | 
			
		||||
F: tests/libqos/rtas*
 | 
			
		||||
 | 
			
		||||
PowerNV (Non-Virtualized)
 | 
			
		||||
M: Cédric Le Goater <clg@kaod.org>
 | 
			
		||||
M: David Gibson <david@gibson.dropbear.id.au>
 | 
			
		||||
L: qemu-ppc@nongnu.org
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/ppc/pnv*
 | 
			
		||||
F: hw/intc/pnv*
 | 
			
		||||
F: hw/intc/xics_pnv.c
 | 
			
		||||
F: include/hw/ppc/pnv*
 | 
			
		||||
F: pc-bios/skiboot.lid
 | 
			
		||||
F: tests/pnv*
 | 
			
		||||
 | 
			
		||||
virtex_ml507
 | 
			
		||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
 | 
			
		||||
L: qemu-ppc@nongnu.org
 | 
			
		||||
@@ -1120,27 +1089,20 @@ M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/sparc/sun4m.c
 | 
			
		||||
F: hw/sparc/sun4m_iommu.c
 | 
			
		||||
F: hw/display/cg3.c
 | 
			
		||||
F: hw/display/tcx.c
 | 
			
		||||
F: hw/dma/sparc32_dma.c
 | 
			
		||||
F: hw/misc/eccmemctl.c
 | 
			
		||||
F: hw/*/slavio_*.c
 | 
			
		||||
F: include/hw/nvram/sun_nvram.h
 | 
			
		||||
F: hw/misc/slavio_misc.c
 | 
			
		||||
F: include/hw/sparc/sparc32_dma.h
 | 
			
		||||
F: include/hw/sparc/sun4m_iommu.h
 | 
			
		||||
F: pc-bios/openbios-sparc32
 | 
			
		||||
F: include/hw/sparc/sun4m_iommu.h
 | 
			
		||||
 | 
			
		||||
Sun4u
 | 
			
		||||
M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/sparc64/sun4u.c
 | 
			
		||||
F: hw/sparc64/sun4u_iommu.c
 | 
			
		||||
F: include/hw/sparc/sun4u_iommu.h
 | 
			
		||||
F: pc-bios/openbios-sparc64
 | 
			
		||||
F: hw/pci-host/sabre.c
 | 
			
		||||
F: include/hw/pci-host/sabre.h
 | 
			
		||||
F: hw/pci-bridge/simba.c
 | 
			
		||||
F: include/hw/pci-bridge/simba.h
 | 
			
		||||
F: pc-bios/openbios-sparc64
 | 
			
		||||
 | 
			
		||||
Sun4v
 | 
			
		||||
M: Artyom Tarasenko <atar4qemu@gmail.com>
 | 
			
		||||
@@ -1219,10 +1181,6 @@ F: hw/acpi/ich9.c
 | 
			
		||||
F: include/hw/acpi/ich9.h
 | 
			
		||||
F: include/hw/acpi/piix4.h
 | 
			
		||||
F: hw/misc/sga.c
 | 
			
		||||
F: hw/isa/apm.c
 | 
			
		||||
F: include/hw/isa/apm.h
 | 
			
		||||
F: tests/test-x86-cpuid.c
 | 
			
		||||
F: tests/test-x86-cpuid-compat.c
 | 
			
		||||
 | 
			
		||||
PC Chipset
 | 
			
		||||
M: Michael S. Tsirkin <mst@redhat.com>
 | 
			
		||||
@@ -1466,7 +1424,6 @@ vhost
 | 
			
		||||
M: Michael S. Tsirkin <mst@redhat.com>
 | 
			
		||||
S: Supported
 | 
			
		||||
F: hw/*/*vhost*
 | 
			
		||||
F: docs/interop/vhost-user.json
 | 
			
		||||
F: docs/interop/vhost-user.txt
 | 
			
		||||
F: contrib/vhost-user-*/
 | 
			
		||||
 | 
			
		||||
@@ -1813,8 +1770,7 @@ F: qom/cpu.c
 | 
			
		||||
F: include/qom/cpu.h
 | 
			
		||||
 | 
			
		||||
Device Tree
 | 
			
		||||
M: Alistair Francis <alistair.francis@wdc.com>
 | 
			
		||||
R: David Gibson <david@gibson.dropbear.id.au>
 | 
			
		||||
M: Alexander Graf <agraf@suse.de>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: device_tree.c
 | 
			
		||||
F: include/sysemu/device_tree.h
 | 
			
		||||
@@ -1839,7 +1795,8 @@ F: util/error.c
 | 
			
		||||
F: util/qemu-error.c
 | 
			
		||||
 | 
			
		||||
GDB stub
 | 
			
		||||
S: Orphan
 | 
			
		||||
L: qemu-devel@nongnu.org
 | 
			
		||||
S: Odd Fixes
 | 
			
		||||
F: gdbstub*
 | 
			
		||||
F: gdb-xml/
 | 
			
		||||
 | 
			
		||||
@@ -1975,14 +1932,10 @@ F: include/qapi/qmp/
 | 
			
		||||
X: include/qapi/qmp/dispatch.h
 | 
			
		||||
F: scripts/coccinelle/qobject.cocci
 | 
			
		||||
F: tests/check-qdict.c
 | 
			
		||||
F: tests/check-qnum.c
 | 
			
		||||
F: tests/check-qjson.c
 | 
			
		||||
F: tests/check-qlist.c
 | 
			
		||||
F: tests/check-qlit.c
 | 
			
		||||
F: tests/check-qnull.c
 | 
			
		||||
F: tests/check-qnum.c
 | 
			
		||||
F: tests/check-qobject.c
 | 
			
		||||
F: tests/check-qstring.c
 | 
			
		||||
F: tests/data/qobject/qdict.txt
 | 
			
		||||
T: git https://repo.or.cz/qemu/armbru.git qapi-next
 | 
			
		||||
 | 
			
		||||
QEMU Guest Agent
 | 
			
		||||
@@ -2104,14 +2057,11 @@ F: crypto/
 | 
			
		||||
F: include/crypto/
 | 
			
		||||
F: tests/test-crypto-*
 | 
			
		||||
F: tests/benchmark-crypto-*
 | 
			
		||||
F: tests/crypto-tls-*
 | 
			
		||||
F: tests/pkix_asn1_tab.c
 | 
			
		||||
F: qemu.sasl
 | 
			
		||||
 | 
			
		||||
Coroutines
 | 
			
		||||
M: Stefan Hajnoczi <stefanha@redhat.com>
 | 
			
		||||
M: Kevin Wolf <kwolf@redhat.com>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: util/*coroutine*
 | 
			
		||||
F: include/qemu/coroutine*
 | 
			
		||||
F: tests/test-coroutine.c
 | 
			
		||||
@@ -2179,7 +2129,7 @@ F: include/migration/failover.h
 | 
			
		||||
F: docs/COLO-FT.txt
 | 
			
		||||
 | 
			
		||||
COLO Proxy
 | 
			
		||||
M: Zhang Chen <chen.zhang@intel.com>
 | 
			
		||||
M: Zhang Chen <zhangckid@gmail.com>
 | 
			
		||||
M: Li Zhijian <lizhijian@cn.fujitsu.com>
 | 
			
		||||
S: Supported
 | 
			
		||||
F: docs/colo-proxy.txt
 | 
			
		||||
@@ -2210,18 +2160,6 @@ M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: contrib/elf2dmp/
 | 
			
		||||
 | 
			
		||||
I2C and SMBus
 | 
			
		||||
M: Corey Minyard <cminyard@mvista.com>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/i2c/core.c
 | 
			
		||||
F: hw/i2c/smbus_slave.c
 | 
			
		||||
F: hw/i2c/smbus_master.c
 | 
			
		||||
F: hw/i2c/smbus_eeprom.c
 | 
			
		||||
F: include/hw/i2c/i2c.h
 | 
			
		||||
F: include/hw/i2c/smbus_master.h
 | 
			
		||||
F: include/hw/i2c/smbus_slave.h
 | 
			
		||||
F: include/hw/i2c/smbus_eeprom.h
 | 
			
		||||
 | 
			
		||||
Usermode Emulation
 | 
			
		||||
------------------
 | 
			
		||||
Overall
 | 
			
		||||
@@ -2267,7 +2205,7 @@ F: tcg/arm/
 | 
			
		||||
F: disas/arm.c
 | 
			
		||||
 | 
			
		||||
i386 target
 | 
			
		||||
M: Richard Henderson <rth@twiddle.net>
 | 
			
		||||
L: qemu-devel@nongnu.org
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: tcg/i386/
 | 
			
		||||
F: disas/i386.c
 | 
			
		||||
@@ -2286,6 +2224,7 @@ F: tcg/ppc/
 | 
			
		||||
F: disas/ppc.c
 | 
			
		||||
 | 
			
		||||
RISC-V
 | 
			
		||||
M: Michael Clark <mjc@sifive.com>
 | 
			
		||||
M: Palmer Dabbelt <palmer@sifive.com>
 | 
			
		||||
M: Alistair Francis <Alistair.Francis@wdc.com>
 | 
			
		||||
L: qemu-riscv@nongnu.org
 | 
			
		||||
@@ -2558,7 +2497,6 @@ F: .gitlab-ci.yml
 | 
			
		||||
Guest Test Compilation Support
 | 
			
		||||
M: Alex Bennée <alex.bennee@linaro.org>
 | 
			
		||||
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: tests/tcg/Makefile
 | 
			
		||||
F: tests/tcg/Makefile.include
 | 
			
		||||
L: qemu-devel@nongnu.org
 | 
			
		||||
@@ -2586,9 +2524,3 @@ GIT submodules
 | 
			
		||||
M: Daniel P. Berrange <berrange@redhat.com>
 | 
			
		||||
S: Odd Fixes
 | 
			
		||||
F: scripts/git-submodule.sh
 | 
			
		||||
 | 
			
		||||
Sphinx documentation configuration and build machinery
 | 
			
		||||
M: Peter Maydell <peter.maydell@linaro.org>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: docs/conf.py
 | 
			
		||||
F: docs/*/conf.py
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										139
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								Makefile
									
									
									
									
									
								
							@@ -87,20 +87,6 @@ endif
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
 | 
			
		||||
# Create QEMU_PKGVERSION and FULL_VERSION strings
 | 
			
		||||
# If PKGVERSION is set, use that; otherwise get version and -dirty status from git
 | 
			
		||||
QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \
 | 
			
		||||
  cd $(SRC_PATH); \
 | 
			
		||||
  if test -e .git; then \
 | 
			
		||||
    git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
 | 
			
		||||
    if ! git diff-index --quiet HEAD &>/dev/null; then \
 | 
			
		||||
      echo "-dirty"; \
 | 
			
		||||
    fi; \
 | 
			
		||||
  fi))
 | 
			
		||||
 | 
			
		||||
# Either "version (pkgversion)", or just "version" if pkgversion not set
 | 
			
		||||
FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION))
 | 
			
		||||
 | 
			
		||||
GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
 | 
			
		||||
 | 
			
		||||
GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c
 | 
			
		||||
@@ -327,14 +313,14 @@ DOCS=
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
 | 
			
		||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS)))
 | 
			
		||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK))
 | 
			
		||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
 | 
			
		||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
 | 
			
		||||
 | 
			
		||||
ifeq ($(SUBDIR_DEVICES_MAK),)
 | 
			
		||||
config-all-devices.mak: config-host.mak
 | 
			
		||||
config-all-devices.mak:
 | 
			
		||||
	$(call quiet-command,echo '# no devices' > $@,"GEN","$@")
 | 
			
		||||
else
 | 
			
		||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK) config-host.mak
 | 
			
		||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
 | 
			
		||||
	$(call quiet-command, sed -n \
 | 
			
		||||
             's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \
 | 
			
		||||
             $(SUBDIR_DEVICES_MAK) | sort -u > $@, \
 | 
			
		||||
@@ -343,27 +329,9 @@ endif
 | 
			
		||||
 | 
			
		||||
-include $(SUBDIR_DEVICES_MAK_DEP)
 | 
			
		||||
 | 
			
		||||
# This has to be kept in sync with Kconfig.host.
 | 
			
		||||
MINIKCONF_ARGS = \
 | 
			
		||||
    $(CONFIG_MINIKCONF_MODE) \
 | 
			
		||||
    $@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \
 | 
			
		||||
    CONFIG_KVM=$(CONFIG_KVM) \
 | 
			
		||||
    CONFIG_SPICE=$(CONFIG_SPICE) \
 | 
			
		||||
    CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
 | 
			
		||||
    CONFIG_TPM=$(CONFIG_TPM) \
 | 
			
		||||
    CONFIG_XEN=$(CONFIG_XEN) \
 | 
			
		||||
    CONFIG_OPENGL=$(CONFIG_OPENGL) \
 | 
			
		||||
    CONFIG_X11=$(CONFIG_X11) \
 | 
			
		||||
    CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
 | 
			
		||||
    CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
 | 
			
		||||
    CONFIG_LINUX=$(CONFIG_LINUX) \
 | 
			
		||||
    CONFIG_PVRDMA=$(CONFIG_PVRDMA)
 | 
			
		||||
 | 
			
		||||
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
 | 
			
		||||
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
 | 
			
		||||
 | 
			
		||||
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
	$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
 | 
			
		||||
%/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
            $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp")
 | 
			
		||||
	$(call quiet-command, if test -f $@; then \
 | 
			
		||||
	  if cmp -s $@.old $@; then \
 | 
			
		||||
	    mv $@.tmp $@; \
 | 
			
		||||
@@ -415,16 +383,32 @@ dummy := $(call unnest-vars,, \
 | 
			
		||||
                ui-obj-m \
 | 
			
		||||
                audio-obj-y \
 | 
			
		||||
                audio-obj-m \
 | 
			
		||||
                trace-obj-y)
 | 
			
		||||
                trace-obj-y \
 | 
			
		||||
                slirp-obj-y)
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/tests/Makefile.include
 | 
			
		||||
 | 
			
		||||
all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules
 | 
			
		||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
 | 
			
		||||
 | 
			
		||||
qemu-version.h: FORCE
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
                (printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \
 | 
			
		||||
		printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \
 | 
			
		||||
		(cd $(SRC_PATH); \
 | 
			
		||||
		if test -n "$(PKGVERSION)"; then \
 | 
			
		||||
			pkgvers="$(PKGVERSION)"; \
 | 
			
		||||
		else \
 | 
			
		||||
			if test -d .git; then \
 | 
			
		||||
				pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
 | 
			
		||||
				if ! git diff-index --quiet HEAD &>/dev/null; then \
 | 
			
		||||
					pkgvers="$${pkgvers}-dirty"; \
 | 
			
		||||
				fi; \
 | 
			
		||||
			fi; \
 | 
			
		||||
		fi; \
 | 
			
		||||
		printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
 | 
			
		||||
		if test -n "$${pkgvers}"; then \
 | 
			
		||||
			printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
 | 
			
		||||
		else \
 | 
			
		||||
			printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
 | 
			
		||||
		fi; \
 | 
			
		||||
		) > $@.tmp)
 | 
			
		||||
	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
 | 
			
		||||
	  mv $@.tmp $@; \
 | 
			
		||||
@@ -474,10 +458,7 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86
 | 
			
		||||
subdir-capstone: .git-submodule-status
 | 
			
		||||
	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE))
 | 
			
		||||
 | 
			
		||||
subdir-slirp: .git-submodule-status
 | 
			
		||||
	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS)")
 | 
			
		||||
 | 
			
		||||
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \
 | 
			
		||||
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) $(slirp-obj-y) \
 | 
			
		||||
	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
 | 
			
		||||
 | 
			
		||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
 | 
			
		||||
@@ -498,7 +479,7 @@ Makefile: $(version-obj-y)
 | 
			
		||||
# Build libraries
 | 
			
		||||
 | 
			
		||||
libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y)
 | 
			
		||||
libvhost-user.a: $(libvhost-user-obj-y) $(util-obj-y) $(stub-obj-y)
 | 
			
		||||
libvhost-user.a: $(libvhost-user-obj-y)
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
 | 
			
		||||
@@ -628,11 +609,7 @@ clean:
 | 
			
		||||
	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 | 
			
		||||
	rm -f qemu-options.def
 | 
			
		||||
	rm -f *.msi
 | 
			
		||||
	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \
 | 
			
		||||
		! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \
 | 
			
		||||
		! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \
 | 
			
		||||
		! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
 | 
			
		||||
		-exec rm {} +
 | 
			
		||||
	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
 | 
			
		||||
	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
 | 
			
		||||
	rm -f fsdev/*.pod scsi/*.pod
 | 
			
		||||
	rm -f qemu-img-cmds.h
 | 
			
		||||
@@ -656,22 +633,6 @@ dist: qemu-$(VERSION).tar.bz2
 | 
			
		||||
qemu-%.tar.bz2:
 | 
			
		||||
	$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
 | 
			
		||||
 | 
			
		||||
# Sphinx does not allow building manuals into the same directory as
 | 
			
		||||
# the source files, so if we're doing an in-tree QEMU build we must
 | 
			
		||||
# build the manuals into a subdirectory (and then install them from
 | 
			
		||||
# there for 'make install'). For an out-of-tree build we can just
 | 
			
		||||
# use the docs/ subdirectory in the build tree as normal.
 | 
			
		||||
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
 | 
			
		||||
MANUAL_BUILDDIR := docs/built
 | 
			
		||||
else
 | 
			
		||||
MANUAL_BUILDDIR := docs
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
define clean-manual =
 | 
			
		||||
rm -rf $(MANUAL_BUILDDIR)/$1/_static
 | 
			
		||||
rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html
 | 
			
		||||
endef
 | 
			
		||||
 | 
			
		||||
distclean: clean
 | 
			
		||||
	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
 | 
			
		||||
	rm -f config-all-devices.mak config-all-disas.mak config.status
 | 
			
		||||
@@ -692,9 +653,6 @@ distclean: clean
 | 
			
		||||
	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
 | 
			
		||||
	rm -f docs/qemu-block-drivers.7
 | 
			
		||||
	rm -f docs/qemu-cpu-models.7
 | 
			
		||||
	rm -rf .doctrees
 | 
			
		||||
	$(call clean-manual,devel)
 | 
			
		||||
	$(call clean-manual,interop)
 | 
			
		||||
	for d in $(TARGET_DIRS); do \
 | 
			
		||||
	rm -rf $$d || exit 1 ; \
 | 
			
		||||
        done
 | 
			
		||||
@@ -728,20 +686,7 @@ else
 | 
			
		||||
BLOBS=
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Note that we manually filter-out the non-Sphinx documentation which
 | 
			
		||||
# is currently built into the docs/interop directory in the build tree.
 | 
			
		||||
define install-manual =
 | 
			
		||||
for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
 | 
			
		||||
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
 | 
			
		||||
endef
 | 
			
		||||
 | 
			
		||||
# Note that we deliberately do not install the "devel" manual: it is
 | 
			
		||||
# for QEMU developers, and not interesting to our users.
 | 
			
		||||
.PHONY: install-sphinxdocs
 | 
			
		||||
install-sphinxdocs: sphinxdocs
 | 
			
		||||
	$(call install-manual,interop)
 | 
			
		||||
 | 
			
		||||
install-doc: $(DOCS) install-sphinxdocs
 | 
			
		||||
install-doc: $(DOCS)
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
 | 
			
		||||
	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
 | 
			
		||||
	$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
 | 
			
		||||
@@ -892,23 +837,6 @@ docs/version.texi: $(SRC_PATH)/VERSION
 | 
			
		||||
%.pdf: %.texi docs/version.texi
 | 
			
		||||
	$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
 | 
			
		||||
 | 
			
		||||
# Sphinx builds all its documentation at once in one invocation
 | 
			
		||||
# and handles "don't rebuild things unless necessary" itself.
 | 
			
		||||
# The '.doctrees' files are cached information to speed this up.
 | 
			
		||||
.PHONY: sphinxdocs
 | 
			
		||||
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html
 | 
			
		||||
 | 
			
		||||
# Canned command to build a single manual
 | 
			
		||||
build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
 | 
			
		||||
# We assume all RST files in the manual's directory are used in it
 | 
			
		||||
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
 | 
			
		||||
 | 
			
		||||
$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
 | 
			
		||||
	$(call build-manual,devel)
 | 
			
		||||
 | 
			
		||||
$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
 | 
			
		||||
	$(call build-manual,interop)
 | 
			
		||||
 | 
			
		||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
 | 
			
		||||
 | 
			
		||||
@@ -937,7 +865,7 @@ docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
 | 
			
		||||
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
 | 
			
		||||
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
 | 
			
		||||
 | 
			
		||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
 | 
			
		||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
 | 
			
		||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
 | 
			
		||||
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
 | 
			
		||||
@@ -965,8 +893,7 @@ $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
 | 
			
		||||
%/coverage-report.html:
 | 
			
		||||
	@mkdir -p $*
 | 
			
		||||
	$(call quiet-command,\
 | 
			
		||||
		gcovr -r $(SRC_PATH) --object-directory $(BUILD_PATH) \
 | 
			
		||||
		-p --html --html-details -o $@, \
 | 
			
		||||
		gcovr -p --html --html-details -o $@, \
 | 
			
		||||
		"GEN", "coverage-report.html")
 | 
			
		||||
 | 
			
		||||
.PHONY: coverage-report
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ stub-obj-y = stubs/ util/ crypto/
 | 
			
		||||
util-obj-y = util/ qobject/ qapi/
 | 
			
		||||
 | 
			
		||||
chardev-obj-y = chardev/
 | 
			
		||||
slirp-obj-$(CONFIG_SLIRP) = slirp/
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# authz-obj-y is code used by both qemu system emulation and qemu-img
 | 
			
		||||
@@ -13,7 +14,7 @@ authz-obj-y = authz/
 | 
			
		||||
#######################################################################
 | 
			
		||||
# block-obj-y is code used by both qemu system emulation and qemu-img
 | 
			
		||||
 | 
			
		||||
block-obj-y = nbd/
 | 
			
		||||
block-obj-y += nbd/
 | 
			
		||||
block-obj-y += block.o blockjob.o job.o
 | 
			
		||||
block-obj-y += block/ scsi/
 | 
			
		||||
block-obj-y += qemu-io-cmds.o
 | 
			
		||||
@@ -101,6 +102,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
 | 
			
		||||
######################################################################
 | 
			
		||||
# tracing
 | 
			
		||||
util-obj-y +=  trace/
 | 
			
		||||
target-obj-y += trace/
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# guest agent
 | 
			
		||||
@@ -182,11 +184,9 @@ trace-events-subdirs += qapi
 | 
			
		||||
trace-events-subdirs += qom
 | 
			
		||||
trace-events-subdirs += scsi
 | 
			
		||||
trace-events-subdirs += target/arm
 | 
			
		||||
trace-events-subdirs += target/hppa
 | 
			
		||||
trace-events-subdirs += target/i386
 | 
			
		||||
trace-events-subdirs += target/mips
 | 
			
		||||
trace-events-subdirs += target/ppc
 | 
			
		||||
trace-events-subdirs += target/riscv
 | 
			
		||||
trace-events-subdirs += target/s390x
 | 
			
		||||
trace-events-subdirs += target/sparc
 | 
			
		||||
trace-events-subdirs += ui
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,8 @@ BUILD_DIR?=$(CURDIR)/..
 | 
			
		||||
 | 
			
		||||
include ../config-host.mak
 | 
			
		||||
include config-target.mak
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_SOFTMMU
 | 
			
		||||
include config-devices.mak
 | 
			
		||||
endif
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
 | 
			
		||||
ifdef CONFIG_LINUX
 | 
			
		||||
@@ -40,7 +37,9 @@ PROGS=$(QEMU_PROG) $(QEMU_PROGW)
 | 
			
		||||
STPFILES=
 | 
			
		||||
 | 
			
		||||
# Makefile Tests
 | 
			
		||||
ifdef CONFIG_USER_ONLY
 | 
			
		||||
include $(SRC_PATH)/tests/tcg/Makefile.include
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
config-target.h: config-target.h-timestamp
 | 
			
		||||
config-target.h-timestamp: config-target.mak
 | 
			
		||||
@@ -103,8 +102,6 @@ all: $(PROGS) stap
 | 
			
		||||
# Dummy command so that make thinks it has done something
 | 
			
		||||
	@true
 | 
			
		||||
 | 
			
		||||
obj-y += trace/
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
# cpu emulator library
 | 
			
		||||
obj-y += exec.o
 | 
			
		||||
@@ -173,7 +170,14 @@ endif # CONFIG_SOFTMMU
 | 
			
		||||
dummy := $(call unnest-vars,,obj-y)
 | 
			
		||||
all-obj-y := $(obj-y)
 | 
			
		||||
 | 
			
		||||
target-obj-y :=
 | 
			
		||||
block-obj-y :=
 | 
			
		||||
common-obj-y :=
 | 
			
		||||
chardev-obj-y :=
 | 
			
		||||
slirp-obj-y :=
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
dummy := $(call unnest-vars,,target-obj-y)
 | 
			
		||||
target-obj-y-save := $(target-obj-y)
 | 
			
		||||
dummy := $(call unnest-vars,.., \
 | 
			
		||||
               authz-obj-y \
 | 
			
		||||
               block-obj-y \
 | 
			
		||||
@@ -184,18 +188,20 @@ dummy := $(call unnest-vars,.., \
 | 
			
		||||
               qom-obj-y \
 | 
			
		||||
               io-obj-y \
 | 
			
		||||
               common-obj-y \
 | 
			
		||||
               common-obj-m)
 | 
			
		||||
               common-obj-m \
 | 
			
		||||
               slirp-obj-y)
 | 
			
		||||
target-obj-y := $(target-obj-y-save)
 | 
			
		||||
all-obj-y += $(common-obj-y)
 | 
			
		||||
all-obj-y += $(target-obj-y)
 | 
			
		||||
all-obj-y += $(qom-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(slirp-obj-y)
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_SOFTMMU
 | 
			
		||||
$(QEMU_PROG_BUILD): config-devices.mak
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
COMMON_LDADDS = ../libqemuutil.a
 | 
			
		||||
 | 
			
		||||
@@ -220,7 +226,6 @@ clean: clean-target
 | 
			
		||||
	rm -f *.a *~ $(PROGS)
 | 
			
		||||
	rm -f $(shell find . -name '*.[od]')
 | 
			
		||||
	rm -f hmp-commands.h gdbstub-xml.c
 | 
			
		||||
	rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
 | 
			
		||||
ifdef CONFIG_TRACE_SYSTEMTAP
 | 
			
		||||
	rm -f *.stp
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -65,8 +65,6 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
 | 
			
		||||
        ms->accelerator = NULL;
 | 
			
		||||
        *(acc->allowed) = false;
 | 
			
		||||
        object_unref(OBJECT(accel));
 | 
			
		||||
    } else {
 | 
			
		||||
        object_set_accelerator_compat_props(acc->compat_props);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -93,9 +91,7 @@ void configure_accelerator(MachineState *ms, const char *progname)
 | 
			
		||||
#elif defined(CONFIG_KVM)
 | 
			
		||||
            accel = "kvm";
 | 
			
		||||
#else
 | 
			
		||||
            error_report("No accelerator selected and"
 | 
			
		||||
                         " no default accelerator available");
 | 
			
		||||
            exit(1);
 | 
			
		||||
#error "No default accelerator available"
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1593,7 +1593,7 @@ static int kvm_init(MachineState *ms)
 | 
			
		||||
 | 
			
		||||
    kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
 | 
			
		||||
    if (mc->kvm_type) {
 | 
			
		||||
        type = mc->kvm_type(ms, kvm_type);
 | 
			
		||||
        type = mc->kvm_type(kvm_type);
 | 
			
		||||
    } else if (kvm_type) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
 | 
			
		||||
@@ -2267,6 +2267,13 @@ bool kvm_arm_supports_user_irq(void)
 | 
			
		||||
    return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Whether the KVM_SET_GUEST_DEBUG ioctl supports single stepping */
 | 
			
		||||
int kvm_has_guestdbg_singlestep(void)
 | 
			
		||||
{
 | 
			
		||||
    /* return kvm_check_extension(kvm_state, KVM_CAP_GUEST_DEBUG_SSTEP); */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
 | 
			
		||||
struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu,
 | 
			
		||||
                                                 target_ulong pc)
 | 
			
		||||
@@ -2316,6 +2323,15 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
 | 
			
		||||
    return data.err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void kvm_set_singlestep(CPUState *cs, int enabled)
 | 
			
		||||
{
 | 
			
		||||
    if (kvm_has_guestdbg_singlestep()) {
 | 
			
		||||
        kvm_update_guest_debug(cs, 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        kvm_arch_set_singlestep(cs, enabled);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
 | 
			
		||||
                          target_ulong len, int type)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
# Trace events for debugging and performance instrumentation
 | 
			
		||||
 | 
			
		||||
# kvm-all.c
 | 
			
		||||
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,10 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
 | 
			
		||||
    return -ENOSYS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void kvm_set_singlestep(CPUState *cs, int enabled)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
 | 
			
		||||
                          target_ulong len, int type)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
# Trace events for debugging and performance instrumentation
 | 
			
		||||
 | 
			
		||||
# TCG related tracing (mostly disabled by default)
 | 
			
		||||
# cpu-exec.c
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
 | 
			
		||||
common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
 | 
			
		||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
 | 
			
		||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
 | 
			
		||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,28 @@
 | 
			
		||||
#define AUDIO_CAP "alsa"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
typedef struct ALSAConf {
 | 
			
		||||
    int size_in_usec_in;
 | 
			
		||||
    int size_in_usec_out;
 | 
			
		||||
    const char *pcm_name_in;
 | 
			
		||||
    const char *pcm_name_out;
 | 
			
		||||
    unsigned int buffer_size_in;
 | 
			
		||||
    unsigned int period_size_in;
 | 
			
		||||
    unsigned int buffer_size_out;
 | 
			
		||||
    unsigned int period_size_out;
 | 
			
		||||
    unsigned int threshold;
 | 
			
		||||
 | 
			
		||||
    int buffer_size_in_overridden;
 | 
			
		||||
    int period_size_in_overridden;
 | 
			
		||||
 | 
			
		||||
    int buffer_size_out_overridden;
 | 
			
		||||
    int period_size_out_overridden;
 | 
			
		||||
} ALSAConf;
 | 
			
		||||
 | 
			
		||||
struct pollhlp {
 | 
			
		||||
    snd_pcm_t *handle;
 | 
			
		||||
    struct pollfd *pfds;
 | 
			
		||||
    ALSAConf *conf;
 | 
			
		||||
    int count;
 | 
			
		||||
    int mask;
 | 
			
		||||
};
 | 
			
		||||
@@ -47,7 +66,6 @@ typedef struct ALSAVoiceOut {
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    snd_pcm_t *handle;
 | 
			
		||||
    struct pollhlp pollhlp;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
} ALSAVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct ALSAVoiceIn {
 | 
			
		||||
@@ -55,18 +73,21 @@ typedef struct ALSAVoiceIn {
 | 
			
		||||
    snd_pcm_t *handle;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    struct pollhlp pollhlp;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
} ALSAVoiceIn;
 | 
			
		||||
 | 
			
		||||
struct alsa_params_req {
 | 
			
		||||
    int freq;
 | 
			
		||||
    snd_pcm_format_t fmt;
 | 
			
		||||
    int nchannels;
 | 
			
		||||
    int size_in_usec;
 | 
			
		||||
    int override_mask;
 | 
			
		||||
    unsigned int buffer_size;
 | 
			
		||||
    unsigned int period_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct alsa_params_obt {
 | 
			
		||||
    int freq;
 | 
			
		||||
    AudioFormat fmt;
 | 
			
		||||
    audfmt_e fmt;
 | 
			
		||||
    int endianness;
 | 
			
		||||
    int nchannels;
 | 
			
		||||
    snd_pcm_uframes_t samples;
 | 
			
		||||
@@ -273,16 +294,16 @@ 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)
 | 
			
		||||
static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
 | 
			
		||||
{
 | 
			
		||||
    switch (fmt) {
 | 
			
		||||
    case AUDIO_FORMAT_S8:
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
        return SND_PCM_FORMAT_S8;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        return SND_PCM_FORMAT_U8;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S16:
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
        if (endianness) {
 | 
			
		||||
            return SND_PCM_FORMAT_S16_BE;
 | 
			
		||||
        }
 | 
			
		||||
@@ -290,7 +311,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 | 
			
		||||
            return SND_PCM_FORMAT_S16_LE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        if (endianness) {
 | 
			
		||||
            return SND_PCM_FORMAT_U16_BE;
 | 
			
		||||
        }
 | 
			
		||||
@@ -298,7 +319,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 | 
			
		||||
            return SND_PCM_FORMAT_U16_LE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S32:
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
        if (endianness) {
 | 
			
		||||
            return SND_PCM_FORMAT_S32_BE;
 | 
			
		||||
        }
 | 
			
		||||
@@ -306,7 +327,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 | 
			
		||||
            return SND_PCM_FORMAT_S32_LE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        if (endianness) {
 | 
			
		||||
            return SND_PCM_FORMAT_U32_BE;
 | 
			
		||||
        }
 | 
			
		||||
@@ -323,58 +344,58 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
 | 
			
		||||
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
 | 
			
		||||
                           int *endianness)
 | 
			
		||||
{
 | 
			
		||||
    switch (alsafmt) {
 | 
			
		||||
    case SND_PCM_FORMAT_S8:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S8;
 | 
			
		||||
        *fmt = AUD_FMT_S8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_U8:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U8;
 | 
			
		||||
        *fmt = AUD_FMT_U8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_S16_LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        *fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_U16_LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U16;
 | 
			
		||||
        *fmt = AUD_FMT_U16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_S16_BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        *fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_U16_BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U16;
 | 
			
		||||
        *fmt = AUD_FMT_U16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_S32_LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S32;
 | 
			
		||||
        *fmt = AUD_FMT_S32;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_U32_LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U32;
 | 
			
		||||
        *fmt = AUD_FMT_U32;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_S32_BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S32;
 | 
			
		||||
        *fmt = AUD_FMT_S32;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case SND_PCM_FORMAT_U32_BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U32;
 | 
			
		||||
        *fmt = AUD_FMT_U32;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
@@ -387,8 +408,7 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
 | 
			
		||||
 | 
			
		||||
static void alsa_dump_info (struct alsa_params_req *req,
 | 
			
		||||
                            struct alsa_params_obt *obt,
 | 
			
		||||
                            snd_pcm_format_t obtfmt,
 | 
			
		||||
                            AudiodevAlsaPerDirectionOptions *apdo)
 | 
			
		||||
                            snd_pcm_format_t obtfmt)
 | 
			
		||||
{
 | 
			
		||||
    dolog ("parameter | requested value | obtained value\n");
 | 
			
		||||
    dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt);
 | 
			
		||||
@@ -396,8 +416,8 @@ static void alsa_dump_info (struct alsa_params_req *req,
 | 
			
		||||
           req->nchannels, obt->nchannels);
 | 
			
		||||
    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
 | 
			
		||||
    dolog ("============================================\n");
 | 
			
		||||
    dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
 | 
			
		||||
          apdo->buffer_length, apdo->period_length);
 | 
			
		||||
    dolog ("requested: buffer size %d period size %d\n",
 | 
			
		||||
           req->buffer_size, req->period_size);
 | 
			
		||||
    dolog ("obtained: samples %ld\n", obt->samples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -431,23 +451,23 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int alsa_open(bool in, struct alsa_params_req *req,
 | 
			
		||||
static int alsa_open (int in, struct alsa_params_req *req,
 | 
			
		||||
                      struct alsa_params_obt *obt, snd_pcm_t **handlep,
 | 
			
		||||
                     Audiodev *dev)
 | 
			
		||||
                      ALSAConf *conf)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevAlsaOptions *aopts = &dev->u.alsa;
 | 
			
		||||
    AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
 | 
			
		||||
    snd_pcm_t *handle;
 | 
			
		||||
    snd_pcm_hw_params_t *hw_params;
 | 
			
		||||
    int err;
 | 
			
		||||
    int size_in_usec;
 | 
			
		||||
    unsigned int freq, nchannels;
 | 
			
		||||
    const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
 | 
			
		||||
    const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
 | 
			
		||||
    snd_pcm_uframes_t obt_buffer_size;
 | 
			
		||||
    const char *typ = in ? "ADC" : "DAC";
 | 
			
		||||
    snd_pcm_format_t obtfmt;
 | 
			
		||||
 | 
			
		||||
    freq = req->freq;
 | 
			
		||||
    nchannels = req->nchannels;
 | 
			
		||||
    size_in_usec = req->size_in_usec;
 | 
			
		||||
 | 
			
		||||
    snd_pcm_hw_params_alloca (&hw_params);
 | 
			
		||||
 | 
			
		||||
@@ -507,42 +527,79 @@ static int alsa_open(bool in, struct alsa_params_req *req,
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (apdo->buffer_length) {
 | 
			
		||||
    if (req->buffer_size) {
 | 
			
		||||
        unsigned long obt;
 | 
			
		||||
 | 
			
		||||
        if (size_in_usec) {
 | 
			
		||||
            int dir = 0;
 | 
			
		||||
        unsigned int btime = apdo->buffer_length;
 | 
			
		||||
            unsigned int btime = req->buffer_size;
 | 
			
		||||
 | 
			
		||||
            err = snd_pcm_hw_params_set_buffer_time_near (
 | 
			
		||||
            handle, hw_params, &btime, &dir);
 | 
			
		||||
                handle,
 | 
			
		||||
                hw_params,
 | 
			
		||||
                &btime,
 | 
			
		||||
                &dir
 | 
			
		||||
                );
 | 
			
		||||
            obt = btime;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            snd_pcm_uframes_t bsize = req->buffer_size;
 | 
			
		||||
 | 
			
		||||
            err = snd_pcm_hw_params_set_buffer_size_near (
 | 
			
		||||
                handle,
 | 
			
		||||
                hw_params,
 | 
			
		||||
                &bsize
 | 
			
		||||
                );
 | 
			
		||||
            obt = bsize;
 | 
			
		||||
        }
 | 
			
		||||
        if (err < 0) {
 | 
			
		||||
            alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
 | 
			
		||||
                         apdo->buffer_length);
 | 
			
		||||
            alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
 | 
			
		||||
                          size_in_usec ? "time" : "size", req->buffer_size);
 | 
			
		||||
            goto err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (apdo->has_buffer_length && btime != apdo->buffer_length) {
 | 
			
		||||
            dolog("Requested buffer time %" PRId32
 | 
			
		||||
                  " was rejected, using %u\n", apdo->buffer_length, btime);
 | 
			
		||||
        }
 | 
			
		||||
        if ((req->override_mask & 2) && (obt - req->buffer_size))
 | 
			
		||||
            dolog ("Requested buffer %s %u was rejected, using %lu\n",
 | 
			
		||||
                   size_in_usec ? "time" : "size", req->buffer_size, obt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (apdo->period_length) {
 | 
			
		||||
    if (req->period_size) {
 | 
			
		||||
        unsigned long obt;
 | 
			
		||||
 | 
			
		||||
        if (size_in_usec) {
 | 
			
		||||
            int dir = 0;
 | 
			
		||||
        unsigned int ptime = apdo->period_length;
 | 
			
		||||
            unsigned int ptime = req->period_size;
 | 
			
		||||
 | 
			
		||||
        err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
 | 
			
		||||
                                                     &dir);
 | 
			
		||||
            err = snd_pcm_hw_params_set_period_time_near (
 | 
			
		||||
                handle,
 | 
			
		||||
                hw_params,
 | 
			
		||||
                &ptime,
 | 
			
		||||
                &dir
 | 
			
		||||
                );
 | 
			
		||||
            obt = ptime;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            int dir = 0;
 | 
			
		||||
            snd_pcm_uframes_t psize = req->period_size;
 | 
			
		||||
 | 
			
		||||
            err = snd_pcm_hw_params_set_period_size_near (
 | 
			
		||||
                handle,
 | 
			
		||||
                hw_params,
 | 
			
		||||
                &psize,
 | 
			
		||||
                &dir
 | 
			
		||||
                );
 | 
			
		||||
            obt = psize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (err < 0) {
 | 
			
		||||
            alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
 | 
			
		||||
                         apdo->period_length);
 | 
			
		||||
            alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
 | 
			
		||||
                          size_in_usec ? "time" : "size", req->period_size);
 | 
			
		||||
            goto err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (apdo->has_period_length && ptime != apdo->period_length) {
 | 
			
		||||
            dolog("Requested period time %" PRId32 " was rejected, using %d\n",
 | 
			
		||||
                  apdo->period_length, ptime);
 | 
			
		||||
        }
 | 
			
		||||
        if (((req->override_mask & 1) && (obt - req->period_size)))
 | 
			
		||||
            dolog ("Requested period %s %u was rejected, using %lu\n",
 | 
			
		||||
                   size_in_usec ? "time" : "size", req->period_size, obt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = snd_pcm_hw_params (handle, hw_params);
 | 
			
		||||
@@ -574,12 +631,30 @@ static int alsa_open(bool in, struct alsa_params_req *req,
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!in && aopts->has_threshold && aopts->threshold) {
 | 
			
		||||
        struct audsettings as = { .freq = freq };
 | 
			
		||||
        alsa_set_threshold(
 | 
			
		||||
            handle,
 | 
			
		||||
            audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
 | 
			
		||||
                                &as, aopts->threshold));
 | 
			
		||||
    if (!in && conf->threshold) {
 | 
			
		||||
        snd_pcm_uframes_t threshold;
 | 
			
		||||
        int bytes_per_sec;
 | 
			
		||||
 | 
			
		||||
        bytes_per_sec = freq << (nchannels == 2);
 | 
			
		||||
 | 
			
		||||
        switch (obt->fmt) {
 | 
			
		||||
        case AUD_FMT_S8:
 | 
			
		||||
        case AUD_FMT_U8:
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case AUD_FMT_S16:
 | 
			
		||||
        case AUD_FMT_U16:
 | 
			
		||||
            bytes_per_sec <<= 1;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case AUD_FMT_S32:
 | 
			
		||||
        case AUD_FMT_U32:
 | 
			
		||||
            bytes_per_sec <<= 2;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        threshold = (conf->threshold * bytes_per_sec) / 1000;
 | 
			
		||||
        alsa_set_threshold (handle, threshold);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    obt->nchannels = nchannels;
 | 
			
		||||
@@ -592,11 +667,11 @@ static int alsa_open(bool in, struct alsa_params_req *req,
 | 
			
		||||
         obt->nchannels != req->nchannels ||
 | 
			
		||||
         obt->freq != req->freq) {
 | 
			
		||||
        dolog ("Audio parameters for %s\n", typ);
 | 
			
		||||
        alsa_dump_info(req, obt, obtfmt, apdo);
 | 
			
		||||
        alsa_dump_info (req, obt, obtfmt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
    alsa_dump_info(req, obt, obtfmt, pdo);
 | 
			
		||||
    alsa_dump_info (req, obt, obtfmt);
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
@@ -722,13 +797,19 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    struct alsa_params_obt obt;
 | 
			
		||||
    snd_pcm_t *handle;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    Audiodev *dev = drv_opaque;
 | 
			
		||||
    ALSAConf *conf = drv_opaque;
 | 
			
		||||
 | 
			
		||||
    req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
 | 
			
		||||
    req.freq = as->freq;
 | 
			
		||||
    req.nchannels = as->nchannels;
 | 
			
		||||
    req.period_size = conf->period_size_out;
 | 
			
		||||
    req.buffer_size = conf->buffer_size_out;
 | 
			
		||||
    req.size_in_usec = conf->size_in_usec_out;
 | 
			
		||||
    req.override_mask =
 | 
			
		||||
        (conf->period_size_out_overridden ? 1 : 0) |
 | 
			
		||||
        (conf->buffer_size_out_overridden ? 2 : 0);
 | 
			
		||||
 | 
			
		||||
    if (alsa_open(0, &req, &obt, &handle, dev)) {
 | 
			
		||||
    if (alsa_open (0, &req, &obt, &handle, conf)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -749,7 +830,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alsa->handle = handle;
 | 
			
		||||
    alsa->dev = dev;
 | 
			
		||||
    alsa->pollhlp.conf = conf;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -789,12 +870,16 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
 | 
			
		||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
 | 
			
		||||
    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
 | 
			
		||||
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
    case VOICE_ENABLE:
 | 
			
		||||
        {
 | 
			
		||||
            bool poll_mode = apdo->try_poll;
 | 
			
		||||
            va_list ap;
 | 
			
		||||
            int poll_mode;
 | 
			
		||||
 | 
			
		||||
            va_start (ap, cmd);
 | 
			
		||||
            poll_mode = va_arg (ap, int);
 | 
			
		||||
            va_end (ap);
 | 
			
		||||
 | 
			
		||||
            ldebug ("enabling voice\n");
 | 
			
		||||
            if (poll_mode && alsa_poll_out (hw)) {
 | 
			
		||||
@@ -823,13 +908,19 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
    struct alsa_params_obt obt;
 | 
			
		||||
    snd_pcm_t *handle;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    Audiodev *dev = drv_opaque;
 | 
			
		||||
    ALSAConf *conf = drv_opaque;
 | 
			
		||||
 | 
			
		||||
    req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
 | 
			
		||||
    req.freq = as->freq;
 | 
			
		||||
    req.nchannels = as->nchannels;
 | 
			
		||||
    req.period_size = conf->period_size_in;
 | 
			
		||||
    req.buffer_size = conf->buffer_size_in;
 | 
			
		||||
    req.size_in_usec = conf->size_in_usec_in;
 | 
			
		||||
    req.override_mask =
 | 
			
		||||
        (conf->period_size_in_overridden ? 1 : 0) |
 | 
			
		||||
        (conf->buffer_size_in_overridden ? 2 : 0);
 | 
			
		||||
 | 
			
		||||
    if (alsa_open(1, &req, &obt, &handle, dev)) {
 | 
			
		||||
    if (alsa_open (1, &req, &obt, &handle, conf)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -850,7 +941,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alsa->handle = handle;
 | 
			
		||||
    alsa->dev = dev;
 | 
			
		||||
    alsa->pollhlp.conf = conf;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -992,12 +1083,16 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
 | 
			
		||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
 | 
			
		||||
    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
 | 
			
		||||
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
    case VOICE_ENABLE:
 | 
			
		||||
        {
 | 
			
		||||
            bool poll_mode = apdo->try_poll;
 | 
			
		||||
            va_list ap;
 | 
			
		||||
            int poll_mode;
 | 
			
		||||
 | 
			
		||||
            va_start (ap, cmd);
 | 
			
		||||
            poll_mode = va_arg (ap, int);
 | 
			
		||||
            va_end (ap);
 | 
			
		||||
 | 
			
		||||
            ldebug ("enabling voice\n");
 | 
			
		||||
            if (poll_mode && alsa_poll_in (hw)) {
 | 
			
		||||
@@ -1020,54 +1115,88 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
 | 
			
		||||
static ALSAConf glob_conf = {
 | 
			
		||||
    .buffer_size_out = 4096,
 | 
			
		||||
    .period_size_out = 1024,
 | 
			
		||||
    .pcm_name_out = "default",
 | 
			
		||||
    .pcm_name_in = "default",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *alsa_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    if (!apdo->has_try_poll) {
 | 
			
		||||
        apdo->try_poll = true;
 | 
			
		||||
        apdo->has_try_poll = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *alsa_audio_init(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevAlsaOptions *aopts;
 | 
			
		||||
    assert(dev->driver == AUDIODEV_DRIVER_ALSA);
 | 
			
		||||
 | 
			
		||||
    aopts = &dev->u.alsa;
 | 
			
		||||
    alsa_init_per_direction(aopts->in);
 | 
			
		||||
    alsa_init_per_direction(aopts->out);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * need to define them, as otherwise alsa produces no sound
 | 
			
		||||
     * doesn't set has_* so alsa_open can identify it wasn't set by the user
 | 
			
		||||
     */
 | 
			
		||||
    if (!dev->u.alsa.out->has_period_length) {
 | 
			
		||||
        /* 1024 frames assuming 44100Hz */
 | 
			
		||||
        dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
 | 
			
		||||
    }
 | 
			
		||||
    if (!dev->u.alsa.out->has_buffer_length) {
 | 
			
		||||
        /* 4096 frames assuming 44100Hz */
 | 
			
		||||
        dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * OptsVisitor sets unspecified optional fields to zero, but do not depend
 | 
			
		||||
     * on it...
 | 
			
		||||
     */
 | 
			
		||||
    if (!dev->u.alsa.in->has_period_length) {
 | 
			
		||||
        dev->u.alsa.in->period_length = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (!dev->u.alsa.in->has_buffer_length) {
 | 
			
		||||
        dev->u.alsa.in->buffer_length = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return dev;
 | 
			
		||||
    ALSAConf *conf = g_malloc(sizeof(ALSAConf));
 | 
			
		||||
    *conf = glob_conf;
 | 
			
		||||
    return conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void alsa_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    g_free(opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option alsa_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "DAC_SIZE_IN_USEC",
 | 
			
		||||
        .tag         = AUD_OPT_BOOL,
 | 
			
		||||
        .valp        = &glob_conf.size_in_usec_out,
 | 
			
		||||
        .descr       = "DAC period/buffer size in microseconds (otherwise in frames)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "DAC_PERIOD_SIZE",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &glob_conf.period_size_out,
 | 
			
		||||
        .descr       = "DAC period size (0 to go with system default)",
 | 
			
		||||
        .overriddenp = &glob_conf.period_size_out_overridden
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "DAC_BUFFER_SIZE",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &glob_conf.buffer_size_out,
 | 
			
		||||
        .descr       = "DAC buffer size (0 to go with system default)",
 | 
			
		||||
        .overriddenp = &glob_conf.buffer_size_out_overridden
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "ADC_SIZE_IN_USEC",
 | 
			
		||||
        .tag         = AUD_OPT_BOOL,
 | 
			
		||||
        .valp        = &glob_conf.size_in_usec_in,
 | 
			
		||||
        .descr       =
 | 
			
		||||
        "ADC period/buffer size in microseconds (otherwise in frames)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "ADC_PERIOD_SIZE",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &glob_conf.period_size_in,
 | 
			
		||||
        .descr       = "ADC period size (0 to go with system default)",
 | 
			
		||||
        .overriddenp = &glob_conf.period_size_in_overridden
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "ADC_BUFFER_SIZE",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &glob_conf.buffer_size_in,
 | 
			
		||||
        .descr       = "ADC buffer size (0 to go with system default)",
 | 
			
		||||
        .overriddenp = &glob_conf.buffer_size_in_overridden
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "THRESHOLD",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &glob_conf.threshold,
 | 
			
		||||
        .descr       = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "DAC_DEV",
 | 
			
		||||
        .tag         = AUD_OPT_STR,
 | 
			
		||||
        .valp        = &glob_conf.pcm_name_out,
 | 
			
		||||
        .descr       = "DAC device name (for instance dmix)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "ADC_DEV",
 | 
			
		||||
        .tag         = AUD_OPT_STR,
 | 
			
		||||
        .valp        = &glob_conf.pcm_name_in,
 | 
			
		||||
        .descr       = "ADC device name"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops alsa_pcm_ops = {
 | 
			
		||||
    .init_out = alsa_init_out,
 | 
			
		||||
    .fini_out = alsa_fini_out,
 | 
			
		||||
@@ -1085,6 +1214,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
 | 
			
		||||
static struct audio_driver alsa_audio_driver = {
 | 
			
		||||
    .name           = "alsa",
 | 
			
		||||
    .descr          = "ALSA http://www.alsa-project.org",
 | 
			
		||||
    .options        = alsa_options,
 | 
			
		||||
    .init           = alsa_audio_init,
 | 
			
		||||
    .fini           = alsa_audio_fini,
 | 
			
		||||
    .pcm_ops        = &alsa_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										920
									
								
								audio/audio.c
									
									
									
									
									
								
							
							
						
						
									
										920
									
								
								audio/audio.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -26,31 +26,30 @@
 | 
			
		||||
#define QEMU_AUDIO_H
 | 
			
		||||
 | 
			
		||||
#include "qemu/queue.h"
 | 
			
		||||
#include "qapi/qapi-types-audio.h"
 | 
			
		||||
 | 
			
		||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    AUD_FMT_U8,
 | 
			
		||||
    AUD_FMT_S8,
 | 
			
		||||
    AUD_FMT_U16,
 | 
			
		||||
    AUD_FMT_S16,
 | 
			
		||||
    AUD_FMT_U32,
 | 
			
		||||
    AUD_FMT_S32
 | 
			
		||||
} audfmt_e;
 | 
			
		||||
 | 
			
		||||
#ifdef HOST_WORDS_BIGENDIAN
 | 
			
		||||
#define AUDIO_HOST_ENDIANNESS 1
 | 
			
		||||
#else
 | 
			
		||||
#define AUDIO_HOST_ENDIANNESS 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct audsettings {
 | 
			
		||||
struct audsettings {
 | 
			
		||||
    int freq;
 | 
			
		||||
    int nchannels;
 | 
			
		||||
    AudioFormat fmt;
 | 
			
		||||
    audfmt_e fmt;
 | 
			
		||||
    int endianness;
 | 
			
		||||
} audsettings;
 | 
			
		||||
 | 
			
		||||
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
 | 
			
		||||
int audioformat_bytes_per_sample(AudioFormat fmt);
 | 
			
		||||
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
 | 
			
		||||
                        audsettings *as, int def_usecs);
 | 
			
		||||
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
 | 
			
		||||
                         audsettings *as, int def_usecs);
 | 
			
		||||
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
 | 
			
		||||
                       audsettings *as, int def_usecs);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    AUD_CNOTIFY_ENABLE,
 | 
			
		||||
@@ -90,6 +89,7 @@ typedef struct QEMUAudioTimeStamp {
 | 
			
		||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
 | 
			
		||||
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
 | 
			
		||||
 | 
			
		||||
void AUD_help (void);
 | 
			
		||||
void AUD_register_card (const char *name, QEMUSoundCard *card);
 | 
			
		||||
void AUD_remove_card (QEMUSoundCard *card);
 | 
			
		||||
CaptureVoiceOut *AUD_add_capture (
 | 
			
		||||
@@ -171,8 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos,
 | 
			
		||||
void audio_sample_from_uint64(void *samples, int pos,
 | 
			
		||||
                            uint64_t left, uint64_t right);
 | 
			
		||||
 | 
			
		||||
void audio_parse_option(const char *opt);
 | 
			
		||||
void audio_init_audiodevs(void);
 | 
			
		||||
void audio_legacy_help(void);
 | 
			
		||||
 | 
			
		||||
#endif /* QEMU_AUDIO_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,22 @@
 | 
			
		||||
 | 
			
		||||
struct audio_pcm_ops;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    AUD_OPT_INT,
 | 
			
		||||
    AUD_OPT_FMT,
 | 
			
		||||
    AUD_OPT_STR,
 | 
			
		||||
    AUD_OPT_BOOL
 | 
			
		||||
} audio_option_tag_e;
 | 
			
		||||
 | 
			
		||||
struct audio_option {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    audio_option_tag_e tag;
 | 
			
		||||
    void *valp;
 | 
			
		||||
    const char *descr;
 | 
			
		||||
    int *overriddenp;
 | 
			
		||||
    int overridden;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_callback {
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    audio_callback_fn fn;
 | 
			
		||||
@@ -129,7 +145,8 @@ typedef struct audio_driver audio_driver;
 | 
			
		||||
struct audio_driver {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    const char *descr;
 | 
			
		||||
    void *(*init) (Audiodev *);
 | 
			
		||||
    struct audio_option *options;
 | 
			
		||||
    void *(*init) (void);
 | 
			
		||||
    void (*fini) (void *);
 | 
			
		||||
    struct audio_pcm_ops *pcm_ops;
 | 
			
		||||
    int can_be_default;
 | 
			
		||||
@@ -176,7 +193,6 @@ struct SWVoiceCap {
 | 
			
		||||
 | 
			
		||||
typedef struct AudioState {
 | 
			
		||||
    struct audio_driver *drv;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
    void *drv_opaque;
 | 
			
		||||
 | 
			
		||||
    QEMUTimer *ts;
 | 
			
		||||
@@ -187,13 +203,10 @@ typedef struct AudioState {
 | 
			
		||||
    int nb_hw_voices_out;
 | 
			
		||||
    int nb_hw_voices_in;
 | 
			
		||||
    int vm_running;
 | 
			
		||||
    int64_t period_ticks;
 | 
			
		||||
} AudioState;
 | 
			
		||||
 | 
			
		||||
extern const struct mixeng_volume nominal_volume;
 | 
			
		||||
 | 
			
		||||
extern const char *audio_prio_list[];
 | 
			
		||||
 | 
			
		||||
void audio_driver_register(audio_driver *drv);
 | 
			
		||||
audio_driver *audio_driver_lookup(const char *name);
 | 
			
		||||
 | 
			
		||||
@@ -235,18 +248,4 @@ static inline int audio_ring_dist (int dst, int src, int len)
 | 
			
		||||
#define AUDIO_STRINGIFY_(n) #n
 | 
			
		||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
 | 
			
		||||
 | 
			
		||||
typedef struct AudiodevListEntry {
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
    QSIMPLEQ_ENTRY(AudiodevListEntry) next;
 | 
			
		||||
} AudiodevListEntry;
 | 
			
		||||
 | 
			
		||||
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
 | 
			
		||||
AudiodevListHead audio_handle_legacy_opts(void);
 | 
			
		||||
 | 
			
		||||
void audio_free_audiodev_list(AudiodevListHead *head);
 | 
			
		||||
 | 
			
		||||
void audio_create_pdos(Audiodev *dev);
 | 
			
		||||
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
 | 
			
		||||
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
 | 
			
		||||
 | 
			
		||||
#endif /* QEMU_AUDIO_INT_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,550 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU Audio subsystem: legacy configuration handling
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "qemu/timer.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qapi/qapi-visit-audio.h"
 | 
			
		||||
#include "qapi/visitor-impl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "audio-legacy"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
static uint32_t toui32(const char *str)
 | 
			
		||||
{
 | 
			
		||||
    unsigned long long ret;
 | 
			
		||||
    if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
 | 
			
		||||
        dolog("Invalid integer value `%s'\n", str);
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* helper functions to convert env variables */
 | 
			
		||||
static void get_bool(const char *env, bool *dst, bool *has_dst)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        *dst = toui32(val) != 0;
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_int(const char *env, uint32_t *dst, bool *has_dst)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        *dst = toui32(val);
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_str(const char *env, char **dst, bool *has_dst)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        if (*has_dst) {
 | 
			
		||||
            g_free(*dst);
 | 
			
		||||
        }
 | 
			
		||||
        *dst = g_strdup(val);
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        size_t i;
 | 
			
		||||
        for (i = 0; AudioFormat_lookup.size; ++i) {
 | 
			
		||||
            if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
 | 
			
		||||
                *dst = i;
 | 
			
		||||
                *has_dst = true;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        dolog("Invalid audio format `%s'\n", val);
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        *dst = toui32(val) * 1000;
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t frames_to_usecs(uint32_t frames,
 | 
			
		||||
                                AudiodevPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
 | 
			
		||||
    return (frames * 1000000 + freq / 2) / freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
 | 
			
		||||
                                AudiodevPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        *dst = frames_to_usecs(toui32(val), pdo);
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t samples_to_usecs(uint32_t samples,
 | 
			
		||||
                                 AudiodevPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t channels = pdo->has_channels ? pdo->channels : 2;
 | 
			
		||||
    return frames_to_usecs(samples / channels, pdo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
 | 
			
		||||
                                 AudiodevPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        *dst = samples_to_usecs(toui32(val), pdo);
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
 | 
			
		||||
    uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt);
 | 
			
		||||
    return samples_to_usecs(bytes / bytes_per_sample, pdo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
 | 
			
		||||
                               AudiodevPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    const char *val = getenv(env);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        *dst = bytes_to_usecs(toui32(val), pdo);
 | 
			
		||||
        *has_dst = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* backend specific functions */
 | 
			
		||||
/* ALSA */
 | 
			
		||||
static void handle_alsa_per_direction(
 | 
			
		||||
    AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
 | 
			
		||||
{
 | 
			
		||||
    char buf[64];
 | 
			
		||||
    size_t len = strlen(prefix);
 | 
			
		||||
    bool size_in_usecs = false;
 | 
			
		||||
    bool dummy;
 | 
			
		||||
 | 
			
		||||
    memcpy(buf, prefix, len);
 | 
			
		||||
    strcpy(buf + len, "TRY_POLL");
 | 
			
		||||
    get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "DEV");
 | 
			
		||||
    get_str(buf, &apdo->dev, &apdo->has_dev);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "SIZE_IN_USEC");
 | 
			
		||||
    get_bool(buf, &size_in_usecs, &dummy);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "PERIOD_SIZE");
 | 
			
		||||
    get_int(buf, &apdo->period_length, &apdo->has_period_length);
 | 
			
		||||
    if (apdo->has_period_length && !size_in_usecs) {
 | 
			
		||||
        apdo->period_length = frames_to_usecs(
 | 
			
		||||
            apdo->period_length,
 | 
			
		||||
            qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "BUFFER_SIZE");
 | 
			
		||||
    get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length);
 | 
			
		||||
    if (apdo->has_buffer_length && !size_in_usecs) {
 | 
			
		||||
        apdo->buffer_length = frames_to_usecs(
 | 
			
		||||
            apdo->buffer_length,
 | 
			
		||||
            qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_alsa(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevAlsaOptions *aopt = &dev->u.alsa;
 | 
			
		||||
    handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_");
 | 
			
		||||
    handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_");
 | 
			
		||||
 | 
			
		||||
    get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
 | 
			
		||||
                        &aopt->threshold, &aopt->has_threshold);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* coreaudio */
 | 
			
		||||
static void handle_coreaudio(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    get_frames_to_usecs(
 | 
			
		||||
        "QEMU_COREAUDIO_BUFFER_SIZE",
 | 
			
		||||
        &dev->u.coreaudio.out->buffer_length,
 | 
			
		||||
        &dev->u.coreaudio.out->has_buffer_length,
 | 
			
		||||
        qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out));
 | 
			
		||||
    get_int("QEMU_COREAUDIO_BUFFER_COUNT",
 | 
			
		||||
            &dev->u.coreaudio.out->buffer_count,
 | 
			
		||||
            &dev->u.coreaudio.out->has_buffer_count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* dsound */
 | 
			
		||||
static void handle_dsound(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS",
 | 
			
		||||
                        &dev->u.dsound.latency, &dev->u.dsound.has_latency);
 | 
			
		||||
    get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT",
 | 
			
		||||
                       &dev->u.dsound.out->buffer_length,
 | 
			
		||||
                       &dev->u.dsound.out->has_buffer_length,
 | 
			
		||||
                       dev->u.dsound.out);
 | 
			
		||||
    get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN",
 | 
			
		||||
                       &dev->u.dsound.in->buffer_length,
 | 
			
		||||
                       &dev->u.dsound.in->has_buffer_length,
 | 
			
		||||
                       dev->u.dsound.in);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* OSS */
 | 
			
		||||
static void handle_oss_per_direction(
 | 
			
		||||
    AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
 | 
			
		||||
    const char *dev_env)
 | 
			
		||||
{
 | 
			
		||||
    get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
 | 
			
		||||
    get_str(dev_env, &opdo->dev, &opdo->has_dev);
 | 
			
		||||
 | 
			
		||||
    get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
 | 
			
		||||
                       &opdo->buffer_length, &opdo->has_buffer_length,
 | 
			
		||||
                       qapi_AudiodevOssPerDirectionOptions_base(opdo));
 | 
			
		||||
    get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count,
 | 
			
		||||
            &opdo->has_buffer_count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_oss(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevOssOptions *oopt = &dev->u.oss;
 | 
			
		||||
    handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL",
 | 
			
		||||
                             "QEMU_OSS_ADC_DEV");
 | 
			
		||||
    handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL",
 | 
			
		||||
                             "QEMU_OSS_DAC_DEV");
 | 
			
		||||
 | 
			
		||||
    get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap);
 | 
			
		||||
    get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
 | 
			
		||||
    get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* pulseaudio */
 | 
			
		||||
static void handle_pa_per_direction(
 | 
			
		||||
    AudiodevPaPerDirectionOptions *ppdo, const char *env)
 | 
			
		||||
{
 | 
			
		||||
    get_str(env, &ppdo->name, &ppdo->has_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_pa(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE");
 | 
			
		||||
    handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK");
 | 
			
		||||
 | 
			
		||||
    get_samples_to_usecs(
 | 
			
		||||
        "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length,
 | 
			
		||||
        &dev->u.pa.in->has_buffer_length,
 | 
			
		||||
        qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in));
 | 
			
		||||
    get_samples_to_usecs(
 | 
			
		||||
        "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length,
 | 
			
		||||
        &dev->u.pa.out->has_buffer_length,
 | 
			
		||||
        qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
 | 
			
		||||
 | 
			
		||||
    get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* SDL */
 | 
			
		||||
static void handle_sdl(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    /* SDL is output only */
 | 
			
		||||
    get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length,
 | 
			
		||||
                         &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* wav */
 | 
			
		||||
static void handle_wav(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    get_int("QEMU_WAV_FREQUENCY",
 | 
			
		||||
            &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency);
 | 
			
		||||
    get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format,
 | 
			
		||||
            &dev->u.wav.out->has_format);
 | 
			
		||||
    get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
 | 
			
		||||
            &dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
 | 
			
		||||
    get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* general */
 | 
			
		||||
static void handle_per_direction(
 | 
			
		||||
    AudiodevPerDirectionOptions *pdo, const char *prefix)
 | 
			
		||||
{
 | 
			
		||||
    char buf[64];
 | 
			
		||||
    size_t len = strlen(prefix);
 | 
			
		||||
 | 
			
		||||
    memcpy(buf, prefix, len);
 | 
			
		||||
    strcpy(buf + len, "FIXED_SETTINGS");
 | 
			
		||||
    get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "FIXED_FREQ");
 | 
			
		||||
    get_int(buf, &pdo->frequency, &pdo->has_frequency);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "FIXED_FMT");
 | 
			
		||||
    get_fmt(buf, &pdo->format, &pdo->has_format);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "FIXED_CHANNELS");
 | 
			
		||||
    get_int(buf, &pdo->channels, &pdo->has_channels);
 | 
			
		||||
 | 
			
		||||
    strcpy(buf + len, "VOICES");
 | 
			
		||||
    get_int(buf, &pdo->voices, &pdo->has_voices);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AudiodevListEntry *legacy_opt(const char *drvname)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
 | 
			
		||||
    e->dev = g_malloc0(sizeof(Audiodev));
 | 
			
		||||
    e->dev->id = g_strdup(drvname);
 | 
			
		||||
    e->dev->driver = qapi_enum_parse(
 | 
			
		||||
        &AudiodevDriver_lookup, drvname, -1, &error_abort);
 | 
			
		||||
 | 
			
		||||
    audio_create_pdos(e->dev);
 | 
			
		||||
 | 
			
		||||
    handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
 | 
			
		||||
    handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
 | 
			
		||||
 | 
			
		||||
    /* Original description: Timer period in HZ (0 - use lowest possible) */
 | 
			
		||||
    get_int("QEMU_AUDIO_TIMER_PERIOD",
 | 
			
		||||
            &e->dev->timer_period, &e->dev->has_timer_period);
 | 
			
		||||
    if (e->dev->has_timer_period && e->dev->timer_period) {
 | 
			
		||||
        e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 /
 | 
			
		||||
                               e->dev->timer_period;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (e->dev->driver) {
 | 
			
		||||
    case AUDIODEV_DRIVER_ALSA:
 | 
			
		||||
        handle_alsa(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER_COREAUDIO:
 | 
			
		||||
        handle_coreaudio(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER_DSOUND:
 | 
			
		||||
        handle_dsound(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER_OSS:
 | 
			
		||||
        handle_oss(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER_PA:
 | 
			
		||||
        handle_pa(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER_SDL:
 | 
			
		||||
        handle_sdl(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER_WAV:
 | 
			
		||||
        handle_wav(e->dev);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return e;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudiodevListHead audio_handle_legacy_opts(void)
 | 
			
		||||
{
 | 
			
		||||
    const char *drvname = getenv("QEMU_AUDIO_DRV");
 | 
			
		||||
    AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
 | 
			
		||||
 | 
			
		||||
    if (drvname) {
 | 
			
		||||
        AudiodevListEntry *e;
 | 
			
		||||
        audio_driver *driver = audio_driver_lookup(drvname);
 | 
			
		||||
        if (!driver) {
 | 
			
		||||
            dolog("Unknown audio driver `%s'\n", drvname);
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
        e = legacy_opt(drvname);
 | 
			
		||||
        QSIMPLEQ_INSERT_TAIL(&head, e, next);
 | 
			
		||||
    } else {
 | 
			
		||||
        for (int i = 0; audio_prio_list[i]; i++) {
 | 
			
		||||
            audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
 | 
			
		||||
            if (driver && driver->can_be_default) {
 | 
			
		||||
                AudiodevListEntry *e = legacy_opt(driver->name);
 | 
			
		||||
                QSIMPLEQ_INSERT_TAIL(&head, e, next);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (QSIMPLEQ_EMPTY(&head)) {
 | 
			
		||||
            dolog("Internal error: no default audio driver available\n");
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return head;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* visitor to print -audiodev option */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    Visitor visitor;
 | 
			
		||||
 | 
			
		||||
    bool comma;
 | 
			
		||||
    GList *path;
 | 
			
		||||
} LegacyPrintVisitor;
 | 
			
		||||
 | 
			
		||||
static void lv_start_struct(Visitor *v, const char *name, void **obj,
 | 
			
		||||
                            size_t size, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
 | 
			
		||||
    lv->path = g_list_append(lv->path, g_strdup(name));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_end_struct(Visitor *v, void **obj)
 | 
			
		||||
{
 | 
			
		||||
    LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
 | 
			
		||||
    lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_print_key(Visitor *v, const char *name)
 | 
			
		||||
{
 | 
			
		||||
    GList *e;
 | 
			
		||||
    LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
 | 
			
		||||
    if (lv->comma) {
 | 
			
		||||
        putchar(',');
 | 
			
		||||
    } else {
 | 
			
		||||
        lv->comma = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (e = lv->path; e; e = e->next) {
 | 
			
		||||
        if (e->data) {
 | 
			
		||||
            printf("%s.", (const char *) e->data);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    printf("%s=", name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
 | 
			
		||||
                          Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    lv_print_key(v, name);
 | 
			
		||||
    printf("%" PRIi64, *obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 | 
			
		||||
                           Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    lv_print_key(v, name);
 | 
			
		||||
    printf("%" PRIu64, *obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    lv_print_key(v, name);
 | 
			
		||||
    printf("%s", *obj ? "on" : "off");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    const char *str = *obj;
 | 
			
		||||
    lv_print_key(v, name);
 | 
			
		||||
 | 
			
		||||
    while (*str) {
 | 
			
		||||
        if (*str == ',') {
 | 
			
		||||
            putchar(',');
 | 
			
		||||
        }
 | 
			
		||||
        putchar(*str++);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_complete(Visitor *v, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
 | 
			
		||||
    assert(lv->path == NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_free(Visitor *v)
 | 
			
		||||
{
 | 
			
		||||
    LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
 | 
			
		||||
 | 
			
		||||
    g_list_free_full(lv->path, g_free);
 | 
			
		||||
    g_free(lv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Visitor *legacy_visitor_new(void)
 | 
			
		||||
{
 | 
			
		||||
    LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
 | 
			
		||||
 | 
			
		||||
    lv->visitor.start_struct = lv_start_struct;
 | 
			
		||||
    lv->visitor.end_struct = lv_end_struct;
 | 
			
		||||
    /* lists not supported */
 | 
			
		||||
    lv->visitor.type_int64 = lv_type_int64;
 | 
			
		||||
    lv->visitor.type_uint64 = lv_type_uint64;
 | 
			
		||||
    lv->visitor.type_bool = lv_type_bool;
 | 
			
		||||
    lv->visitor.type_str = lv_type_str;
 | 
			
		||||
 | 
			
		||||
    lv->visitor.type = VISITOR_OUTPUT;
 | 
			
		||||
    lv->visitor.complete = lv_complete;
 | 
			
		||||
    lv->visitor.free = lv_free;
 | 
			
		||||
 | 
			
		||||
    return &lv->visitor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_legacy_help(void)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevListHead head;
 | 
			
		||||
    AudiodevListEntry *e;
 | 
			
		||||
 | 
			
		||||
    printf("Environment variable based configuration deprecated.\n");
 | 
			
		||||
    printf("Please use the new -audiodev option.\n");
 | 
			
		||||
 | 
			
		||||
    head = audio_handle_legacy_opts();
 | 
			
		||||
    printf("\nEquivalent -audiodev to your current environment variables:\n");
 | 
			
		||||
    if (!getenv("QEMU_AUDIO_DRV")) {
 | 
			
		||||
        printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
 | 
			
		||||
               "possibilities)\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(e, &head, next) {
 | 
			
		||||
        Visitor *v;
 | 
			
		||||
        Audiodev *dev = e->dev;
 | 
			
		||||
        printf("-audiodev ");
 | 
			
		||||
 | 
			
		||||
        v = legacy_visitor_new();
 | 
			
		||||
        visit_type_Audiodev(v, NULL, &dev, &error_abort);
 | 
			
		||||
        visit_free(v);
 | 
			
		||||
 | 
			
		||||
        printf("\n");
 | 
			
		||||
    }
 | 
			
		||||
    audio_free_audiodev_list(&head);
 | 
			
		||||
}
 | 
			
		||||
@@ -299,42 +299,11 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    switch (dev->driver) {
 | 
			
		||||
    case AUDIODEV_DRIVER_NONE:
 | 
			
		||||
        return dev->u.none.TYPE;
 | 
			
		||||
    case AUDIODEV_DRIVER_ALSA:
 | 
			
		||||
        return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
 | 
			
		||||
    case AUDIODEV_DRIVER_COREAUDIO:
 | 
			
		||||
        return qapi_AudiodevCoreaudioPerDirectionOptions_base(
 | 
			
		||||
            dev->u.coreaudio.TYPE);
 | 
			
		||||
    case AUDIODEV_DRIVER_DSOUND:
 | 
			
		||||
        return dev->u.dsound.TYPE;
 | 
			
		||||
    case AUDIODEV_DRIVER_OSS:
 | 
			
		||||
        return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
 | 
			
		||||
    case AUDIODEV_DRIVER_PA:
 | 
			
		||||
        return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
 | 
			
		||||
    case AUDIODEV_DRIVER_SDL:
 | 
			
		||||
        return dev->u.sdl.TYPE;
 | 
			
		||||
    case AUDIODEV_DRIVER_SPICE:
 | 
			
		||||
        return dev->u.spice.TYPE;
 | 
			
		||||
    case AUDIODEV_DRIVER_WAV:
 | 
			
		||||
        return dev->u.wav.TYPE;
 | 
			
		||||
 | 
			
		||||
    case AUDIODEV_DRIVER__MAX:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    HW *hw;
 | 
			
		||||
    AudioState *s = &glob_audio_state;
 | 
			
		||||
    AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
 | 
			
		||||
 | 
			
		||||
    if (pdo->fixed_settings) {
 | 
			
		||||
    if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
 | 
			
		||||
        hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
 | 
			
		||||
        if (hw) {
 | 
			
		||||
            return hw;
 | 
			
		||||
@@ -362,11 +331,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
 | 
			
		||||
    SW *sw;
 | 
			
		||||
    HW *hw;
 | 
			
		||||
    struct audsettings hw_as;
 | 
			
		||||
    AudioState *s = &glob_audio_state;
 | 
			
		||||
    AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
 | 
			
		||||
 | 
			
		||||
    if (pdo->fixed_settings) {
 | 
			
		||||
        hw_as = audiodev_to_audsettings(pdo);
 | 
			
		||||
    if (glue (conf.fixed_, TYPE).enabled) {
 | 
			
		||||
        hw_as = glue (conf.fixed_, TYPE).settings;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        hw_as = *as;
 | 
			
		||||
@@ -431,7 +398,6 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    AudioState *s = &glob_audio_state;
 | 
			
		||||
    AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
 | 
			
		||||
 | 
			
		||||
    if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
 | 
			
		||||
        dolog ("card=%p name=%p callback_fn=%p as=%p\n",
 | 
			
		||||
@@ -456,7 +422,7 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
        return sw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pdo->fixed_settings && sw) {
 | 
			
		||||
    if (!glue (conf.fixed_, TYPE).enabled && sw) {
 | 
			
		||||
        glue (AUD_close_, TYPE) (card, sw);
 | 
			
		||||
        sw = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
 | 
			
		||||
    wfx->cbSize = 0;
 | 
			
		||||
 | 
			
		||||
    switch (as->fmt) {
 | 
			
		||||
    case AUDIO_FORMAT_S8:
 | 
			
		||||
    case AUDIO_FORMAT_U8:
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        wfx->wBitsPerSample = 8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S16:
 | 
			
		||||
    case AUDIO_FORMAT_U16:
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        wfx->wBitsPerSample = 16;
 | 
			
		||||
        wfx->nAvgBytesPerSec <<= 1;
 | 
			
		||||
        wfx->nBlockAlign <<= 1;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S32:
 | 
			
		||||
    case AUDIO_FORMAT_U32:
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        wfx->wBitsPerSample = 32;
 | 
			
		||||
        wfx->nAvgBytesPerSec <<= 2;
 | 
			
		||||
        wfx->nBlockAlign <<= 2;
 | 
			
		||||
@@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
 | 
			
		||||
 | 
			
		||||
    switch (wfx->wBitsPerSample) {
 | 
			
		||||
    case 8:
 | 
			
		||||
        as->fmt = AUDIO_FORMAT_U8;
 | 
			
		||||
        as->fmt = AUD_FMT_U8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 16:
 | 
			
		||||
        as->fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        as->fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 32:
 | 
			
		||||
        as->fmt = AUDIO_FORMAT_S32;
 | 
			
		||||
        as->fmt = AUD_FMT_S32;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,11 @@
 | 
			
		||||
#define MAC_OS_X_VERSION_10_6 1060
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int buffer_frames;
 | 
			
		||||
    int nbuffers;
 | 
			
		||||
} CoreaudioConf;
 | 
			
		||||
 | 
			
		||||
typedef struct coreaudioVoiceOut {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    pthread_mutex_t mutex;
 | 
			
		||||
@@ -502,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    int err;
 | 
			
		||||
    const char *typ = "playback";
 | 
			
		||||
    AudioValueRange frameRange;
 | 
			
		||||
    Audiodev *dev = drv_opaque;
 | 
			
		||||
    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
 | 
			
		||||
    int frames;
 | 
			
		||||
    CoreaudioConf *conf = drv_opaque;
 | 
			
		||||
 | 
			
		||||
    /* create mutex */
 | 
			
		||||
    err = pthread_mutex_init(&core->mutex, NULL);
 | 
			
		||||
@@ -535,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    frames = audio_buffer_frames(
 | 
			
		||||
        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
 | 
			
		||||
    if (frameRange.mMinimum > frames) {
 | 
			
		||||
    if (frameRange.mMinimum > conf->buffer_frames) {
 | 
			
		||||
        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
 | 
			
		||||
        dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
 | 
			
		||||
    } else if (frameRange.mMaximum < frames) {
 | 
			
		||||
    }
 | 
			
		||||
    else if (frameRange.mMaximum < conf->buffer_frames) {
 | 
			
		||||
        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
 | 
			
		||||
        dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        core->audioDevicePropertyBufferFrameSize = frames;
 | 
			
		||||
        core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* set Buffer Frame Size */
 | 
			
		||||
@@ -566,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
                           "Could not get device buffer frame size\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
 | 
			
		||||
        core->audioDevicePropertyBufferFrameSize;
 | 
			
		||||
    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
 | 
			
		||||
 | 
			
		||||
    /* get StreamFormat */
 | 
			
		||||
    status = coreaudio_get_streamformat(core->outputDeviceID,
 | 
			
		||||
@@ -679,15 +680,40 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *coreaudio_audio_init(Audiodev *dev)
 | 
			
		||||
static CoreaudioConf glob_conf = {
 | 
			
		||||
    .buffer_frames = 512,
 | 
			
		||||
    .nbuffers = 4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *coreaudio_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    return dev;
 | 
			
		||||
    CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
 | 
			
		||||
    *conf = glob_conf;
 | 
			
		||||
 | 
			
		||||
    return conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void coreaudio_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    g_free(opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option coreaudio_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFFER_SIZE",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.buffer_frames,
 | 
			
		||||
        .descr = "Size of the buffer in frames"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFFER_COUNT",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.nbuffers,
 | 
			
		||||
        .descr = "Number of buffers"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
 | 
			
		||||
    .init_out = coreaudio_init_out,
 | 
			
		||||
    .fini_out = coreaudio_fini_out,
 | 
			
		||||
@@ -699,6 +725,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
 | 
			
		||||
static struct audio_driver coreaudio_audio_driver = {
 | 
			
		||||
    .name           = "coreaudio",
 | 
			
		||||
    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
 | 
			
		||||
    .options        = coreaudio_options,
 | 
			
		||||
    .init           = coreaudio_audio_init,
 | 
			
		||||
    .fini           = coreaudio_audio_fini,
 | 
			
		||||
    .pcm_ops        = &coreaudio_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
@@ -167,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    dsound *s = drv_opaque;
 | 
			
		||||
    WAVEFORMATEX wfx;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    DSoundConf *conf = &s->conf;
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
    const char *typ = "ADC";
 | 
			
		||||
    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 | 
			
		||||
    DSCBUFFERDESC bd;
 | 
			
		||||
    DSCBCAPS bc;
 | 
			
		||||
    AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
 | 
			
		||||
#else
 | 
			
		||||
    const char *typ = "DAC";
 | 
			
		||||
    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 | 
			
		||||
    DSBUFFERDESC bd;
 | 
			
		||||
    DSBCAPS bc;
 | 
			
		||||
    AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (!s->FIELD2) {
 | 
			
		||||
@@ -194,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    memset (&bd, 0, sizeof (bd));
 | 
			
		||||
    bd.dwSize = sizeof (bd);
 | 
			
		||||
    bd.lpwfxFormat = &wfx;
 | 
			
		||||
    bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
    bd.dwBufferBytes = conf->bufsize_in;
 | 
			
		||||
    hr = IDirectSoundCapture_CreateCaptureBuffer (
 | 
			
		||||
        s->dsound_capture,
 | 
			
		||||
        &bd,
 | 
			
		||||
@@ -204,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
        );
 | 
			
		||||
#else
 | 
			
		||||
    bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
 | 
			
		||||
    bd.dwBufferBytes = conf->bufsize_out;
 | 
			
		||||
    hr = IDirectSound_CreateSoundBuffer (
 | 
			
		||||
        s->dsound,
 | 
			
		||||
        &bd,
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "dsound"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
#include "qemu/host-utils.h"
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <mmsystem.h>
 | 
			
		||||
@@ -43,11 +42,17 @@
 | 
			
		||||
 | 
			
		||||
/* #define DEBUG_DSOUND */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int bufsize_in;
 | 
			
		||||
    int bufsize_out;
 | 
			
		||||
    int latency_millis;
 | 
			
		||||
} DSoundConf;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    LPDIRECTSOUND dsound;
 | 
			
		||||
    LPDIRECTSOUNDCAPTURE dsound_capture;
 | 
			
		||||
    struct audsettings settings;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
    DSoundConf conf;
 | 
			
		||||
} dsound;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -243,9 +248,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
 | 
			
		||||
    dsound_log_hresult (hr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
 | 
			
		||||
static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
 | 
			
		||||
{
 | 
			
		||||
    return muldiv64(usecs, info->bytes_per_second, 1000000);
 | 
			
		||||
    return (millis * info->bytes_per_second) / 1000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_DSOUND
 | 
			
		||||
@@ -473,7 +478,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
    LPVOID p1, p2;
 | 
			
		||||
    int bufsize;
 | 
			
		||||
    dsound *s = ds->s;
 | 
			
		||||
    AudiodevDsoundOptions *dso = &s->dev->u.dsound;
 | 
			
		||||
    DSoundConf *conf = &s->conf;
 | 
			
		||||
 | 
			
		||||
    if (!dsb) {
 | 
			
		||||
        dolog ("Attempt to run empty with playback buffer\n");
 | 
			
		||||
@@ -496,14 +501,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
    len = live << hwshift;
 | 
			
		||||
 | 
			
		||||
    if (ds->first_time) {
 | 
			
		||||
        if (dso->latency) {
 | 
			
		||||
        if (conf->latency_millis) {
 | 
			
		||||
            DWORD cur_blat;
 | 
			
		||||
 | 
			
		||||
            cur_blat = audio_ring_dist (wpos, ppos, bufsize);
 | 
			
		||||
            ds->first_time = 0;
 | 
			
		||||
            old_pos = wpos;
 | 
			
		||||
            old_pos +=
 | 
			
		||||
                usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
 | 
			
		||||
                millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
 | 
			
		||||
            old_pos %= bufsize;
 | 
			
		||||
            old_pos &= ~hw->info.align;
 | 
			
		||||
        }
 | 
			
		||||
@@ -742,6 +747,12 @@ static int dsound_run_in (HWVoiceIn *hw)
 | 
			
		||||
    return decr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DSoundConf glob_conf = {
 | 
			
		||||
    .bufsize_in         = 16384,
 | 
			
		||||
    .bufsize_out        = 16384,
 | 
			
		||||
    .latency_millis     = 10
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void dsound_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    HRESULT hr;
 | 
			
		||||
@@ -772,22 +783,13 @@ static void dsound_audio_fini (void *opaque)
 | 
			
		||||
    g_free(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *dsound_audio_init(Audiodev *dev)
 | 
			
		||||
static void *dsound_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    int err;
 | 
			
		||||
    HRESULT hr;
 | 
			
		||||
    dsound *s = g_malloc0(sizeof(dsound));
 | 
			
		||||
    AudiodevDsoundOptions *dso;
 | 
			
		||||
 | 
			
		||||
    assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
 | 
			
		||||
    s->dev = dev;
 | 
			
		||||
    dso = &dev->u.dsound;
 | 
			
		||||
 | 
			
		||||
    if (!dso->has_latency) {
 | 
			
		||||
        dso->has_latency = true;
 | 
			
		||||
        dso->latency = 10000; /* 10 ms */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->conf = glob_conf;
 | 
			
		||||
    hr = CoInitialize (NULL);
 | 
			
		||||
    if (FAILED (hr)) {
 | 
			
		||||
        dsound_logerr (hr, "Could not initialize COM\n");
 | 
			
		||||
@@ -852,6 +854,28 @@ static void *dsound_audio_init(Audiodev *dev)
 | 
			
		||||
    return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option dsound_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "LATENCY_MILLIS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.latency_millis,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFSIZE_OUT",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.bufsize_out,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFSIZE_IN",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.bufsize_in,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops dsound_pcm_ops = {
 | 
			
		||||
    .init_out = dsound_init_out,
 | 
			
		||||
    .fini_out = dsound_fini_out,
 | 
			
		||||
@@ -869,6 +893,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
 | 
			
		||||
static struct audio_driver dsound_audio_driver = {
 | 
			
		||||
    .name           = "dsound",
 | 
			
		||||
    .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
 | 
			
		||||
    .options        = dsound_options,
 | 
			
		||||
    .init           = dsound_audio_init,
 | 
			
		||||
    .fini           = dsound_audio_fini,
 | 
			
		||||
    .pcm_ops        = &dsound_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *no_audio_init(Audiodev *dev)
 | 
			
		||||
static void *no_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    return &no_audio_init;
 | 
			
		||||
}
 | 
			
		||||
@@ -163,6 +163,7 @@ static struct audio_pcm_ops no_pcm_ops = {
 | 
			
		||||
static struct audio_driver no_audio_driver = {
 | 
			
		||||
    .name           = "none",
 | 
			
		||||
    .descr          = "Timer based audio emulation",
 | 
			
		||||
    .options        = NULL,
 | 
			
		||||
    .init           = no_audio_init,
 | 
			
		||||
    .fini           = no_audio_fini,
 | 
			
		||||
    .pcm_ops        = &no_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										193
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							@@ -37,6 +37,16 @@
 | 
			
		||||
#define USE_DSP_POLICY
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct OSSConf {
 | 
			
		||||
    int try_mmap;
 | 
			
		||||
    int nfrags;
 | 
			
		||||
    int fragsize;
 | 
			
		||||
    const char *devpath_out;
 | 
			
		||||
    const char *devpath_in;
 | 
			
		||||
    int exclusive;
 | 
			
		||||
    int policy;
 | 
			
		||||
} OSSConf;
 | 
			
		||||
 | 
			
		||||
typedef struct OSSVoiceOut {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
@@ -46,7 +56,7 @@ typedef struct OSSVoiceOut {
 | 
			
		||||
    int fragsize;
 | 
			
		||||
    int mmapped;
 | 
			
		||||
    int pending;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
    OSSConf *conf;
 | 
			
		||||
} OSSVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct OSSVoiceIn {
 | 
			
		||||
@@ -55,12 +65,12 @@ typedef struct OSSVoiceIn {
 | 
			
		||||
    int fd;
 | 
			
		||||
    int nfrags;
 | 
			
		||||
    int fragsize;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
    OSSConf *conf;
 | 
			
		||||
} OSSVoiceIn;
 | 
			
		||||
 | 
			
		||||
struct oss_params {
 | 
			
		||||
    int freq;
 | 
			
		||||
    int fmt;
 | 
			
		||||
    audfmt_e fmt;
 | 
			
		||||
    int nchannels;
 | 
			
		||||
    int nfrags;
 | 
			
		||||
    int fragsize;
 | 
			
		||||
@@ -138,16 +148,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
 | 
			
		||||
static int aud_to_ossfmt (audfmt_e fmt, int endianness)
 | 
			
		||||
{
 | 
			
		||||
    switch (fmt) {
 | 
			
		||||
    case AUDIO_FORMAT_S8:
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
        return AFMT_S8;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        return AFMT_U8;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S16:
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
        if (endianness) {
 | 
			
		||||
            return AFMT_S16_BE;
 | 
			
		||||
        }
 | 
			
		||||
@@ -155,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
 | 
			
		||||
            return AFMT_S16_LE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        if (endianness) {
 | 
			
		||||
            return AFMT_U16_BE;
 | 
			
		||||
        }
 | 
			
		||||
@@ -172,37 +182,37 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
 | 
			
		||||
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
 | 
			
		||||
{
 | 
			
		||||
    switch (ossfmt) {
 | 
			
		||||
    case AFMT_S8:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S8;
 | 
			
		||||
        *fmt = AUD_FMT_S8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AFMT_U8:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U8;
 | 
			
		||||
        *fmt = AUD_FMT_U8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AFMT_S16_LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        *fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AFMT_U16_LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U16;
 | 
			
		||||
        *fmt = AUD_FMT_U16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AFMT_S16_BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        *fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AFMT_U16_BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U16;
 | 
			
		||||
        *fmt = AUD_FMT_U16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
@@ -252,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ)
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int oss_open(int in, struct oss_params *req, audsettings *as,
 | 
			
		||||
                    struct oss_params *obt, int *pfd, Audiodev *dev)
 | 
			
		||||
static int oss_open (int in, struct oss_params *req,
 | 
			
		||||
                     struct oss_params *obt, int *pfd, OSSConf* conf)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevOssOptions *oopts = &dev->u.oss;
 | 
			
		||||
    AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
 | 
			
		||||
    int fd;
 | 
			
		||||
    int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
 | 
			
		||||
    int oflags = conf->exclusive ? O_EXCL : 0;
 | 
			
		||||
    audio_buf_info abinfo;
 | 
			
		||||
    int fmt, freq, nchannels;
 | 
			
		||||
    int setfragment = 1;
 | 
			
		||||
    const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
 | 
			
		||||
    const char *dspname = in ? conf->devpath_in : conf->devpath_out;
 | 
			
		||||
    const char *typ = in ? "ADC" : "DAC";
 | 
			
		||||
#ifdef USE_DSP_POLICY
 | 
			
		||||
    int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Kludge needed to have working mmap on Linux */
 | 
			
		||||
    oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
 | 
			
		||||
        O_RDWR : (in ? O_RDONLY : O_WRONLY);
 | 
			
		||||
    oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
 | 
			
		||||
 | 
			
		||||
    fd = open (dspname, oflags | O_NONBLOCK);
 | 
			
		||||
    if (-1 == fd) {
 | 
			
		||||
@@ -281,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
 | 
			
		||||
    freq = req->freq;
 | 
			
		||||
    nchannels = req->nchannels;
 | 
			
		||||
    fmt = req->fmt;
 | 
			
		||||
    req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
 | 
			
		||||
    req->fragsize = audio_buffer_bytes(
 | 
			
		||||
        qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
 | 
			
		||||
 | 
			
		||||
    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
 | 
			
		||||
        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
 | 
			
		||||
@@ -307,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DSP_POLICY
 | 
			
		||||
    if (policy >= 0) {
 | 
			
		||||
    if (conf->policy >= 0) {
 | 
			
		||||
        int version;
 | 
			
		||||
 | 
			
		||||
        if (!oss_get_version (fd, &version, typ)) {
 | 
			
		||||
            trace_oss_version(version);
 | 
			
		||||
 | 
			
		||||
            if (version >= 0x040000) {
 | 
			
		||||
                int policy2 = policy;
 | 
			
		||||
                if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
 | 
			
		||||
                int policy = conf->policy;
 | 
			
		||||
                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
 | 
			
		||||
                    oss_logerr2 (errno, typ,
 | 
			
		||||
                                 "Failed to set timing policy to %d\n",
 | 
			
		||||
                                 policy);
 | 
			
		||||
                                 conf->policy);
 | 
			
		||||
                    goto err;
 | 
			
		||||
                }
 | 
			
		||||
                setfragment = 0;
 | 
			
		||||
@@ -499,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    int endianness;
 | 
			
		||||
    int err;
 | 
			
		||||
    int fd;
 | 
			
		||||
    AudioFormat effective_fmt;
 | 
			
		||||
    audfmt_e effective_fmt;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    Audiodev *dev = drv_opaque;
 | 
			
		||||
    AudiodevOssOptions *oopts = &dev->u.oss;
 | 
			
		||||
    OSSConf *conf = drv_opaque;
 | 
			
		||||
 | 
			
		||||
    oss->fd = -1;
 | 
			
		||||
 | 
			
		||||
    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
 | 
			
		||||
    req.freq = as->freq;
 | 
			
		||||
    req.nchannels = as->nchannels;
 | 
			
		||||
    req.fragsize = conf->fragsize;
 | 
			
		||||
    req.nfrags = conf->nfrags;
 | 
			
		||||
 | 
			
		||||
    if (oss_open(0, &req, as, &obt, &fd, dev)) {
 | 
			
		||||
    if (oss_open (0, &req, &obt, &fd, conf)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -537,7 +539,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 | 
			
		||||
 | 
			
		||||
    oss->mmapped = 0;
 | 
			
		||||
    if (oopts->has_try_mmap && oopts->try_mmap) {
 | 
			
		||||
    if (conf->try_mmap) {
 | 
			
		||||
        oss->pcm_buf = mmap (
 | 
			
		||||
            NULL,
 | 
			
		||||
            hw->samples << hw->info.shift,
 | 
			
		||||
@@ -595,7 +597,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    oss->fd = fd;
 | 
			
		||||
    oss->dev = dev;
 | 
			
		||||
    oss->conf = conf;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -603,12 +605,16 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    int trig;
 | 
			
		||||
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 | 
			
		||||
    AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
 | 
			
		||||
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
    case VOICE_ENABLE:
 | 
			
		||||
        {
 | 
			
		||||
            bool poll_mode = opdo->try_poll;
 | 
			
		||||
            va_list ap;
 | 
			
		||||
            int poll_mode;
 | 
			
		||||
 | 
			
		||||
            va_start (ap, cmd);
 | 
			
		||||
            poll_mode = va_arg (ap, int);
 | 
			
		||||
            va_end (ap);
 | 
			
		||||
 | 
			
		||||
            ldebug ("enabling voice\n");
 | 
			
		||||
            if (poll_mode) {
 | 
			
		||||
@@ -661,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
    int endianness;
 | 
			
		||||
    int err;
 | 
			
		||||
    int fd;
 | 
			
		||||
    AudioFormat effective_fmt;
 | 
			
		||||
    audfmt_e effective_fmt;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    Audiodev *dev = drv_opaque;
 | 
			
		||||
    OSSConf *conf = drv_opaque;
 | 
			
		||||
 | 
			
		||||
    oss->fd = -1;
 | 
			
		||||
 | 
			
		||||
    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
 | 
			
		||||
    req.freq = as->freq;
 | 
			
		||||
    req.nchannels = as->nchannels;
 | 
			
		||||
    if (oss_open(1, &req, as, &obt, &fd, dev)) {
 | 
			
		||||
    req.fragsize = conf->fragsize;
 | 
			
		||||
    req.nfrags = conf->nfrags;
 | 
			
		||||
    if (oss_open (1, &req, &obt, &fd, conf)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -704,7 +712,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    oss->fd = fd;
 | 
			
		||||
    oss->dev = dev;
 | 
			
		||||
    oss->conf = conf;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -795,12 +803,16 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
 | 
			
		||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 | 
			
		||||
    AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
 | 
			
		||||
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
    case VOICE_ENABLE:
 | 
			
		||||
        {
 | 
			
		||||
            bool poll_mode = opdo->try_poll;
 | 
			
		||||
            va_list ap;
 | 
			
		||||
            int poll_mode;
 | 
			
		||||
 | 
			
		||||
            va_start (ap, cmd);
 | 
			
		||||
            poll_mode = va_arg (ap, int);
 | 
			
		||||
            va_end (ap);
 | 
			
		||||
 | 
			
		||||
            if (poll_mode) {
 | 
			
		||||
                oss_poll_in (hw);
 | 
			
		||||
@@ -820,36 +832,82 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
 | 
			
		||||
static OSSConf glob_conf = {
 | 
			
		||||
    .try_mmap = 0,
 | 
			
		||||
    .nfrags = 4,
 | 
			
		||||
    .fragsize = 4096,
 | 
			
		||||
    .devpath_out = "/dev/dsp",
 | 
			
		||||
    .devpath_in = "/dev/dsp",
 | 
			
		||||
    .exclusive = 0,
 | 
			
		||||
    .policy = 5
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *oss_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    if (!opdo->has_try_poll) {
 | 
			
		||||
        opdo->try_poll = true;
 | 
			
		||||
        opdo->has_try_poll = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    OSSConf *conf = g_malloc(sizeof(OSSConf));
 | 
			
		||||
    *conf = glob_conf;
 | 
			
		||||
 | 
			
		||||
static void *oss_audio_init(Audiodev *dev)
 | 
			
		||||
{
 | 
			
		||||
    AudiodevOssOptions *oopts;
 | 
			
		||||
    assert(dev->driver == AUDIODEV_DRIVER_OSS);
 | 
			
		||||
 | 
			
		||||
    oopts = &dev->u.oss;
 | 
			
		||||
    oss_init_per_direction(oopts->in);
 | 
			
		||||
    oss_init_per_direction(oopts->out);
 | 
			
		||||
 | 
			
		||||
    if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
 | 
			
		||||
               R_OK | W_OK) < 0 ||
 | 
			
		||||
        access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
 | 
			
		||||
               R_OK | W_OK) < 0) {
 | 
			
		||||
    if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
 | 
			
		||||
        access(conf->devpath_out, R_OK | W_OK) < 0) {
 | 
			
		||||
        g_free(conf);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return dev;
 | 
			
		||||
    return conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void oss_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    g_free(opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option oss_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FRAGSIZE",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.fragsize,
 | 
			
		||||
        .descr = "Fragment size in bytes"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "NFRAGS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.nfrags,
 | 
			
		||||
        .descr = "Number of fragments"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "MMAP",
 | 
			
		||||
        .tag   = AUD_OPT_BOOL,
 | 
			
		||||
        .valp  = &glob_conf.try_mmap,
 | 
			
		||||
        .descr = "Try using memory mapped access"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DAC_DEV",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &glob_conf.devpath_out,
 | 
			
		||||
        .descr = "Path to DAC device"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "ADC_DEV",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &glob_conf.devpath_in,
 | 
			
		||||
        .descr = "Path to ADC device"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "EXCLUSIVE",
 | 
			
		||||
        .tag   = AUD_OPT_BOOL,
 | 
			
		||||
        .valp  = &glob_conf.exclusive,
 | 
			
		||||
        .descr = "Open device in exclusive mode (vmix won't work)"
 | 
			
		||||
    },
 | 
			
		||||
#ifdef USE_DSP_POLICY
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "POLICY",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.policy,
 | 
			
		||||
        .descr = "Set the timing policy of the device, -1 to use fragment mode",
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops oss_pcm_ops = {
 | 
			
		||||
    .init_out = oss_init_out,
 | 
			
		||||
    .fini_out = oss_fini_out,
 | 
			
		||||
@@ -867,6 +925,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
 | 
			
		||||
static struct audio_driver oss_audio_driver = {
 | 
			
		||||
    .name           = "oss",
 | 
			
		||||
    .descr          = "OSS http://www.opensound.com",
 | 
			
		||||
    .options        = oss_options,
 | 
			
		||||
    .init           = oss_audio_init,
 | 
			
		||||
    .fini           = oss_audio_fini,
 | 
			
		||||
    .pcm_ops        = &oss_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										149
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								audio/paaudio.c
									
									
									
									
									
								
							@@ -2,7 +2,6 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "qapi/opts-visitor.h"
 | 
			
		||||
 | 
			
		||||
#include <pulse/pulseaudio.h>
 | 
			
		||||
 | 
			
		||||
@@ -11,7 +10,14 @@
 | 
			
		||||
#include "audio_pt_int.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
    int samples;
 | 
			
		||||
    char *server;
 | 
			
		||||
    char *sink;
 | 
			
		||||
    char *source;
 | 
			
		||||
} PAConf;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PAConf conf;
 | 
			
		||||
    pa_threaded_mainloop *mainloop;
 | 
			
		||||
    pa_context *context;
 | 
			
		||||
} paaudio;
 | 
			
		||||
@@ -26,7 +32,6 @@ typedef struct {
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    struct audio_pt pt;
 | 
			
		||||
    paaudio *g;
 | 
			
		||||
    int samples;
 | 
			
		||||
} PAVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -41,7 +46,6 @@ typedef struct {
 | 
			
		||||
    const void *read_data;
 | 
			
		||||
    size_t read_index, read_length;
 | 
			
		||||
    paaudio *g;
 | 
			
		||||
    int samples;
 | 
			
		||||
} PAVoiceIn;
 | 
			
		||||
 | 
			
		||||
static void qpa_audio_fini(void *opaque);
 | 
			
		||||
@@ -223,7 +227,7 @@ static void *qpa_thread_out (void *arg)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        decr = to_mix = audio_MIN(pa->live, pa->samples >> 5);
 | 
			
		||||
        decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5);
 | 
			
		||||
        rpos = pa->rpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock(&pa->pt, __func__)) {
 | 
			
		||||
@@ -315,7 +319,7 @@ static void *qpa_thread_in (void *arg)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5);
 | 
			
		||||
        incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5);
 | 
			
		||||
        wpos = pa->wpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock(&pa->pt, __func__)) {
 | 
			
		||||
@@ -381,21 +385,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
 | 
			
		||||
    return audio_pcm_sw_read (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
 | 
			
		||||
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
 | 
			
		||||
{
 | 
			
		||||
    int format;
 | 
			
		||||
 | 
			
		||||
    switch (afmt) {
 | 
			
		||||
    case AUDIO_FORMAT_S8:
 | 
			
		||||
    case AUDIO_FORMAT_U8:
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        format = PA_SAMPLE_U8;
 | 
			
		||||
        break;
 | 
			
		||||
    case AUDIO_FORMAT_S16:
 | 
			
		||||
    case AUDIO_FORMAT_U16:
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
 | 
			
		||||
        break;
 | 
			
		||||
    case AUDIO_FORMAT_S32:
 | 
			
		||||
    case AUDIO_FORMAT_U32:
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
@@ -406,26 +410,26 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
 | 
			
		||||
    return format;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 | 
			
		||||
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 | 
			
		||||
{
 | 
			
		||||
    switch (fmt) {
 | 
			
		||||
    case PA_SAMPLE_U8:
 | 
			
		||||
        return AUDIO_FORMAT_U8;
 | 
			
		||||
        return AUD_FMT_U8;
 | 
			
		||||
    case PA_SAMPLE_S16BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        return AUDIO_FORMAT_S16;
 | 
			
		||||
        return AUD_FMT_S16;
 | 
			
		||||
    case PA_SAMPLE_S16LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        return AUDIO_FORMAT_S16;
 | 
			
		||||
        return AUD_FMT_S16;
 | 
			
		||||
    case PA_SAMPLE_S32BE:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        return AUDIO_FORMAT_S32;
 | 
			
		||||
        return AUD_FMT_S32;
 | 
			
		||||
    case PA_SAMPLE_S32LE:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        return AUDIO_FORMAT_S32;
 | 
			
		||||
        return AUD_FMT_S32;
 | 
			
		||||
    default:
 | 
			
		||||
        dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
 | 
			
		||||
        return AUDIO_FORMAT_U8;
 | 
			
		||||
        return AUD_FMT_U8;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -542,15 +546,17 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 | 
			
		||||
    paaudio *g = pa->g = drv_opaque;
 | 
			
		||||
    AudiodevPaOptions *popts = &g->dev->u.pa;
 | 
			
		||||
    AudiodevPaPerDirectionOptions *ppdo = popts->out;
 | 
			
		||||
 | 
			
		||||
    ss.format = audfmt_to_pa (as->fmt, as->endianness);
 | 
			
		||||
    ss.channels = as->nchannels;
 | 
			
		||||
    ss.rate = as->freq;
 | 
			
		||||
 | 
			
		||||
    ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
 | 
			
		||||
    ba.minreq = -1;
 | 
			
		||||
    /*
 | 
			
		||||
     * qemu audio tick runs at 100 Hz (by default), so processing
 | 
			
		||||
     * data chunks worth 10 ms of sound should be a good fit.
 | 
			
		||||
     */
 | 
			
		||||
    ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
 | 
			
		||||
    ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
 | 
			
		||||
    ba.maxlength = -1;
 | 
			
		||||
    ba.prebuf = -1;
 | 
			
		||||
 | 
			
		||||
@@ -560,7 +566,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
        g,
 | 
			
		||||
        "qemu",
 | 
			
		||||
        PA_STREAM_PLAYBACK,
 | 
			
		||||
        ppdo->has_name ? ppdo->name : NULL,
 | 
			
		||||
        g->conf.sink,
 | 
			
		||||
        &ss,
 | 
			
		||||
        NULL,                   /* channel map */
 | 
			
		||||
        &ba,                    /* buffering attributes */
 | 
			
		||||
@@ -572,9 +578,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    hw->samples = pa->samples = audio_buffer_samples(
 | 
			
		||||
        qapi_AudiodevPaPerDirectionOptions_base(ppdo),
 | 
			
		||||
        &obt_as, ppdo->buffer_length);
 | 
			
		||||
    hw->samples = g->conf.samples;
 | 
			
		||||
    pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
    pa->rpos = hw->rpos;
 | 
			
		||||
    if (!pa->pcm_buf) {
 | 
			
		||||
@@ -605,32 +609,24 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
{
 | 
			
		||||
    int error;
 | 
			
		||||
    pa_sample_spec ss;
 | 
			
		||||
    pa_buffer_attr ba;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
    PAVoiceIn *pa = (PAVoiceIn *) hw;
 | 
			
		||||
    paaudio *g = pa->g = drv_opaque;
 | 
			
		||||
    AudiodevPaOptions *popts = &g->dev->u.pa;
 | 
			
		||||
    AudiodevPaPerDirectionOptions *ppdo = popts->in;
 | 
			
		||||
 | 
			
		||||
    ss.format = audfmt_to_pa (as->fmt, as->endianness);
 | 
			
		||||
    ss.channels = as->nchannels;
 | 
			
		||||
    ss.rate = as->freq;
 | 
			
		||||
 | 
			
		||||
    ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
 | 
			
		||||
    ba.maxlength = -1;
 | 
			
		||||
    ba.minreq = -1;
 | 
			
		||||
    ba.prebuf = -1;
 | 
			
		||||
 | 
			
		||||
    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 | 
			
		||||
 | 
			
		||||
    pa->stream = qpa_simple_new (
 | 
			
		||||
        g,
 | 
			
		||||
        "qemu",
 | 
			
		||||
        PA_STREAM_RECORD,
 | 
			
		||||
        ppdo->has_name ? ppdo->name : NULL,
 | 
			
		||||
        g->conf.source,
 | 
			
		||||
        &ss,
 | 
			
		||||
        NULL,                   /* channel map */
 | 
			
		||||
        &ba,                    /* buffering attributes */
 | 
			
		||||
        NULL,                   /* buffering attributes */
 | 
			
		||||
        &error
 | 
			
		||||
        );
 | 
			
		||||
    if (!pa->stream) {
 | 
			
		||||
@@ -639,9 +635,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    hw->samples = pa->samples = audio_buffer_samples(
 | 
			
		||||
        qapi_AudiodevPaPerDirectionOptions_base(ppdo),
 | 
			
		||||
        &obt_as, ppdo->buffer_length);
 | 
			
		||||
    hw->samples = g->conf.samples;
 | 
			
		||||
    pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
    pa->wpos = hw->wpos;
 | 
			
		||||
    if (!pa->pcm_buf) {
 | 
			
		||||
@@ -813,27 +807,14 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qpa_validate_per_direction_opts(Audiodev *dev,
 | 
			
		||||
                                           AudiodevPaPerDirectionOptions *pdo)
 | 
			
		||||
{
 | 
			
		||||
    if (!pdo->has_buffer_length) {
 | 
			
		||||
        pdo->has_buffer_length = true;
 | 
			
		||||
        pdo->buffer_length = 46440;
 | 
			
		||||
    }
 | 
			
		||||
    if (!pdo->has_latency) {
 | 
			
		||||
        pdo->has_latency = true;
 | 
			
		||||
        pdo->latency = 15000;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
/* common */
 | 
			
		||||
static PAConf glob_conf = {
 | 
			
		||||
    .samples = 4096,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *qpa_audio_init(Audiodev *dev)
 | 
			
		||||
static void *qpa_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    paaudio *g;
 | 
			
		||||
    AudiodevPaOptions *popts = &dev->u.pa;
 | 
			
		||||
    const char *server;
 | 
			
		||||
 | 
			
		||||
    if (!popts->has_server) {
 | 
			
		||||
    if (glob_conf.server == NULL) {
 | 
			
		||||
        char pidfile[64];
 | 
			
		||||
        char *runtime;
 | 
			
		||||
        struct stat st;
 | 
			
		||||
@@ -848,19 +829,8 @@ static void *qpa_audio_init(Audiodev *dev)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(dev->driver == AUDIODEV_DRIVER_PA);
 | 
			
		||||
 | 
			
		||||
    g = g_malloc(sizeof(paaudio));
 | 
			
		||||
    server = popts->has_server ? popts->server : NULL;
 | 
			
		||||
 | 
			
		||||
    if (!qpa_validate_per_direction_opts(dev, popts->in)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (!qpa_validate_per_direction_opts(dev, popts->out)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g->dev = dev;
 | 
			
		||||
    paaudio *g = g_malloc(sizeof(paaudio));
 | 
			
		||||
    g->conf = glob_conf;
 | 
			
		||||
    g->mainloop = NULL;
 | 
			
		||||
    g->context = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -870,14 +840,14 @@ static void *qpa_audio_init(Audiodev *dev)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
 | 
			
		||||
                                 server);
 | 
			
		||||
                                 g->conf.server);
 | 
			
		||||
    if (!g->context) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_context_set_state_callback (g->context, context_state_cb, g);
 | 
			
		||||
 | 
			
		||||
    if (pa_context_connect(g->context, server, 0, NULL) < 0) {
 | 
			
		||||
    if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
 | 
			
		||||
        qpa_logerr (pa_context_errno (g->context),
 | 
			
		||||
                    "pa_context_connect() failed\n");
 | 
			
		||||
        goto fail;
 | 
			
		||||
@@ -940,6 +910,34 @@ static void qpa_audio_fini (void *opaque)
 | 
			
		||||
    g_free(g);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct audio_option qpa_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SAMPLES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.samples,
 | 
			
		||||
        .descr = "buffer size in samples"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SERVER",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &glob_conf.server,
 | 
			
		||||
        .descr = "server address"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SINK",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &glob_conf.sink,
 | 
			
		||||
        .descr = "sink device name"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SOURCE",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &glob_conf.source,
 | 
			
		||||
        .descr = "source device name"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops qpa_pcm_ops = {
 | 
			
		||||
    .init_out = qpa_init_out,
 | 
			
		||||
    .fini_out = qpa_fini_out,
 | 
			
		||||
@@ -957,6 +955,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
 | 
			
		||||
static struct audio_driver pa_audio_driver = {
 | 
			
		||||
    .name           = "pa",
 | 
			
		||||
    .descr          = "http://www.pulseaudio.org/",
 | 
			
		||||
    .options        = qpa_options,
 | 
			
		||||
    .init           = qpa_audio_init,
 | 
			
		||||
    .fini           = qpa_audio_fini,
 | 
			
		||||
    .pcm_ops        = &qpa_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										208
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							@@ -38,17 +38,31 @@
 | 
			
		||||
#define AUDIO_CAP "sdl"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
 | 
			
		||||
 | 
			
		||||
typedef struct SDLVoiceOut {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    int live;
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    int rpos;
 | 
			
		||||
#endif
 | 
			
		||||
    int decr;
 | 
			
		||||
} SDLVoiceOut;
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    int nb_samples;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .nb_samples = 1024
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct SDLAudioState {
 | 
			
		||||
    int exit;
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    SDL_mutex *mutex;
 | 
			
		||||
    SDL_sem *sem;
 | 
			
		||||
#endif
 | 
			
		||||
    int initialized;
 | 
			
		||||
    bool driver_created;
 | 
			
		||||
    Audiodev *dev;
 | 
			
		||||
} glob_sdl;
 | 
			
		||||
typedef struct SDLAudioState SDLAudioState;
 | 
			
		||||
 | 
			
		||||
@@ -63,19 +77,79 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
 | 
			
		||||
    AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aud_to_sdlfmt (AudioFormat fmt)
 | 
			
		||||
static int sdl_lock (SDLAudioState *s, const char *forfn)
 | 
			
		||||
{
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    if (SDL_LockMutex (s->mutex)) {
 | 
			
		||||
        sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    SDL_LockAudio();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdl_unlock (SDLAudioState *s, const char *forfn)
 | 
			
		||||
{
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    if (SDL_UnlockMutex (s->mutex)) {
 | 
			
		||||
        sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    SDL_UnlockAudio();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdl_post (SDLAudioState *s, const char *forfn)
 | 
			
		||||
{
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    if (SDL_SemPost (s->sem)) {
 | 
			
		||||
        sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
static int sdl_wait (SDLAudioState *s, const char *forfn)
 | 
			
		||||
{
 | 
			
		||||
    if (SDL_SemWait (s->sem)) {
 | 
			
		||||
        sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
 | 
			
		||||
{
 | 
			
		||||
    if (sdl_unlock (s, forfn)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sdl_post (s, forfn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aud_to_sdlfmt (audfmt_e fmt)
 | 
			
		||||
{
 | 
			
		||||
    switch (fmt) {
 | 
			
		||||
    case AUDIO_FORMAT_S8:
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
        return AUDIO_S8;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        return AUDIO_U8;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S16:
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
        return AUDIO_S16LSB;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_U16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        return AUDIO_U16LSB;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
@@ -87,37 +161,37 @@ static int aud_to_sdlfmt (AudioFormat fmt)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
 | 
			
		||||
static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
 | 
			
		||||
{
 | 
			
		||||
    switch (sdlfmt) {
 | 
			
		||||
    case AUDIO_S8:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S8;
 | 
			
		||||
        *fmt = AUD_FMT_S8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_U8:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U8;
 | 
			
		||||
        *fmt = AUD_FMT_U8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_S16LSB:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        *fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_U16LSB:
 | 
			
		||||
        *endianness = 0;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U16;
 | 
			
		||||
        *fmt = AUD_FMT_U16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_S16MSB:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_S16;
 | 
			
		||||
        *fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_U16MSB:
 | 
			
		||||
        *endianness = 1;
 | 
			
		||||
        *fmt = AUDIO_FORMAT_U16;
 | 
			
		||||
        *fmt = AUD_FMT_U16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
@@ -169,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
 | 
			
		||||
static void sdl_close (SDLAudioState *s)
 | 
			
		||||
{
 | 
			
		||||
    if (s->initialized) {
 | 
			
		||||
        SDL_LockAudio();
 | 
			
		||||
        sdl_lock (s, "sdl_close");
 | 
			
		||||
        s->exit = 1;
 | 
			
		||||
        SDL_UnlockAudio();
 | 
			
		||||
        sdl_unlock_and_post (s, "sdl_close");
 | 
			
		||||
        SDL_PauseAudio (1);
 | 
			
		||||
        SDL_CloseAudio ();
 | 
			
		||||
        s->initialized = 0;
 | 
			
		||||
@@ -184,14 +258,41 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
 | 
			
		||||
    SDLAudioState *s = &glob_sdl;
 | 
			
		||||
    HWVoiceOut *hw = &sdl->hw;
 | 
			
		||||
    int samples = len >> hw->info.shift;
 | 
			
		||||
    int to_mix, decr;
 | 
			
		||||
 | 
			
		||||
    if (s->exit || !sdl->live) {
 | 
			
		||||
    if (s->exit) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
 | 
			
		||||
    while (samples) {
 | 
			
		||||
        int to_mix, decr;
 | 
			
		||||
 | 
			
		||||
        /* dolog ("in callback samples=%d\n", samples); */
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
        sdl_wait (s, "sdl_callback");
 | 
			
		||||
        if (s->exit) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sdl_lock (s, "sdl_callback")) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
 | 
			
		||||
            dolog ("sdl->live=%d hw->samples=%d\n",
 | 
			
		||||
                   sdl->live, hw->samples);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!sdl->live) {
 | 
			
		||||
            goto again;
 | 
			
		||||
        }
 | 
			
		||||
#else
 | 
			
		||||
        if (s->exit || !sdl->live) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        /* dolog ("in callback live=%d\n", live); */
 | 
			
		||||
        to_mix = audio_MIN (samples, sdl->live);
 | 
			
		||||
        decr = to_mix;
 | 
			
		||||
        while (to_mix) {
 | 
			
		||||
@@ -200,7 +301,11 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
 | 
			
		||||
 | 
			
		||||
            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
 | 
			
		||||
            hw->clip (buf, src, chunk);
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
 | 
			
		||||
#else
 | 
			
		||||
            hw->rpos = (hw->rpos + chunk) % hw->samples;
 | 
			
		||||
#endif
 | 
			
		||||
            to_mix -= chunk;
 | 
			
		||||
            buf += chunk << hw->info.shift;
 | 
			
		||||
        }
 | 
			
		||||
@@ -208,12 +313,21 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
 | 
			
		||||
        sdl->live -= decr;
 | 
			
		||||
        sdl->decr += decr;
 | 
			
		||||
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    again:
 | 
			
		||||
        if (sdl_unlock (s, "sdl_callback")) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    /* dolog ("done len=%d\n", len); */
 | 
			
		||||
 | 
			
		||||
#if (SDL_MAJOR_VERSION >= 2)
 | 
			
		||||
    /* SDL2 does not clear the remaining buffer for us, so do it on our own */
 | 
			
		||||
    if (samples) {
 | 
			
		||||
        memset(buf, 0, samples << hw->info.shift);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
@@ -225,8 +339,11 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
{
 | 
			
		||||
    int decr;
 | 
			
		||||
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
 | 
			
		||||
    SDLAudioState *s = &glob_sdl;
 | 
			
		||||
 | 
			
		||||
    SDL_LockAudio();
 | 
			
		||||
    if (sdl_lock (s, "sdl_run_out")) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sdl->decr > live) {
 | 
			
		||||
        ldebug ("sdl->decr %d live %d sdl->live %d\n",
 | 
			
		||||
@@ -238,10 +355,19 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
    decr = audio_MIN (sdl->decr, live);
 | 
			
		||||
    sdl->decr -= decr;
 | 
			
		||||
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    sdl->live = live - decr;
 | 
			
		||||
    hw->rpos = sdl->rpos;
 | 
			
		||||
#else
 | 
			
		||||
    sdl->live = live;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    SDL_UnlockAudio();
 | 
			
		||||
 | 
			
		||||
    if (sdl->live > 0) {
 | 
			
		||||
        sdl_unlock_and_post (s, "sdl_run_out");
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        sdl_unlock (s, "sdl_run_out");
 | 
			
		||||
    }
 | 
			
		||||
    return decr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -260,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    SDL_AudioSpec req, obt;
 | 
			
		||||
    int endianness;
 | 
			
		||||
    int err;
 | 
			
		||||
    AudioFormat effective_fmt;
 | 
			
		||||
    audfmt_e effective_fmt;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
 | 
			
		||||
    req.freq = as->freq;
 | 
			
		||||
    req.format = aud_to_sdlfmt (as->fmt);
 | 
			
		||||
    req.channels = as->nchannels;
 | 
			
		||||
    req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
 | 
			
		||||
    req.samples = conf.nb_samples;
 | 
			
		||||
    req.callback = sdl_callback;
 | 
			
		||||
    req.userdata = sdl;
 | 
			
		||||
 | 
			
		||||
@@ -310,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *sdl_audio_init(Audiodev *dev)
 | 
			
		||||
static void *sdl_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    SDLAudioState *s = &glob_sdl;
 | 
			
		||||
    if (s->driver_created) {
 | 
			
		||||
@@ -323,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev)
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    s->mutex = SDL_CreateMutex ();
 | 
			
		||||
    if (!s->mutex) {
 | 
			
		||||
        sdl_logerr ("Failed to create SDL mutex\n");
 | 
			
		||||
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->sem = SDL_CreateSemaphore (0);
 | 
			
		||||
    if (!s->sem) {
 | 
			
		||||
        sdl_logerr ("Failed to create SDL semaphore\n");
 | 
			
		||||
        SDL_DestroyMutex (s->mutex);
 | 
			
		||||
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    s->driver_created = true;
 | 
			
		||||
    s->dev = dev;
 | 
			
		||||
    return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -332,11 +474,24 @@ static void sdl_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    SDLAudioState *s = opaque;
 | 
			
		||||
    sdl_close (s);
 | 
			
		||||
#if USE_SEMAPHORE
 | 
			
		||||
    SDL_DestroySemaphore (s->sem);
 | 
			
		||||
    SDL_DestroyMutex (s->mutex);
 | 
			
		||||
#endif
 | 
			
		||||
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
 | 
			
		||||
    s->driver_created = false;
 | 
			
		||||
    s->dev = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option sdl_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SAMPLES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.nb_samples,
 | 
			
		||||
        .descr = "Size of SDL buffer in samples"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops sdl_pcm_ops = {
 | 
			
		||||
    .init_out = sdl_init_out,
 | 
			
		||||
    .fini_out = sdl_fini_out,
 | 
			
		||||
@@ -348,6 +503,7 @@ static struct audio_pcm_ops sdl_pcm_ops = {
 | 
			
		||||
static struct audio_driver sdl_audio_driver = {
 | 
			
		||||
    .name           = "sdl",
 | 
			
		||||
    .descr          = "SDL http://www.libsdl.org",
 | 
			
		||||
    .options        = sdl_options,
 | 
			
		||||
    .init           = sdl_audio_init,
 | 
			
		||||
    .fini           = sdl_audio_fini,
 | 
			
		||||
    .pcm_ops        = &sdl_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
 | 
			
		||||
    .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *spice_audio_init(Audiodev *dev)
 | 
			
		||||
static void *spice_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    if (!using_spice) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ;
 | 
			
		||||
#endif
 | 
			
		||||
    settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN;
 | 
			
		||||
    settings.fmt        = AUDIO_FORMAT_S16;
 | 
			
		||||
    settings.fmt        = AUD_FMT_S16;
 | 
			
		||||
    settings.endianness = AUDIO_HOST_ENDIANNESS;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &settings);
 | 
			
		||||
@@ -258,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 | 
			
		||||
    settings.freq       = SPICE_INTERFACE_RECORD_FREQ;
 | 
			
		||||
#endif
 | 
			
		||||
    settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN;
 | 
			
		||||
    settings.fmt        = AUDIO_FORMAT_S16;
 | 
			
		||||
    settings.fmt        = AUD_FMT_S16;
 | 
			
		||||
    settings.endianness = AUDIO_HOST_ENDIANNESS;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &settings);
 | 
			
		||||
@@ -373,6 +373,10 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option audio_options[] = {
 | 
			
		||||
    { /* end of list */ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops audio_callbacks = {
 | 
			
		||||
    .init_out = line_out_init,
 | 
			
		||||
    .fini_out = line_out_fini,
 | 
			
		||||
@@ -390,6 +394,7 @@ static struct audio_pcm_ops audio_callbacks = {
 | 
			
		||||
static struct audio_driver spice_audio_driver = {
 | 
			
		||||
    .name           = "spice",
 | 
			
		||||
    .descr          = "spice audio driver",
 | 
			
		||||
    .options        = audio_options,
 | 
			
		||||
    .init           = spice_audio_init,
 | 
			
		||||
    .fini           = spice_audio_fini,
 | 
			
		||||
    .pcm_ops        = &audio_callbacks,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
 | 
			
		||||
# alsaaudio.c
 | 
			
		||||
# audio/alsaaudio.c
 | 
			
		||||
alsa_revents(int revents) "revents = %d"
 | 
			
		||||
alsa_pollout(int i, int fd) "i = %d fd = %d"
 | 
			
		||||
alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d"
 | 
			
		||||
@@ -12,11 +12,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
 | 
			
		||||
alsa_resume_in(void) "Resuming suspended input stream"
 | 
			
		||||
alsa_no_frames(int state) "No frames available and ALSA state is %d"
 | 
			
		||||
 | 
			
		||||
# ossaudio.c
 | 
			
		||||
# audio/ossaudio.c
 | 
			
		||||
oss_version(int version) "OSS version = 0x%x"
 | 
			
		||||
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
 | 
			
		||||
 | 
			
		||||
# audio.c
 | 
			
		||||
# audio/audio.c
 | 
			
		||||
audio_timer_start(int interval) "interval %d ms"
 | 
			
		||||
audio_timer_stop(void) ""
 | 
			
		||||
audio_timer_delayed(int interval) "interval %d ms"
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu/host-utils.h"
 | 
			
		||||
#include "qemu/timer.h"
 | 
			
		||||
#include "qapi/opts-visitor.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "wav"
 | 
			
		||||
@@ -38,6 +37,11 @@ typedef struct WAVVoiceOut {
 | 
			
		||||
    int total_samples;
 | 
			
		||||
} WAVVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    struct audsettings settings;
 | 
			
		||||
    const char *wav_path;
 | 
			
		||||
} WAVConf;
 | 
			
		||||
 | 
			
		||||
static int wav_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
{
 | 
			
		||||
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 | 
			
		||||
@@ -108,30 +112,25 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
 | 
			
		||||
        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
    Audiodev *dev = drv_opaque;
 | 
			
		||||
    AudiodevWavOptions *wopts = &dev->u.wav;
 | 
			
		||||
    struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
 | 
			
		||||
    const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
 | 
			
		||||
    WAVConf *conf = drv_opaque;
 | 
			
		||||
    struct audsettings wav_as = conf->settings;
 | 
			
		||||
 | 
			
		||||
    stereo = wav_as.nchannels == 2;
 | 
			
		||||
    switch (wav_as.fmt) {
 | 
			
		||||
    case AUDIO_FORMAT_S8:
 | 
			
		||||
    case AUDIO_FORMAT_U8:
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        bits16 = 0;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S16:
 | 
			
		||||
    case AUDIO_FORMAT_U16:
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        bits16 = 1;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUDIO_FORMAT_S32:
 | 
			
		||||
    case AUDIO_FORMAT_U32:
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        dolog ("WAVE files can not handle 32bit formats\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hdr[34] = bits16 ? 0x10 : 0x08;
 | 
			
		||||
@@ -152,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
 | 
			
		||||
    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 | 
			
		||||
 | 
			
		||||
    wav->f = fopen(wav_path, "wb");
 | 
			
		||||
    wav->f = fopen (conf->wav_path, "wb");
 | 
			
		||||
    if (!wav->f) {
 | 
			
		||||
        dolog ("Failed to open wave file `%s'\nReason: %s\n",
 | 
			
		||||
               wav_path, strerror(errno));
 | 
			
		||||
               conf->wav_path, strerror (errno));
 | 
			
		||||
        g_free (wav->pcm_buf);
 | 
			
		||||
        wav->pcm_buf = NULL;
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -223,17 +222,54 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *wav_audio_init(Audiodev *dev)
 | 
			
		||||
static WAVConf glob_conf = {
 | 
			
		||||
    .settings.freq      = 44100,
 | 
			
		||||
    .settings.nchannels = 2,
 | 
			
		||||
    .settings.fmt       = AUD_FMT_S16,
 | 
			
		||||
    .wav_path           = "qemu.wav"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *wav_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    assert(dev->driver == AUDIODEV_DRIVER_WAV);
 | 
			
		||||
    return dev;
 | 
			
		||||
    WAVConf *conf = g_malloc(sizeof(WAVConf));
 | 
			
		||||
    *conf = glob_conf;
 | 
			
		||||
    return conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wav_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    ldebug ("wav_fini");
 | 
			
		||||
    g_free(opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option wav_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FREQUENCY",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.settings.freq,
 | 
			
		||||
        .descr = "Frequency"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FORMAT",
 | 
			
		||||
        .tag   = AUD_OPT_FMT,
 | 
			
		||||
        .valp  = &glob_conf.settings.fmt,
 | 
			
		||||
        .descr = "Format"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DAC_FIXED_CHANNELS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &glob_conf.settings.nchannels,
 | 
			
		||||
        .descr = "Number of channels (1 - mono, 2 - stereo)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "PATH",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &glob_conf.wav_path,
 | 
			
		||||
        .descr = "Path to wave file"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops wav_pcm_ops = {
 | 
			
		||||
    .init_out = wav_init_out,
 | 
			
		||||
    .fini_out = wav_fini_out,
 | 
			
		||||
@@ -245,6 +281,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
 | 
			
		||||
static struct audio_driver wav_audio_driver = {
 | 
			
		||||
    .name           = "wav",
 | 
			
		||||
    .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
 | 
			
		||||
    .options        = wav_options,
 | 
			
		||||
    .init           = wav_audio_init,
 | 
			
		||||
    .fini           = wav_audio_fini,
 | 
			
		||||
    .pcm_ops        = &wav_pcm_ops,
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
 | 
			
		||||
 | 
			
		||||
    as.freq = freq;
 | 
			
		||||
    as.nchannels = 1 << stereo;
 | 
			
		||||
    as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
 | 
			
		||||
    as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
 | 
			
		||||
    as.endianness = 0;
 | 
			
		||||
 | 
			
		||||
    ops.notify = wav_notify;
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qauthz_list_file_event(int64_t wd G_GNUC_UNUSED,
 | 
			
		||||
qauthz_list_file_event(int wd G_GNUC_UNUSED,
 | 
			
		||||
                       QFileMonitorEvent ev G_GNUC_UNUSED,
 | 
			
		||||
                       const char *name G_GNUC_UNUSED,
 | 
			
		||||
                       void *opaque)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,18 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
 | 
			
		||||
# base.c
 | 
			
		||||
# authz/base.c
 | 
			
		||||
qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
 | 
			
		||||
 | 
			
		||||
# simple.c
 | 
			
		||||
# auth/simple.c
 | 
			
		||||
qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s"
 | 
			
		||||
 | 
			
		||||
# list.c
 | 
			
		||||
# auth/list.c
 | 
			
		||||
qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d"
 | 
			
		||||
qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d"
 | 
			
		||||
 | 
			
		||||
# listfile.c
 | 
			
		||||
# auth/listfile.c
 | 
			
		||||
qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
 | 
			
		||||
qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
 | 
			
		||||
 | 
			
		||||
# pamacct.c
 | 
			
		||||
# auth/pam.c
 | 
			
		||||
qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,10 @@ common-obj-$(CONFIG_POSIX) += hostmem-file.o
 | 
			
		||||
common-obj-y += cryptodev.o
 | 
			
		||||
common-obj-y += cryptodev-builtin.o
 | 
			
		||||
 | 
			
		||||
ifeq ($(CONFIG_VIRTIO_CRYPTO),y)
 | 
			
		||||
ifeq ($(CONFIG_VIRTIO),y)
 | 
			
		||||
common-obj-y += cryptodev-vhost.o
 | 
			
		||||
common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o
 | 
			
		||||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
 | 
			
		||||
    cryptodev-vhost-user.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@
 | 
			
		||||
typedef struct CryptoDevBackendVhostUser {
 | 
			
		||||
    CryptoDevBackend parent_obj;
 | 
			
		||||
 | 
			
		||||
    VhostUserState vhost_user;
 | 
			
		||||
    VhostUserState *vhost_user;
 | 
			
		||||
    CharBackend chr;
 | 
			
		||||
    char *chr_name;
 | 
			
		||||
    bool opened;
 | 
			
		||||
@@ -104,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        options.opaque = &s->vhost_user;
 | 
			
		||||
        options.opaque = s->vhost_user;
 | 
			
		||||
        options.backend_type = VHOST_BACKEND_TYPE_USER;
 | 
			
		||||
        options.cc = b->conf.peers.ccs[i];
 | 
			
		||||
        s->vhost_crypto[i] = cryptodev_vhost_init(&options);
 | 
			
		||||
@@ -182,6 +182,7 @@ static void cryptodev_vhost_user_init(
 | 
			
		||||
    size_t i;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    Chardev *chr;
 | 
			
		||||
    VhostUserState *user;
 | 
			
		||||
    CryptoDevBackendClient *cc;
 | 
			
		||||
    CryptoDevBackendVhostUser *s =
 | 
			
		||||
                      CRYPTODEV_BACKEND_VHOST_USER(backend);
 | 
			
		||||
@@ -212,10 +213,15 @@ static void cryptodev_vhost_user_init(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
 | 
			
		||||
    user = vhost_user_init();
 | 
			
		||||
    if (!user) {
 | 
			
		||||
        error_setg(errp, "Failed to init vhost_user");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    user->chr = &s->chr;
 | 
			
		||||
    s->vhost_user = user;
 | 
			
		||||
 | 
			
		||||
    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
 | 
			
		||||
                     cryptodev_vhost_user_event, NULL, s, NULL, true);
 | 
			
		||||
 | 
			
		||||
@@ -301,7 +307,11 @@ static void cryptodev_vhost_user_cleanup(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vhost_user_cleanup(&s->vhost_user);
 | 
			
		||||
    if (s->vhost_user) {
 | 
			
		||||
        vhost_user_cleanup(s->vhost_user);
 | 
			
		||||
        g_free(s->vhost_user);
 | 
			
		||||
        s->vhost_user = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cryptodev_vhost_user_set_chardev(Object *obj,
 | 
			
		||||
 
 | 
			
		||||
@@ -41,12 +41,10 @@ struct HostMemoryBackendFile {
 | 
			
		||||
static void
 | 
			
		||||
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
#ifndef CONFIG_POSIX
 | 
			
		||||
    error_setg(errp, "backend '%s' not supported on this host",
 | 
			
		||||
               object_get_typename(OBJECT(backend)));
 | 
			
		||||
#else
 | 
			
		||||
    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
 | 
			
		||||
#ifdef CONFIG_POSIX
 | 
			
		||||
    gchar *name;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (!backend->size) {
 | 
			
		||||
        error_setg(errp, "can't create backend with size 0");
 | 
			
		||||
@@ -56,29 +54,9 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
        error_setg(errp, "mem-path property not set");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Verify pmem file size since starting a guest with an incorrect size
 | 
			
		||||
     * leads to confusing failures inside the guest.
 | 
			
		||||
     */
 | 
			
		||||
    if (fb->is_pmem) {
 | 
			
		||||
        Error *local_err = NULL;
 | 
			
		||||
        uint64_t size;
 | 
			
		||||
 | 
			
		||||
        size = qemu_get_pmem_size(fb->mem_path, &local_err);
 | 
			
		||||
        if (!size) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (backend->size > size) {
 | 
			
		||||
            error_setg(errp, "size property %" PRIu64 " is larger than "
 | 
			
		||||
                       "pmem file \"%s\" size %" PRIu64, backend->size,
 | 
			
		||||
                       fb->mem_path, size);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_POSIX
 | 
			
		||||
    error_setg(errp, "-mem-path not supported on this host");
 | 
			
		||||
#else
 | 
			
		||||
    backend->force_prealloc = mem_prealloc;
 | 
			
		||||
    name = host_memory_backend_get_name(backend);
 | 
			
		||||
    memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
 | 
			
		||||
 
 | 
			
		||||
@@ -154,6 +154,7 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
                                              "Huge pages size (ex: 2M, 1G)",
 | 
			
		||||
                                              &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
    if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
 | 
			
		||||
        object_class_property_add_bool(oc, "seal",
 | 
			
		||||
                                       memfd_backend_get_seal,
 | 
			
		||||
                                       memfd_backend_set_seal,
 | 
			
		||||
@@ -162,6 +163,7 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
                                              "Seal growing & shrinking",
 | 
			
		||||
                                              &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo memfd_backend_info = {
 | 
			
		||||
    .name = TYPE_MEMORY_BACKEND_MEMFD,
 | 
			
		||||
@@ -173,7 +175,7 @@ static const TypeInfo memfd_backend_info = {
 | 
			
		||||
 | 
			
		||||
static void register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
 | 
			
		||||
    if (qemu_memfd_check(0)) {
 | 
			
		||||
        type_register_static(&memfd_backend_info);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
 | 
			
		||||
 | 
			
		||||
    value = find_first_bit(backend->host_nodes, MAX_NODES);
 | 
			
		||||
    if (value == MAX_NODES) {
 | 
			
		||||
        goto ret;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *node = g_malloc0(sizeof(**node));
 | 
			
		||||
@@ -106,7 +106,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
 | 
			
		||||
        node = &(*node)->next;
 | 
			
		||||
    } while (true);
 | 
			
		||||
 | 
			
		||||
ret:
 | 
			
		||||
    visit_type_uint16List(v, name, &host_nodes, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										459
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										459
									
								
								block.c
									
									
									
									
									
								
							@@ -426,7 +426,7 @@ BlockDriver *bdrv_find_format(const char *format_name)
 | 
			
		||||
    return bdrv_do_find_format(format_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
 | 
			
		||||
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
 | 
			
		||||
{
 | 
			
		||||
    static const char *whitelist_rw[] = {
 | 
			
		||||
        CONFIG_BDRV_RW_WHITELIST
 | 
			
		||||
@@ -441,13 +441,13 @@ static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (p = whitelist_rw; *p; p++) {
 | 
			
		||||
        if (!strcmp(format_name, *p)) {
 | 
			
		||||
        if (!strcmp(drv->format_name, *p)) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (read_only) {
 | 
			
		||||
        for (p = whitelist_ro; *p; p++) {
 | 
			
		||||
            if (!strcmp(format_name, *p)) {
 | 
			
		||||
            if (!strcmp(drv->format_name, *p)) {
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -455,11 +455,6 @@ static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_format_is_whitelisted(drv->format_name, read_only);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_uses_whitelist(void)
 | 
			
		||||
{
 | 
			
		||||
    return use_bdrv_whitelist;
 | 
			
		||||
@@ -1163,6 +1158,13 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
 | 
			
		||||
     */
 | 
			
		||||
    open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Snapshots should be writable.
 | 
			
		||||
     */
 | 
			
		||||
    if (flags & BDRV_O_TEMPORARY) {
 | 
			
		||||
        open_flags |= BDRV_O_RDWR;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return open_flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1691,7 +1693,6 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
 | 
			
		||||
 | 
			
		||||
typedef struct BlockReopenQueueEntry {
 | 
			
		||||
     bool prepared;
 | 
			
		||||
     bool perms_checked;
 | 
			
		||||
     BDRVReopenState state;
 | 
			
		||||
     QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
 | 
			
		||||
} BlockReopenQueueEntry;
 | 
			
		||||
@@ -2126,8 +2127,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
 | 
			
		||||
    BlockDriverState *old_bs = child->bs;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    assert(!child->frozen);
 | 
			
		||||
 | 
			
		||||
    if (old_bs && new_bs) {
 | 
			
		||||
        assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
 | 
			
		||||
    }
 | 
			
		||||
@@ -2344,10 +2343,6 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
 | 
			
		||||
    bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
 | 
			
		||||
        bdrv_inherits_from_recursive(backing_hd, bs);
 | 
			
		||||
 | 
			
		||||
    if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (backing_hd) {
 | 
			
		||||
        bdrv_ref(backing_hd);
 | 
			
		||||
    }
 | 
			
		||||
@@ -2983,74 +2978,6 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
 | 
			
		||||
                             NULL, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if the NULL-terminated @list contains @str */
 | 
			
		||||
static bool is_str_in_list(const char *str, const char *const *list)
 | 
			
		||||
{
 | 
			
		||||
    if (str && list) {
 | 
			
		||||
        int i;
 | 
			
		||||
        for (i = 0; list[i] != NULL; i++) {
 | 
			
		||||
            if (!strcmp(str, list[i])) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Check that every option set in @bs->options is also set in
 | 
			
		||||
 * @new_opts.
 | 
			
		||||
 *
 | 
			
		||||
 * Options listed in the common_options list and in
 | 
			
		||||
 * @bs->drv->mutable_opts are skipped.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success, otherwise return -EINVAL and set @errp.
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_reset_options_allowed(BlockDriverState *bs,
 | 
			
		||||
                                      const QDict *new_opts, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    const QDictEntry *e;
 | 
			
		||||
    /* These options are common to all block drivers and are handled
 | 
			
		||||
     * in bdrv_reopen_prepare() so they can be left out of @new_opts */
 | 
			
		||||
    const char *const common_options[] = {
 | 
			
		||||
        "node-name", "discard", "cache.direct", "cache.no-flush",
 | 
			
		||||
        "read-only", "auto-read-only", "detect-zeroes", NULL
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
 | 
			
		||||
        if (!qdict_haskey(new_opts, e->key) &&
 | 
			
		||||
            !is_str_in_list(e->key, common_options) &&
 | 
			
		||||
            !is_str_in_list(e->key, bs->drv->mutable_opts)) {
 | 
			
		||||
            error_setg(errp, "Option '%s' cannot be reset "
 | 
			
		||||
                       "to its default value", e->key);
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns true if @child can be reached recursively from @bs
 | 
			
		||||
 */
 | 
			
		||||
static bool bdrv_recurse_has_child(BlockDriverState *bs,
 | 
			
		||||
                                   BlockDriverState *child)
 | 
			
		||||
{
 | 
			
		||||
    BdrvChild *c;
 | 
			
		||||
 | 
			
		||||
    if (bs == child) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(c, &bs->children, next) {
 | 
			
		||||
        if (bdrv_recurse_has_child(c->bs, child)) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Adds a BlockDriverState to a simple queue for an atomic, transactional
 | 
			
		||||
 * reopen of multiple devices.
 | 
			
		||||
@@ -3078,8 +3005,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
                                                 QDict *options,
 | 
			
		||||
                                                 const BdrvChildRole *role,
 | 
			
		||||
                                                 QDict *parent_options,
 | 
			
		||||
                                                 int parent_flags,
 | 
			
		||||
                                                 bool keep_old_opts)
 | 
			
		||||
                                                 int parent_flags)
 | 
			
		||||
{
 | 
			
		||||
    assert(bs != NULL);
 | 
			
		||||
 | 
			
		||||
@@ -3119,13 +3045,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /* Old explicitly set values (don't overwrite by inherited value) */
 | 
			
		||||
    if (bs_entry || keep_old_opts) {
 | 
			
		||||
        old_options = qdict_clone_shallow(bs_entry ?
 | 
			
		||||
                                          bs_entry->state.explicit_options :
 | 
			
		||||
                                          bs->explicit_options);
 | 
			
		||||
    if (bs_entry) {
 | 
			
		||||
        old_options = qdict_clone_shallow(bs_entry->state.explicit_options);
 | 
			
		||||
    } else {
 | 
			
		||||
        old_options = qdict_clone_shallow(bs->explicit_options);
 | 
			
		||||
    }
 | 
			
		||||
    bdrv_join_options(bs, options, old_options);
 | 
			
		||||
    qobject_unref(old_options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    explicit_options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
@@ -3137,12 +3063,10 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
        flags = bdrv_get_flags(bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (keep_old_opts) {
 | 
			
		||||
    /* Old values are used for options that aren't set yet */
 | 
			
		||||
    old_options = qdict_clone_shallow(bs->options);
 | 
			
		||||
    bdrv_join_options(bs, options, old_options);
 | 
			
		||||
    qobject_unref(old_options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We have the final set of options so let's update the flags */
 | 
			
		||||
    options_copy = qdict_clone_shallow(options);
 | 
			
		||||
@@ -3175,21 +3099,9 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
    bs_entry->state.perm = UINT64_MAX;
 | 
			
		||||
    bs_entry->state.shared_perm = 0;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If keep_old_opts is false then it means that unspecified
 | 
			
		||||
     * options must be reset to their original value. We don't allow
 | 
			
		||||
     * resetting 'backing' but we need to know if the option is
 | 
			
		||||
     * missing in order to decide if we have to return an error.
 | 
			
		||||
     */
 | 
			
		||||
    if (!keep_old_opts) {
 | 
			
		||||
        bs_entry->state.backing_missing =
 | 
			
		||||
            !qdict_haskey(options, "backing") &&
 | 
			
		||||
            !qdict_haskey(options, "backing.driver");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(child, &bs->children, next) {
 | 
			
		||||
        QDict *new_child_options = NULL;
 | 
			
		||||
        bool child_keep_old = keep_old_opts;
 | 
			
		||||
        QDict *new_child_options;
 | 
			
		||||
        char *child_key_dot;
 | 
			
		||||
 | 
			
		||||
        /* reopen can only change the options of block devices that were
 | 
			
		||||
         * implicitly created and inherited options. For other (referenced)
 | 
			
		||||
@@ -3198,32 +3110,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Check if the options contain a child reference */
 | 
			
		||||
        if (qdict_haskey(options, child->name)) {
 | 
			
		||||
            const char *childref = qdict_get_try_str(options, child->name);
 | 
			
		||||
            /*
 | 
			
		||||
             * The current child must not be reopened if the child
 | 
			
		||||
             * reference is null or points to a different node.
 | 
			
		||||
             */
 | 
			
		||||
            if (g_strcmp0(childref, child->bs->node_name)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            /*
 | 
			
		||||
             * If the child reference points to the current child then
 | 
			
		||||
             * reopen it with its existing set of options (note that
 | 
			
		||||
             * it can still inherit new options from the parent).
 | 
			
		||||
             */
 | 
			
		||||
            child_keep_old = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Extract child options ("child-name.*") */
 | 
			
		||||
            char *child_key_dot = g_strdup_printf("%s.", child->name);
 | 
			
		||||
        child_key_dot = g_strdup_printf("%s.", child->name);
 | 
			
		||||
        qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
 | 
			
		||||
        qdict_extract_subqdict(options, &new_child_options, child_key_dot);
 | 
			
		||||
        g_free(child_key_dot);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options,
 | 
			
		||||
                                child->role, options, flags, child_keep_old);
 | 
			
		||||
                                child->role, options, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return bs_queue;
 | 
			
		||||
@@ -3231,10 +3124,9 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
 | 
			
		||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
			
		||||
                                    BlockDriverState *bs,
 | 
			
		||||
                                    QDict *options, bool keep_old_opts)
 | 
			
		||||
                                    QDict *options)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0,
 | 
			
		||||
                                   keep_old_opts);
 | 
			
		||||
    return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -3254,44 +3146,23 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
			
		||||
 * All affected nodes must be drained between bdrv_reopen_queue() and
 | 
			
		||||
 * bdrv_reopen_multiple().
 | 
			
		||||
 */
 | 
			
		||||
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
 | 
			
		||||
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    int ret = -1;
 | 
			
		||||
    BlockReopenQueueEntry *bs_entry, *next;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    assert(bs_queue != NULL);
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
 | 
			
		||||
        assert(bs_entry->state.bs->quiesce_counter > 0);
 | 
			
		||||
        if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
 | 
			
		||||
        if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto cleanup;
 | 
			
		||||
        }
 | 
			
		||||
        bs_entry->prepared = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
 | 
			
		||||
        BDRVReopenState *state = &bs_entry->state;
 | 
			
		||||
        ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
 | 
			
		||||
                              state->shared_perm, NULL, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto cleanup_perm;
 | 
			
		||||
        }
 | 
			
		||||
        /* Check if new_backing_bs would accept the new permissions */
 | 
			
		||||
        if (state->replace_backing_bs && state->new_backing_bs) {
 | 
			
		||||
            uint64_t nperm, nshared;
 | 
			
		||||
            bdrv_child_perm(state->bs, state->new_backing_bs,
 | 
			
		||||
                            NULL, &child_backing, bs_queue,
 | 
			
		||||
                            state->perm, state->shared_perm,
 | 
			
		||||
                            &nperm, &nshared);
 | 
			
		||||
            ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
 | 
			
		||||
                                         nperm, nshared, NULL, errp);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                goto cleanup_perm;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        bs_entry->perms_checked = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If we reach this point, we have success and just need to apply the
 | 
			
		||||
     * changes
 | 
			
		||||
     */
 | 
			
		||||
@@ -3300,23 +3171,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
cleanup_perm:
 | 
			
		||||
    QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
 | 
			
		||||
        BDRVReopenState *state = &bs_entry->state;
 | 
			
		||||
 | 
			
		||||
        if (!bs_entry->perms_checked) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ret == 0) {
 | 
			
		||||
            bdrv_set_perm(state->bs, state->perm, state->shared_perm);
 | 
			
		||||
        } else {
 | 
			
		||||
            bdrv_abort_perm_update(state->bs);
 | 
			
		||||
            if (state->replace_backing_bs && state->new_backing_bs) {
 | 
			
		||||
                bdrv_abort_perm_update(state->new_backing_bs);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
cleanup:
 | 
			
		||||
    QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
 | 
			
		||||
        if (ret) {
 | 
			
		||||
@@ -3326,9 +3181,6 @@ cleanup:
 | 
			
		||||
            qobject_unref(bs_entry->state.explicit_options);
 | 
			
		||||
            qobject_unref(bs_entry->state.options);
 | 
			
		||||
        }
 | 
			
		||||
        if (bs_entry->state.new_backing_bs) {
 | 
			
		||||
            bdrv_unref(bs_entry->state.new_backing_bs);
 | 
			
		||||
        }
 | 
			
		||||
        g_free(bs_entry);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(bs_queue);
 | 
			
		||||
@@ -3346,8 +3198,8 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
 | 
			
		||||
    qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
 | 
			
		||||
 | 
			
		||||
    bdrv_subtree_drained_begin(bs);
 | 
			
		||||
    queue = bdrv_reopen_queue(NULL, bs, opts, true);
 | 
			
		||||
    ret = bdrv_reopen_multiple(queue, errp);
 | 
			
		||||
    queue = bdrv_reopen_queue(NULL, bs, opts);
 | 
			
		||||
    ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp);
 | 
			
		||||
    bdrv_subtree_drained_end(bs);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
@@ -3400,101 +3252,6 @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
 | 
			
		||||
    *shared = cumulative_shared_perms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Take a BDRVReopenState and check if the value of 'backing' in the
 | 
			
		||||
 * reopen_state->options QDict is valid or not.
 | 
			
		||||
 *
 | 
			
		||||
 * If 'backing' is missing from the QDict then return 0.
 | 
			
		||||
 *
 | 
			
		||||
 * If 'backing' contains the node name of the backing file of
 | 
			
		||||
 * reopen_state->bs then return 0.
 | 
			
		||||
 *
 | 
			
		||||
 * If 'backing' contains a different node name (or is null) then check
 | 
			
		||||
 * whether the current backing file can be replaced with the new one.
 | 
			
		||||
 * If that's the case then reopen_state->replace_backing_bs is set to
 | 
			
		||||
 * true and reopen_state->new_backing_bs contains a pointer to the new
 | 
			
		||||
 * backing BlockDriverState (or NULL).
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success, otherwise return < 0 and set @errp.
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
 | 
			
		||||
                                     Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs = reopen_state->bs;
 | 
			
		||||
    BlockDriverState *overlay_bs, *new_backing_bs;
 | 
			
		||||
    QObject *value;
 | 
			
		||||
    const char *str;
 | 
			
		||||
 | 
			
		||||
    value = qdict_get(reopen_state->options, "backing");
 | 
			
		||||
    if (value == NULL) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (qobject_type(value)) {
 | 
			
		||||
    case QTYPE_QNULL:
 | 
			
		||||
        new_backing_bs = NULL;
 | 
			
		||||
        break;
 | 
			
		||||
    case QTYPE_QSTRING:
 | 
			
		||||
        str = qobject_get_try_str(value);
 | 
			
		||||
        new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
 | 
			
		||||
        if (new_backing_bs == NULL) {
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        } else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
 | 
			
		||||
            error_setg(errp, "Making '%s' a backing file of '%s' "
 | 
			
		||||
                       "would create a cycle", str, bs->node_name);
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        /* 'backing' does not allow any other data type */
 | 
			
		||||
        g_assert_not_reached();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * TODO: before removing the x- prefix from x-blockdev-reopen we
 | 
			
		||||
     * should move the new backing file into the right AioContext
 | 
			
		||||
     * instead of returning an error.
 | 
			
		||||
     */
 | 
			
		||||
    if (new_backing_bs) {
 | 
			
		||||
        if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
 | 
			
		||||
            error_setg(errp, "Cannot use a new backing file "
 | 
			
		||||
                       "with a different AioContext");
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Find the "actual" backing file by skipping all links that point
 | 
			
		||||
     * to an implicit node, if any (e.g. a commit filter node).
 | 
			
		||||
     */
 | 
			
		||||
    overlay_bs = bs;
 | 
			
		||||
    while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) {
 | 
			
		||||
        overlay_bs = backing_bs(overlay_bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If we want to replace the backing file we need some extra checks */
 | 
			
		||||
    if (new_backing_bs != backing_bs(overlay_bs)) {
 | 
			
		||||
        /* Check for implicit nodes between bs and its backing file */
 | 
			
		||||
        if (bs != overlay_bs) {
 | 
			
		||||
            error_setg(errp, "Cannot change backing link if '%s' has "
 | 
			
		||||
                       "an implicit backing file", bs->node_name);
 | 
			
		||||
            return -EPERM;
 | 
			
		||||
        }
 | 
			
		||||
        /* Check if the backing link that we want to replace is frozen */
 | 
			
		||||
        if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs),
 | 
			
		||||
                                         errp)) {
 | 
			
		||||
            return -EPERM;
 | 
			
		||||
        }
 | 
			
		||||
        reopen_state->replace_backing_bs = true;
 | 
			
		||||
        if (new_backing_bs) {
 | 
			
		||||
            bdrv_ref(new_backing_bs);
 | 
			
		||||
            reopen_state->new_backing_bs = new_backing_bs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Prepares a BlockDriverState for reopen. All changes are staged in the
 | 
			
		||||
 * 'opaque' field of the BDRVReopenState, which is used and allocated by
 | 
			
		||||
@@ -3593,17 +3350,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (drv->bdrv_reopen_prepare) {
 | 
			
		||||
        /*
 | 
			
		||||
         * If a driver-specific option is missing, it means that we
 | 
			
		||||
         * should reset it to its default value.
 | 
			
		||||
         * But not all options allow that, so we need to check it first.
 | 
			
		||||
         */
 | 
			
		||||
        ret = bdrv_reset_options_allowed(reopen_state->bs,
 | 
			
		||||
                                         reopen_state->options, errp);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            if (local_err != NULL) {
 | 
			
		||||
@@ -3627,30 +3373,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
 | 
			
		||||
 | 
			
		||||
    drv_prepared = true;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * We must provide the 'backing' option if the BDS has a backing
 | 
			
		||||
     * file or if the image file has a backing file name as part of
 | 
			
		||||
     * its metadata. Otherwise the 'backing' option can be omitted.
 | 
			
		||||
     */
 | 
			
		||||
    if (drv->supports_backing && reopen_state->backing_missing &&
 | 
			
		||||
        (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) {
 | 
			
		||||
        error_setg(errp, "backing is missing for '%s'",
 | 
			
		||||
                   reopen_state->bs->node_name);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Allow changing the 'backing' option. The new value can be
 | 
			
		||||
     * either a reference to an existing node (using its node name)
 | 
			
		||||
     * or NULL to simply detach the current backing file.
 | 
			
		||||
     */
 | 
			
		||||
    ret = bdrv_reopen_parse_backing(reopen_state, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
    qdict_del(reopen_state->options, "backing");
 | 
			
		||||
 | 
			
		||||
    /* Options that are not handled are only okay if they are unchanged
 | 
			
		||||
     * compared to the old state. It is expected that some options are only
 | 
			
		||||
     * used for the initial open, but not reopen (e.g. filename) */
 | 
			
		||||
@@ -3703,6 +3425,12 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
 | 
			
		||||
        } while ((entry = qdict_next(reopen_state->options, entry)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm,
 | 
			
		||||
                          reopen_state->shared_perm, NULL, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
 | 
			
		||||
    /* Restore the original reopen_state->options QDict */
 | 
			
		||||
@@ -3760,11 +3488,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
 | 
			
		||||
    bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
 | 
			
		||||
    bs->detect_zeroes      = reopen_state->detect_zeroes;
 | 
			
		||||
 | 
			
		||||
    if (reopen_state->replace_backing_bs) {
 | 
			
		||||
        qdict_del(bs->explicit_options, "backing");
 | 
			
		||||
        qdict_del(bs->options, "backing");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Remove child references from bs->options and bs->explicit_options.
 | 
			
		||||
     * Child options were already removed in bdrv_reopen_queue_child() */
 | 
			
		||||
    QLIST_FOREACH(child, &bs->children, next) {
 | 
			
		||||
@@ -3772,23 +3495,11 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
 | 
			
		||||
        qdict_del(bs->options, child->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Change the backing file if a new one was specified. We do this
 | 
			
		||||
     * after updating bs->options, so bdrv_refresh_filename() (called
 | 
			
		||||
     * from bdrv_set_backing_hd()) has the new values.
 | 
			
		||||
     */
 | 
			
		||||
    if (reopen_state->replace_backing_bs) {
 | 
			
		||||
        BlockDriverState *old_backing_bs = backing_bs(bs);
 | 
			
		||||
        assert(!old_backing_bs || !old_backing_bs->implicit);
 | 
			
		||||
        /* Abort the permission update on the backing bs we're detaching */
 | 
			
		||||
        if (old_backing_bs) {
 | 
			
		||||
            bdrv_abort_perm_update(old_backing_bs);
 | 
			
		||||
        }
 | 
			
		||||
        bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_refresh_limits(bs, NULL);
 | 
			
		||||
 | 
			
		||||
    bdrv_set_perm(reopen_state->bs, reopen_state->perm,
 | 
			
		||||
                  reopen_state->shared_perm);
 | 
			
		||||
 | 
			
		||||
    new_can_write =
 | 
			
		||||
        !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
 | 
			
		||||
    if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) {
 | 
			
		||||
@@ -3820,6 +3531,8 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
 | 
			
		||||
    if (drv->bdrv_reopen_abort) {
 | 
			
		||||
        drv->bdrv_reopen_abort(reopen_state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_abort_perm_update(reopen_state->bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -3999,11 +3712,6 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
 | 
			
		||||
        if (!should_update_child(c, to)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (c->frozen) {
 | 
			
		||||
            error_setg(errp, "Cannot change '%s' link to '%s'",
 | 
			
		||||
                       c->name, from->node_name);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        list = g_slist_prepend(list, c);
 | 
			
		||||
        perm |= c->perm;
 | 
			
		||||
        shared &= c->shared_perm;
 | 
			
		||||
@@ -4215,70 +3923,6 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
 | 
			
		||||
    return bdrv_find_overlay(bs, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return true if at least one of the backing links between @bs and
 | 
			
		||||
 * @base is frozen. @errp is set if that's the case.
 | 
			
		||||
 * @base must be reachable from @bs, or NULL.
 | 
			
		||||
 */
 | 
			
		||||
bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
 | 
			
		||||
                                  Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *i;
 | 
			
		||||
 | 
			
		||||
    for (i = bs; i != base; i = backing_bs(i)) {
 | 
			
		||||
        if (i->backing && i->backing->frozen) {
 | 
			
		||||
            error_setg(errp, "Cannot change '%s' link from '%s' to '%s'",
 | 
			
		||||
                       i->backing->name, i->node_name,
 | 
			
		||||
                       backing_bs(i)->node_name);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Freeze all backing links between @bs and @base.
 | 
			
		||||
 * If any of the links is already frozen the operation is aborted and
 | 
			
		||||
 * none of the links are modified.
 | 
			
		||||
 * @base must be reachable from @bs, or NULL.
 | 
			
		||||
 * Returns 0 on success. On failure returns < 0 and sets @errp.
 | 
			
		||||
 */
 | 
			
		||||
int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
 | 
			
		||||
                              Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *i;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_is_backing_chain_frozen(bs, base, errp)) {
 | 
			
		||||
        return -EPERM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = bs; i != base; i = backing_bs(i)) {
 | 
			
		||||
        if (i->backing) {
 | 
			
		||||
            i->backing->frozen = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Unfreeze all backing links between @bs and @base. The caller must
 | 
			
		||||
 * ensure that all links are frozen before using this function.
 | 
			
		||||
 * @base must be reachable from @bs, or NULL.
 | 
			
		||||
 */
 | 
			
		||||
void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *i;
 | 
			
		||||
 | 
			
		||||
    for (i = bs; i != base; i = backing_bs(i)) {
 | 
			
		||||
        if (i->backing) {
 | 
			
		||||
            assert(i->backing->frozen);
 | 
			
		||||
            i->backing->frozen = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Drops images above 'base' up to and including 'top', and sets the image
 | 
			
		||||
 * above 'top' to have base as its backing file.
 | 
			
		||||
@@ -4328,14 +3972,6 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* This function changes all links that point to top and makes
 | 
			
		||||
     * them point to base. Check that none of them is frozen. */
 | 
			
		||||
    QLIST_FOREACH(c, &top->parents, next_parent) {
 | 
			
		||||
        if (c->frozen) {
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If 'base' recursively inherits from 'top' then we should set
 | 
			
		||||
     * base->inherits_from to top->inherits_from after 'top' and all
 | 
			
		||||
     * other intermediate nodes have been dropped.
 | 
			
		||||
@@ -4357,10 +3993,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
 | 
			
		||||
    QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
 | 
			
		||||
        /* Check whether we are allowed to switch c from top to base */
 | 
			
		||||
        GSList *ignore_children = g_slist_prepend(NULL, c);
 | 
			
		||||
        ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
 | 
			
		||||
        bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
 | 
			
		||||
                               ignore_children, &local_err);
 | 
			
		||||
        g_slist_free(ignore_children);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            ret = -EPERM;
 | 
			
		||||
            error_report_err(local_err);
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
@@ -4510,7 +4147,7 @@ static int qsort_strcmp(const void *a, const void *b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
 | 
			
		||||
                         void *opaque, bool read_only)
 | 
			
		||||
                         void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    int count = 0;
 | 
			
		||||
@@ -4521,11 +4158,6 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
 | 
			
		||||
        if (drv->format_name) {
 | 
			
		||||
            bool found = false;
 | 
			
		||||
            int i = count;
 | 
			
		||||
 | 
			
		||||
            if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            while (formats && i && !found) {
 | 
			
		||||
                found = !strcmp(formats[--i], drv->format_name);
 | 
			
		||||
            }
 | 
			
		||||
@@ -4544,11 +4176,6 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
 | 
			
		||||
            bool found = false;
 | 
			
		||||
            int j = count;
 | 
			
		||||
 | 
			
		||||
            if (use_bdrv_whitelist &&
 | 
			
		||||
                !bdrv_format_is_whitelisted(format_name, read_only)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            while (formats && j && !found) {
 | 
			
		||||
                found = !strcmp(formats[--j], format_name);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -401,7 +401,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
            bs->file->bs->supported_zero_flags);
 | 
			
		||||
    ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1764,13 +1764,6 @@ int blk_get_flags(BlockBackend *blk)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the minimum request alignment, in bytes; guaranteed nonzero */
 | 
			
		||||
uint32_t blk_get_request_alignment(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
    return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the maximum transfer length, in bytes; guaranteed nonzero */
 | 
			
		||||
uint32_t blk_get_max_transfer(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,6 @@ typedef struct CommitBlockJob {
 | 
			
		||||
    BlockDriverState *base_bs;
 | 
			
		||||
    BlockdevOnError on_error;
 | 
			
		||||
    bool base_read_only;
 | 
			
		||||
    bool chain_frozen;
 | 
			
		||||
    char *backing_file_str;
 | 
			
		||||
} CommitBlockJob;
 | 
			
		||||
 | 
			
		||||
@@ -69,9 +68,6 @@ static int commit_prepare(Job *job)
 | 
			
		||||
{
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
 | 
			
		||||
 | 
			
		||||
    bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
 | 
			
		||||
    s->chain_frozen = false;
 | 
			
		||||
 | 
			
		||||
    /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
 | 
			
		||||
     * the normal backing chain can be restored. */
 | 
			
		||||
    blk_unref(s->base);
 | 
			
		||||
@@ -88,10 +84,6 @@ static void commit_abort(Job *job)
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
 | 
			
		||||
    BlockDriverState *top_bs = blk_bs(s->top);
 | 
			
		||||
 | 
			
		||||
    if (s->chain_frozen) {
 | 
			
		||||
        bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
 | 
			
		||||
    bdrv_ref(top_bs);
 | 
			
		||||
    bdrv_ref(s->commit_top_bs);
 | 
			
		||||
@@ -338,11 +330,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    s->chain_frozen = true;
 | 
			
		||||
 | 
			
		||||
    ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
@@ -375,9 +362,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    if (s->chain_frozen) {
 | 
			
		||||
        bdrv_unfreeze_backing_chain(commit_top_bs, base);
 | 
			
		||||
    }
 | 
			
		||||
    if (s->base) {
 | 
			
		||||
        blk_unref(s->base);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,11 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
 | 
			
		||||
                                (BDRV_REQ_FUA &
 | 
			
		||||
                                    bs->file->bs->supported_write_flags);
 | 
			
		||||
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
 | 
			
		||||
                               ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
                                    bs->file->bs->supported_zero_flags);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -133,7 +134,7 @@ static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_copy_on_read = {
 | 
			
		||||
BlockDriver bdrv_copy_on_read = {
 | 
			
		||||
    .format_name                        = "copy-on-read",
 | 
			
		||||
 | 
			
		||||
    .bdrv_open                          = cor_open,
 | 
			
		||||
 
 | 
			
		||||
@@ -625,7 +625,7 @@ static const char *const block_crypto_strong_runtime_opts[] = {
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_crypto_luks = {
 | 
			
		||||
BlockDriver bdrv_crypto_luks = {
 | 
			
		||||
    .format_name        = "luks",
 | 
			
		||||
    .instance_size      = sizeof(BlockCrypto),
 | 
			
		||||
    .bdrv_probe         = block_crypto_probe_luks,
 | 
			
		||||
 
 | 
			
		||||
@@ -28,12 +28,29 @@
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/blockjob.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A BdrvDirtyBitmap can be in four possible user-visible states:
 | 
			
		||||
 * (1) Active:   successor is NULL, and disabled is false: full r/w mode
 | 
			
		||||
 * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode,
 | 
			
		||||
 *               guest writes are dropped, but monitor writes are possible,
 | 
			
		||||
 *               through commands like merge and clear.
 | 
			
		||||
 * (3) Frozen:   successor is not NULL.
 | 
			
		||||
 *               A frozen bitmap cannot be renamed, deleted, cleared, set,
 | 
			
		||||
 *               enabled, merged to, etc. A frozen bitmap can only abdicate()
 | 
			
		||||
 *               or reclaim().
 | 
			
		||||
 *               In this state, the anonymous successor bitmap may be either
 | 
			
		||||
 *               Active and recording writes from the guest (e.g. backup jobs),
 | 
			
		||||
 *               but it can be Disabled and not recording writes.
 | 
			
		||||
 * (4) Locked:   Whether Active or Disabled, the user cannot modify this bitmap
 | 
			
		||||
 *               in any way from the monitor.
 | 
			
		||||
 */
 | 
			
		||||
struct BdrvDirtyBitmap {
 | 
			
		||||
    QemuMutex *mutex;
 | 
			
		||||
    HBitmap *bitmap;            /* Dirty bitmap implementation */
 | 
			
		||||
    HBitmap *meta;              /* Meta dirty bitmap */
 | 
			
		||||
    bool busy;                  /* Bitmap is busy, it can't be used via QMP */
 | 
			
		||||
    BdrvDirtyBitmap *successor; /* Anonymous child, if any. */
 | 
			
		||||
    bool qmp_locked;            /* Bitmap is locked, it can't be modified
 | 
			
		||||
                                   through QMP */
 | 
			
		||||
    BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
 | 
			
		||||
    char *name;                 /* Optional non-empty unique ID */
 | 
			
		||||
    int64_t size;               /* Size of the bitmap, in bytes */
 | 
			
		||||
    bool disabled;              /* Bitmap is disabled. It ignores all writes to
 | 
			
		||||
@@ -46,9 +63,6 @@ struct BdrvDirtyBitmap {
 | 
			
		||||
                                   and this bitmap must remain unchanged while
 | 
			
		||||
                                   this flag is set. */
 | 
			
		||||
    bool persistent;            /* bitmap must be saved to owner disk image */
 | 
			
		||||
    bool inconsistent;          /* bitmap is persistent, but inconsistent.
 | 
			
		||||
                                   It cannot be used at all in any way, except
 | 
			
		||||
                                   a QMP user can remove it. */
 | 
			
		||||
    bool migration;             /* Bitmap is selected for migration, it should
 | 
			
		||||
                                   not be stored on the next inactivation
 | 
			
		||||
                                   (persistent flag doesn't matter until next
 | 
			
		||||
@@ -169,58 +183,41 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->successor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->busy;
 | 
			
		||||
/* Both conditions disallow user-modification via QMP. */
 | 
			
		||||
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) {
 | 
			
		||||
    return bdrv_dirty_bitmap_frozen(bitmap) ||
 | 
			
		||||
           bdrv_dirty_bitmap_qmp_locked(bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
 | 
			
		||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
 | 
			
		||||
{
 | 
			
		||||
    qemu_mutex_lock(bitmap->mutex);
 | 
			
		||||
    bitmap->busy = busy;
 | 
			
		||||
    bitmap->qmp_locked = qmp_locked;
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->qmp_locked;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return !bitmap->disabled;
 | 
			
		||||
    return !(bitmap->disabled || bitmap->successor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bdrv_dirty_bitmap_status: This API is now deprecated.
 | 
			
		||||
 * Called with BQL taken.
 | 
			
		||||
 *
 | 
			
		||||
 * A BdrvDirtyBitmap can be in four possible user-visible states:
 | 
			
		||||
 * (1) Active:   successor is NULL, and disabled is false: full r/w mode
 | 
			
		||||
 * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode,
 | 
			
		||||
 *               guest writes are dropped, but monitor writes are possible,
 | 
			
		||||
 *               through commands like merge and clear.
 | 
			
		||||
 * (3) Frozen:   successor is not NULL.
 | 
			
		||||
 *               A frozen bitmap cannot be renamed, deleted, cleared, set,
 | 
			
		||||
 *               enabled, merged to, etc. A frozen bitmap can only abdicate()
 | 
			
		||||
 *               or reclaim().
 | 
			
		||||
 *               In this state, the anonymous successor bitmap may be either
 | 
			
		||||
 *               Active and recording writes from the guest (e.g. backup jobs),
 | 
			
		||||
 *               or it can be Disabled and not recording writes.
 | 
			
		||||
 * (4) Locked:   Whether Active or Disabled, the user cannot modify this bitmap
 | 
			
		||||
 *               in any way from the monitor.
 | 
			
		||||
 * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and
 | 
			
		||||
 *                   is unusable by QEMU. It can be deleted to remove it from
 | 
			
		||||
 *                   the qcow2.
 | 
			
		||||
 */
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    if (bdrv_dirty_bitmap_inconsistent(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_INCONSISTENT;
 | 
			
		||||
    } else if (bdrv_dirty_bitmap_has_successor(bitmap)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_frozen(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_FROZEN;
 | 
			
		||||
    } else if (bdrv_dirty_bitmap_busy(bitmap)) {
 | 
			
		||||
    } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_LOCKED;
 | 
			
		||||
    } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_DISABLED;
 | 
			
		||||
@@ -229,44 +226,9 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return !bitmap->disabled || (bitmap->successor &&
 | 
			
		||||
                                 !bitmap->successor->disabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
 | 
			
		||||
                            Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is currently in use by another"
 | 
			
		||||
                   " operation and cannot be used", bitmap->name);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
 | 
			
		||||
                   bitmap->name);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((flags & BDRV_BITMAP_INCONSISTENT) &&
 | 
			
		||||
        bdrv_dirty_bitmap_inconsistent(bitmap)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used",
 | 
			
		||||
                   bitmap->name);
 | 
			
		||||
        error_append_hint(errp, "Try block-dirty-bitmap-remove to delete"
 | 
			
		||||
                          " this bitmap from disk");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a successor bitmap destined to replace this bitmap after an operation.
 | 
			
		||||
 * Requires that the bitmap is not marked busy and has no successor.
 | 
			
		||||
 * The successor will be enabled if the parent bitmap was.
 | 
			
		||||
 * Requires that the bitmap is not frozen and has no successor.
 | 
			
		||||
 * Called with BQL taken.
 | 
			
		||||
 */
 | 
			
		||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
 | 
			
		||||
@@ -275,14 +237,12 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
 | 
			
		||||
    uint64_t granularity;
 | 
			
		||||
    BdrvDirtyBitmap *child;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (bdrv_dirty_bitmap_has_successor(bitmap)) {
 | 
			
		||||
        error_setg(errp, "Cannot create a successor for a bitmap that already "
 | 
			
		||||
                   "has one");
 | 
			
		||||
    if (bdrv_dirty_bitmap_frozen(bitmap)) {
 | 
			
		||||
        error_setg(errp, "Cannot create a successor for a bitmap that is "
 | 
			
		||||
                   "currently frozen");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    assert(!bitmap->successor);
 | 
			
		||||
 | 
			
		||||
    /* Create an anonymous successor */
 | 
			
		||||
    granularity = bdrv_dirty_bitmap_granularity(bitmap);
 | 
			
		||||
@@ -293,16 +253,15 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    /* Successor will be on or off based on our current state. */
 | 
			
		||||
    child->disabled = bitmap->disabled;
 | 
			
		||||
    bitmap->disabled = true;
 | 
			
		||||
 | 
			
		||||
    /* Install the successor and mark the parent as busy */
 | 
			
		||||
    /* Install the successor and freeze the parent */
 | 
			
		||||
    bitmap->successor = child;
 | 
			
		||||
    bitmap->busy = true;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    bitmap->disabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -319,8 +278,7 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    assert(!bitmap->active_iterators);
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_busy(bitmap));
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_has_successor(bitmap));
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    assert(!bitmap->meta);
 | 
			
		||||
    QLIST_REMOVE(bitmap, list);
 | 
			
		||||
    hbitmap_free(bitmap->bitmap);
 | 
			
		||||
@@ -352,7 +310,6 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
 | 
			
		||||
    bitmap->successor = NULL;
 | 
			
		||||
    successor->persistent = bitmap->persistent;
 | 
			
		||||
    bitmap->persistent = false;
 | 
			
		||||
    bitmap->busy = false;
 | 
			
		||||
    bdrv_release_dirty_bitmap(bs, bitmap);
 | 
			
		||||
 | 
			
		||||
    return successor;
 | 
			
		||||
@@ -361,8 +318,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
 | 
			
		||||
/**
 | 
			
		||||
 * In cases of failure where we can no longer safely delete the parent,
 | 
			
		||||
 * we may wish to re-join the parent and child/successor.
 | 
			
		||||
 * The merged parent will be marked as not busy.
 | 
			
		||||
 * The marged parent will be enabled if and only if the successor was enabled.
 | 
			
		||||
 * The merged parent will be un-frozen, but not explicitly re-enabled.
 | 
			
		||||
 * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
 | 
			
		||||
 */
 | 
			
		||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
 | 
			
		||||
@@ -380,9 +336,6 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
 | 
			
		||||
        error_setg(errp, "Merging of parent and successor bitmap failed");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parent->disabled = successor->disabled;
 | 
			
		||||
    parent->busy = false;
 | 
			
		||||
    bdrv_release_dirty_bitmap_locked(successor);
 | 
			
		||||
    parent->successor = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -413,8 +366,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
 | 
			
		||||
 | 
			
		||||
    bdrv_dirty_bitmaps_lock(bs);
 | 
			
		||||
    QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
 | 
			
		||||
        assert(!bdrv_dirty_bitmap_busy(bitmap));
 | 
			
		||||
        assert(!bdrv_dirty_bitmap_has_successor(bitmap));
 | 
			
		||||
        assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
        assert(!bitmap->active_iterators);
 | 
			
		||||
        hbitmap_truncate(bitmap->bitmap, bytes);
 | 
			
		||||
        bitmap->size = bytes;
 | 
			
		||||
@@ -432,7 +384,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
 | 
			
		||||
 * There must not be any busy bitmaps attached.
 | 
			
		||||
 * There must not be any frozen bitmaps attached.
 | 
			
		||||
 * This function does not remove persistent bitmaps from the storage.
 | 
			
		||||
 * Called with BQL taken.
 | 
			
		||||
 */
 | 
			
		||||
@@ -469,6 +421,7 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
 | 
			
		||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_dirty_bitmap_lock(bitmap);
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    bitmap->disabled = true;
 | 
			
		||||
    bdrv_dirty_bitmap_unlock(bitmap);
 | 
			
		||||
}
 | 
			
		||||
@@ -495,11 +448,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
 | 
			
		||||
        info->has_name = !!bm->name;
 | 
			
		||||
        info->name = g_strdup(bm->name);
 | 
			
		||||
        info->status = bdrv_dirty_bitmap_status(bm);
 | 
			
		||||
        info->recording = bdrv_dirty_bitmap_recording(bm);
 | 
			
		||||
        info->busy = bdrv_dirty_bitmap_busy(bm);
 | 
			
		||||
        info->persistent = bm->persistent;
 | 
			
		||||
        info->has_inconsistent = bm->inconsistent;
 | 
			
		||||
        info->inconsistent = bm->inconsistent;
 | 
			
		||||
        entry->value = info;
 | 
			
		||||
        *plist = entry;
 | 
			
		||||
        plist = &entry->next;
 | 
			
		||||
@@ -582,6 +531,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
 | 
			
		||||
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
 | 
			
		||||
                                  int64_t offset, int64_t bytes)
 | 
			
		||||
{
 | 
			
		||||
    assert(bdrv_dirty_bitmap_enabled(bitmap));
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_readonly(bitmap));
 | 
			
		||||
    hbitmap_set(bitmap->bitmap, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
@@ -598,6 +548,7 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
 | 
			
		||||
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
 | 
			
		||||
                                    int64_t offset, int64_t bytes)
 | 
			
		||||
{
 | 
			
		||||
    assert(bdrv_dirty_bitmap_enabled(bitmap));
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_readonly(bitmap));
 | 
			
		||||
    hbitmap_reset(bitmap->bitmap, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
@@ -740,23 +691,13 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken. */
 | 
			
		||||
void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent)
 | 
			
		||||
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
 | 
			
		||||
{
 | 
			
		||||
    qemu_mutex_lock(bitmap->mutex);
 | 
			
		||||
    bitmap->persistent = persistent;
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken. */
 | 
			
		||||
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    qemu_mutex_lock(bitmap->mutex);
 | 
			
		||||
    assert(bitmap->persistent == true);
 | 
			
		||||
    bitmap->inconsistent = true;
 | 
			
		||||
    bitmap->disabled = true;
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken. */
 | 
			
		||||
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
 | 
			
		||||
{
 | 
			
		||||
@@ -765,16 +706,11 @@ void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->persistent && !bitmap->migration;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->inconsistent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BdrvDirtyBitmap *bm;
 | 
			
		||||
@@ -821,11 +757,15 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(dest->mutex);
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(dest)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is currently in use by another"
 | 
			
		||||
        " operation and cannot be modified", dest->name);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_readonly(dest)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
 | 
			
		||||
                   dest->name);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -144,9 +144,6 @@ typedef struct BDRVRawState {
 | 
			
		||||
    uint64_t locked_perm;
 | 
			
		||||
    uint64_t locked_shared_perm;
 | 
			
		||||
 | 
			
		||||
    int perm_change_fd;
 | 
			
		||||
    BDRVReopenState *reopen_state;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_XFS
 | 
			
		||||
    bool is_xfs:1;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -157,7 +154,6 @@ typedef struct BDRVRawState {
 | 
			
		||||
    bool page_cache_inconsistent:1;
 | 
			
		||||
    bool has_fallocate;
 | 
			
		||||
    bool needs_alignment;
 | 
			
		||||
    bool drop_cache;
 | 
			
		||||
    bool check_cache_dropped;
 | 
			
		||||
 | 
			
		||||
    PRManager *pr_mgr;
 | 
			
		||||
@@ -166,7 +162,6 @@ typedef struct BDRVRawState {
 | 
			
		||||
typedef struct BDRVRawReopenState {
 | 
			
		||||
    int fd;
 | 
			
		||||
    int open_flags;
 | 
			
		||||
    bool drop_cache;
 | 
			
		||||
    bool check_cache_dropped;
 | 
			
		||||
} BDRVRawReopenState;
 | 
			
		||||
 | 
			
		||||
@@ -378,21 +373,13 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers)
 | 
			
		||||
static void raw_parse_flags(int bdrv_flags, int *open_flags)
 | 
			
		||||
{
 | 
			
		||||
    bool read_write = false;
 | 
			
		||||
    assert(open_flags != NULL);
 | 
			
		||||
 | 
			
		||||
    *open_flags |= O_BINARY;
 | 
			
		||||
    *open_flags &= ~O_ACCMODE;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_flags & BDRV_O_AUTO_RDONLY) {
 | 
			
		||||
        read_write = has_writers;
 | 
			
		||||
    } else if (bdrv_flags & BDRV_O_RDWR) {
 | 
			
		||||
        read_write = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (read_write) {
 | 
			
		||||
    if (bdrv_flags & BDRV_O_RDWR) {
 | 
			
		||||
        *open_flags |= O_RDWR;
 | 
			
		||||
    } else {
 | 
			
		||||
        *open_flags |= O_RDONLY;
 | 
			
		||||
@@ -435,13 +422,6 @@ static QemuOptsList raw_runtime_opts = {
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "id of persistent reservation manager object (default: none)",
 | 
			
		||||
        },
 | 
			
		||||
#if defined(__linux__)
 | 
			
		||||
        {
 | 
			
		||||
            .name = "drop-cache",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "invalidate page cache during live migration (default: on)",
 | 
			
		||||
        },
 | 
			
		||||
#endif
 | 
			
		||||
        {
 | 
			
		||||
            .name = "x-check-cache-dropped",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
@@ -451,8 +431,6 @@ static QemuOptsList raw_runtime_opts = {
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL };
 | 
			
		||||
 | 
			
		||||
static int raw_open_common(BlockDriverState *bs, QDict *options,
 | 
			
		||||
                           int bdrv_flags, int open_flags,
 | 
			
		||||
                           bool device, Error **errp)
 | 
			
		||||
@@ -533,17 +511,28 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->drop_cache = qemu_opt_get_bool(opts, "drop-cache", true);
 | 
			
		||||
    s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped",
 | 
			
		||||
                                               false);
 | 
			
		||||
 | 
			
		||||
    s->open_flags = open_flags;
 | 
			
		||||
    raw_parse_flags(bdrv_flags, &s->open_flags, false);
 | 
			
		||||
    raw_parse_flags(bdrv_flags, &s->open_flags);
 | 
			
		||||
 | 
			
		||||
    s->fd = -1;
 | 
			
		||||
    fd = qemu_open(filename, s->open_flags, 0644);
 | 
			
		||||
    ret = fd < 0 ? -errno : 0;
 | 
			
		||||
 | 
			
		||||
    if (ret == -EACCES || ret == -EROFS) {
 | 
			
		||||
        /* Try to degrade to read-only, but if it doesn't work, still use the
 | 
			
		||||
         * normal error message. */
 | 
			
		||||
        if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
 | 
			
		||||
            bdrv_flags &= ~BDRV_O_RDWR;
 | 
			
		||||
            raw_parse_flags(bdrv_flags, &s->open_flags);
 | 
			
		||||
            assert(!(s->open_flags & O_CREAT));
 | 
			
		||||
            fd = qemu_open(filename, s->open_flags);
 | 
			
		||||
            ret = fd < 0 ? -errno : 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Could not open '%s'", filename);
 | 
			
		||||
        if (ret == -EROFS) {
 | 
			
		||||
@@ -652,7 +641,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP;
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) {
 | 
			
		||||
@@ -815,18 +804,6 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    switch (op) {
 | 
			
		||||
    case RAW_PL_PREPARE:
 | 
			
		||||
        if ((s->perm | new_perm) == s->perm &&
 | 
			
		||||
            (s->shared_perm & new_shared) == s->shared_perm)
 | 
			
		||||
        {
 | 
			
		||||
            /*
 | 
			
		||||
             * We are going to unlock bytes, it should not fail. If it fail due
 | 
			
		||||
             * to some fs-dependent permission-unrelated reasons (which occurs
 | 
			
		||||
             * sometimes on NFS and leads to abort in bdrv_replace_child) we
 | 
			
		||||
             * can't prevent such errors by any check here. And we ignore them
 | 
			
		||||
             * anyway in ABORT and COMMIT.
 | 
			
		||||
             */
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm,
 | 
			
		||||
                                   ~s->shared_perm | ~new_shared,
 | 
			
		||||
                                   false, errp);
 | 
			
		||||
@@ -865,77 +842,13 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
 | 
			
		||||
                                 int *open_flags, uint64_t perm, bool force_dup,
 | 
			
		||||
                                 Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    int fd = -1;
 | 
			
		||||
    int ret;
 | 
			
		||||
    bool has_writers = perm &
 | 
			
		||||
        (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE);
 | 
			
		||||
    int fcntl_flags = O_APPEND | O_NONBLOCK;
 | 
			
		||||
#ifdef O_NOATIME
 | 
			
		||||
    fcntl_flags |= O_NOATIME;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    *open_flags = 0;
 | 
			
		||||
    if (s->type == FTYPE_CD) {
 | 
			
		||||
        *open_flags |= O_NONBLOCK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    raw_parse_flags(flags, open_flags, has_writers);
 | 
			
		||||
 | 
			
		||||
#ifdef O_ASYNC
 | 
			
		||||
    /* Not all operating systems have O_ASYNC, and those that don't
 | 
			
		||||
     * will not let us track the state into rs->open_flags (typically
 | 
			
		||||
     * you achieve the same effect with an ioctl, for example I_SETSIG
 | 
			
		||||
     * on Solaris). But we do not use O_ASYNC, so that's fine.
 | 
			
		||||
     */
 | 
			
		||||
    assert((s->open_flags & O_ASYNC) == 0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (!force_dup && *open_flags == s->open_flags) {
 | 
			
		||||
        /* We're lucky, the existing fd is fine */
 | 
			
		||||
        return s->fd;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
 | 
			
		||||
        /* dup the original fd */
 | 
			
		||||
        fd = qemu_dup(s->fd);
 | 
			
		||||
        if (fd >= 0) {
 | 
			
		||||
            ret = fcntl_setfl(fd, *open_flags);
 | 
			
		||||
            if (ret) {
 | 
			
		||||
                qemu_close(fd);
 | 
			
		||||
                fd = -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
 | 
			
		||||
    if (fd == -1) {
 | 
			
		||||
        const char *normalized_filename = bs->filename;
 | 
			
		||||
        ret = raw_normalize_devicepath(&normalized_filename, errp);
 | 
			
		||||
        if (ret >= 0) {
 | 
			
		||||
            assert(!(*open_flags & O_CREAT));
 | 
			
		||||
            fd = qemu_open(normalized_filename, *open_flags);
 | 
			
		||||
            if (fd == -1) {
 | 
			
		||||
                error_setg_errno(errp, errno, "Could not reopen file");
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
                              BlockReopenQueue *queue, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s;
 | 
			
		||||
    BDRVRawReopenState *rs;
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    assert(state != NULL);
 | 
			
		||||
@@ -945,6 +858,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
 | 
			
		||||
    state->opaque = g_new0(BDRVRawReopenState, 1);
 | 
			
		||||
    rs = state->opaque;
 | 
			
		||||
    rs->fd = -1;
 | 
			
		||||
 | 
			
		||||
    /* Handle options changes */
 | 
			
		||||
    opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
 | 
			
		||||
@@ -955,7 +869,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rs->drop_cache = qemu_opt_get_bool_del(opts, "drop-cache", true);
 | 
			
		||||
    rs->check_cache_dropped =
 | 
			
		||||
        qemu_opt_get_bool_del(opts, "x-check-cache-dropped", false);
 | 
			
		||||
 | 
			
		||||
@@ -964,12 +877,50 @@ static int raw_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
     * bdrv_reopen_prepare() will detect changes and complain. */
 | 
			
		||||
    qemu_opts_to_qdict(opts, state->options);
 | 
			
		||||
 | 
			
		||||
    rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
 | 
			
		||||
                                   state->perm, true, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
    if (s->type == FTYPE_CD) {
 | 
			
		||||
        rs->open_flags |= O_NONBLOCK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    raw_parse_flags(state->flags, &rs->open_flags);
 | 
			
		||||
 | 
			
		||||
    int fcntl_flags = O_APPEND | O_NONBLOCK;
 | 
			
		||||
#ifdef O_NOATIME
 | 
			
		||||
    fcntl_flags |= O_NOATIME;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef O_ASYNC
 | 
			
		||||
    /* Not all operating systems have O_ASYNC, and those that don't
 | 
			
		||||
     * will not let us track the state into rs->open_flags (typically
 | 
			
		||||
     * you achieve the same effect with an ioctl, for example I_SETSIG
 | 
			
		||||
     * on Solaris). But we do not use O_ASYNC, so that's fine.
 | 
			
		||||
     */
 | 
			
		||||
    assert((s->open_flags & O_ASYNC) == 0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
 | 
			
		||||
        /* dup the original fd */
 | 
			
		||||
        rs->fd = qemu_dup(s->fd);
 | 
			
		||||
        if (rs->fd >= 0) {
 | 
			
		||||
            ret = fcntl_setfl(rs->fd, rs->open_flags);
 | 
			
		||||
            if (ret) {
 | 
			
		||||
                qemu_close(rs->fd);
 | 
			
		||||
                rs->fd = -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
 | 
			
		||||
    if (rs->fd == -1) {
 | 
			
		||||
        const char *normalized_filename = state->bs->filename;
 | 
			
		||||
        ret = raw_normalize_devicepath(&normalized_filename, errp);
 | 
			
		||||
        if (ret >= 0) {
 | 
			
		||||
            assert(!(rs->open_flags & O_CREAT));
 | 
			
		||||
            rs->fd = qemu_open(normalized_filename, rs->open_flags);
 | 
			
		||||
            if (rs->fd == -1) {
 | 
			
		||||
                error_setg_errno(errp, errno, "Could not reopen file");
 | 
			
		||||
                ret = -1;
 | 
			
		||||
        goto out;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Fail already reopen_prepare() if we can't get a working O_DIRECT
 | 
			
		||||
@@ -977,19 +928,13 @@ static int raw_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
    if (rs->fd != -1) {
 | 
			
		||||
        raw_probe_alignment(state->bs, rs->fd, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            qemu_close(rs->fd);
 | 
			
		||||
            rs->fd = -1;
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto out_fd;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->reopen_state = state;
 | 
			
		||||
    ret = 0;
 | 
			
		||||
out_fd:
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        qemu_close(rs->fd);
 | 
			
		||||
        rs->fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
out:
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    return ret;
 | 
			
		||||
@@ -999,26 +944,29 @@ static void raw_reopen_commit(BDRVReopenState *state)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawReopenState *rs = state->opaque;
 | 
			
		||||
    BDRVRawState *s = state->bs->opaque;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    s->drop_cache = rs->drop_cache;
 | 
			
		||||
    s->check_cache_dropped = rs->check_cache_dropped;
 | 
			
		||||
    s->open_flags = rs->open_flags;
 | 
			
		||||
 | 
			
		||||
    /* Copy locks to the new fd before closing the old one. */
 | 
			
		||||
    raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
 | 
			
		||||
                         s->locked_shared_perm, false, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        /* shouldn't fail in a sane host, but report it just in case. */
 | 
			
		||||
        error_report_err(local_err);
 | 
			
		||||
    }
 | 
			
		||||
    qemu_close(s->fd);
 | 
			
		||||
    s->fd = rs->fd;
 | 
			
		||||
 | 
			
		||||
    g_free(state->opaque);
 | 
			
		||||
    state->opaque = NULL;
 | 
			
		||||
 | 
			
		||||
    assert(s->reopen_state == state);
 | 
			
		||||
    s->reopen_state = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void raw_reopen_abort(BDRVReopenState *state)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawReopenState *rs = state->opaque;
 | 
			
		||||
    BDRVRawState *s = state->bs->opaque;
 | 
			
		||||
 | 
			
		||||
     /* nothing to do if NULL, we didn't get far enough */
 | 
			
		||||
    if (rs == NULL) {
 | 
			
		||||
@@ -1031,9 +979,6 @@ static void raw_reopen_abort(BDRVReopenState *state)
 | 
			
		||||
    }
 | 
			
		||||
    g_free(state->opaque);
 | 
			
		||||
    state->opaque = NULL;
 | 
			
		||||
 | 
			
		||||
    assert(s->reopen_state == state);
 | 
			
		||||
    s->reopen_state = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
 | 
			
		||||
@@ -1512,10 +1457,6 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef BLKZEROOUT
 | 
			
		||||
    /* The BLKZEROOUT implementation in the kernel doesn't set
 | 
			
		||||
     * BLKDEV_ZERO_NOFALLBACK, so we can't call this if we have to avoid slow
 | 
			
		||||
     * fallbacks. */
 | 
			
		||||
    if (!(aiocb->aio_type & QEMU_AIO_NO_FALLBACK)) {
 | 
			
		||||
    do {
 | 
			
		||||
        uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes };
 | 
			
		||||
        if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) {
 | 
			
		||||
@@ -1524,7 +1465,6 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb)
 | 
			
		||||
    } while (errno == EINTR);
 | 
			
		||||
 | 
			
		||||
    ret = translate_err(-errno);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (ret == -ENOTSUP) {
 | 
			
		||||
@@ -2591,10 +2531,6 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!s->drop_cache) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->open_flags & O_DIRECT) {
 | 
			
		||||
        return; /* No host kernel page cache */
 | 
			
		||||
    }
 | 
			
		||||
@@ -2676,9 +2612,6 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
 | 
			
		||||
    if (blkdev) {
 | 
			
		||||
        acb.aio_type |= QEMU_AIO_BLKDEV;
 | 
			
		||||
    }
 | 
			
		||||
    if (flags & BDRV_REQ_NO_FALLBACK) {
 | 
			
		||||
        acb.aio_type |= QEMU_AIO_NO_FALLBACK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (flags & BDRV_REQ_MAY_UNMAP) {
 | 
			
		||||
        acb.aio_type |= QEMU_AIO_DISCARD;
 | 
			
		||||
@@ -2731,78 +2664,12 @@ static QemuOptsList raw_create_opts = {
 | 
			
		||||
static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
 | 
			
		||||
                          Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    BDRVRawReopenState *rs = NULL;
 | 
			
		||||
    int open_flags;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (s->perm_change_fd) {
 | 
			
		||||
        /*
 | 
			
		||||
         * In the context of reopen, this function may be called several times
 | 
			
		||||
         * (directly and recursively while change permissions of the parent).
 | 
			
		||||
         * This is even true for children that don't inherit from the original
 | 
			
		||||
         * reopen node, so s->reopen_state is not set.
 | 
			
		||||
         *
 | 
			
		||||
         * Ignore all but the first call.
 | 
			
		||||
         */
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->reopen_state) {
 | 
			
		||||
        /* We already have a new file descriptor to set permissions for */
 | 
			
		||||
        assert(s->reopen_state->perm == perm);
 | 
			
		||||
        assert(s->reopen_state->shared_perm == shared);
 | 
			
		||||
        rs = s->reopen_state->opaque;
 | 
			
		||||
        s->perm_change_fd = rs->fd;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* We may need a new fd if auto-read-only switches the mode */
 | 
			
		||||
        ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
 | 
			
		||||
                                    false, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        } else if (ret != s->fd) {
 | 
			
		||||
            s->perm_change_fd = ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Prepare permissions on old fd to avoid conflicts between old and new,
 | 
			
		||||
     * but keep everything locked that new will need. */
 | 
			
		||||
    ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Copy locks to the new fd */
 | 
			
		||||
    if (s->perm_change_fd) {
 | 
			
		||||
        ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared,
 | 
			
		||||
                                   false, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    if (s->perm_change_fd && !s->reopen_state) {
 | 
			
		||||
        qemu_close(s->perm_change_fd);
 | 
			
		||||
    }
 | 
			
		||||
    s->perm_change_fd = 0;
 | 
			
		||||
    return ret;
 | 
			
		||||
    return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    /* For reopen, we have already switched to the new fd (.bdrv_set_perm is
 | 
			
		||||
     * called after .bdrv_reopen_commit) */
 | 
			
		||||
    if (s->perm_change_fd && s->fd != s->perm_change_fd) {
 | 
			
		||||
        qemu_close(s->fd);
 | 
			
		||||
        s->fd = s->perm_change_fd;
 | 
			
		||||
    }
 | 
			
		||||
    s->perm_change_fd = 0;
 | 
			
		||||
 | 
			
		||||
    raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
 | 
			
		||||
    s->perm = perm;
 | 
			
		||||
    s->shared_perm = shared;
 | 
			
		||||
@@ -2810,15 +2677,6 @@ static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
 | 
			
		||||
 | 
			
		||||
static void raw_abort_perm_update(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    /* For reopen, .bdrv_reopen_abort is called afterwards and will close
 | 
			
		||||
     * the file descriptor. */
 | 
			
		||||
    if (s->perm_change_fd && !s->reopen_state) {
 | 
			
		||||
        qemu_close(s->perm_change_fd);
 | 
			
		||||
    }
 | 
			
		||||
    s->perm_change_fd = 0;
 | 
			
		||||
 | 
			
		||||
    raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2908,7 +2766,6 @@ BlockDriver bdrv_file = {
 | 
			
		||||
    .bdrv_set_perm   = raw_set_perm,
 | 
			
		||||
    .bdrv_abort_perm_update = raw_abort_perm_update,
 | 
			
		||||
    .create_opts = &raw_create_opts,
 | 
			
		||||
    .mutable_opts = mutable_opts,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/***********************************************/
 | 
			
		||||
@@ -3360,7 +3217,6 @@ static BlockDriver bdrv_host_device = {
 | 
			
		||||
    .bdrv_reopen_abort   = raw_reopen_abort,
 | 
			
		||||
    .bdrv_co_create_opts = hdev_co_create_opts,
 | 
			
		||||
    .create_opts         = &raw_create_opts,
 | 
			
		||||
    .mutable_opts        = mutable_opts,
 | 
			
		||||
    .bdrv_co_invalidate_cache = raw_co_invalidate_cache,
 | 
			
		||||
    .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
 | 
			
		||||
 | 
			
		||||
@@ -3487,7 +3343,6 @@ static BlockDriver bdrv_host_cdrom = {
 | 
			
		||||
    .bdrv_reopen_abort   = raw_reopen_abort,
 | 
			
		||||
    .bdrv_co_create_opts = hdev_co_create_opts,
 | 
			
		||||
    .create_opts         = &raw_create_opts,
 | 
			
		||||
    .mutable_opts        = mutable_opts,
 | 
			
		||||
    .bdrv_co_invalidate_cache = raw_co_invalidate_cache,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -3621,7 +3476,6 @@ static BlockDriver bdrv_host_cdrom = {
 | 
			
		||||
    .bdrv_reopen_abort   = raw_reopen_abort,
 | 
			
		||||
    .bdrv_co_create_opts = hdev_co_create_opts,
 | 
			
		||||
    .create_opts        = &raw_create_opts,
 | 
			
		||||
    .mutable_opts       = mutable_opts,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_preadv         = raw_co_preadv,
 | 
			
		||||
    .bdrv_co_pwritev        = raw_co_pwritev,
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu/units.h"
 | 
			
		||||
#include <glusterfs/api/glfs.h>
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
@@ -21,10 +20,6 @@
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
 | 
			
		||||
# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define GLUSTER_OPT_FILENAME        "filename"
 | 
			
		||||
#define GLUSTER_OPT_VOLUME          "volume"
 | 
			
		||||
#define GLUSTER_OPT_PATH            "path"
 | 
			
		||||
@@ -42,12 +37,6 @@
 | 
			
		||||
#define GLUSTER_DEBUG_MAX           9
 | 
			
		||||
#define GLUSTER_OPT_LOGFILE         "logfile"
 | 
			
		||||
#define GLUSTER_LOGFILE_DEFAULT     "-" /* handled in libgfapi as /dev/stderr */
 | 
			
		||||
/*
 | 
			
		||||
 * Several versions of GlusterFS (3.12? -> 6.0.1) fail when the transfer size
 | 
			
		||||
 * is greater or equal to 1024 MiB, so we are limiting the transfer size to 512
 | 
			
		||||
 * MiB to avoid this rare issue.
 | 
			
		||||
 */
 | 
			
		||||
#define GLUSTER_MAX_TRANSFER        (512 * MiB)
 | 
			
		||||
 | 
			
		||||
#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
 | 
			
		||||
 | 
			
		||||
@@ -736,11 +725,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
 | 
			
		||||
/*
 | 
			
		||||
 * AIO callback routine called from GlusterFS thread.
 | 
			
		||||
 */
 | 
			
		||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
 | 
			
		||||
#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
 | 
			
		||||
                                 struct glfs_stat *pre, struct glfs_stat *post,
 | 
			
		||||
#endif
 | 
			
		||||
                                 void *arg)
 | 
			
		||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    GlusterAIOCB *acb = (GlusterAIOCB *)arg;
 | 
			
		||||
 | 
			
		||||
@@ -894,11 +879,6 @@ out:
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    bs->bl.max_transfer = GLUSTER_MAX_TRANSFER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
                                       BlockReopenQueue *queue, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
@@ -1556,7 +1536,6 @@ static BlockDriver bdrv_gluster = {
 | 
			
		||||
    .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 | 
			
		||||
#endif
 | 
			
		||||
    .bdrv_co_block_status         = qemu_gluster_co_block_status,
 | 
			
		||||
    .bdrv_refresh_limits          = qemu_gluster_refresh_limits,
 | 
			
		||||
    .create_opts                  = &qemu_gluster_create_opts,
 | 
			
		||||
    .strong_runtime_opts          = gluster_strong_open_opts,
 | 
			
		||||
};
 | 
			
		||||
@@ -1587,7 +1566,6 @@ static BlockDriver bdrv_gluster_tcp = {
 | 
			
		||||
    .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 | 
			
		||||
#endif
 | 
			
		||||
    .bdrv_co_block_status         = qemu_gluster_co_block_status,
 | 
			
		||||
    .bdrv_refresh_limits          = qemu_gluster_refresh_limits,
 | 
			
		||||
    .create_opts                  = &qemu_gluster_create_opts,
 | 
			
		||||
    .strong_runtime_opts          = gluster_strong_open_opts,
 | 
			
		||||
};
 | 
			
		||||
@@ -1618,7 +1596,6 @@ static BlockDriver bdrv_gluster_unix = {
 | 
			
		||||
    .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 | 
			
		||||
#endif
 | 
			
		||||
    .bdrv_co_block_status         = qemu_gluster_co_block_status,
 | 
			
		||||
    .bdrv_refresh_limits          = qemu_gluster_refresh_limits,
 | 
			
		||||
    .create_opts                  = &qemu_gluster_create_opts,
 | 
			
		||||
    .strong_runtime_opts          = gluster_strong_open_opts,
 | 
			
		||||
};
 | 
			
		||||
@@ -1655,7 +1632,6 @@ static BlockDriver bdrv_gluster_rdma = {
 | 
			
		||||
    .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 | 
			
		||||
#endif
 | 
			
		||||
    .bdrv_co_block_status         = qemu_gluster_co_block_status,
 | 
			
		||||
    .bdrv_refresh_limits          = qemu_gluster_refresh_limits,
 | 
			
		||||
    .create_opts                  = &qemu_gluster_create_opts,
 | 
			
		||||
    .strong_runtime_opts          = gluster_strong_open_opts,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								block/io.c
									
									
									
									
									
								
							@@ -909,6 +909,8 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
 | 
			
		||||
        }
 | 
			
		||||
        ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_report("error getting block status at offset %" PRId64 ": %s",
 | 
			
		||||
                         offset, strerror(-ret));
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        if (ret & BDRV_BLOCK_ZERO) {
 | 
			
		||||
@@ -917,6 +919,8 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
 | 
			
		||||
        }
 | 
			
		||||
        ret = bdrv_pwrite_zeroes(child, offset, bytes, flags);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_report("error writing zeroes at offset %" PRId64 ": %s",
 | 
			
		||||
                         offset, strerror(-ret));
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        offset += bytes;
 | 
			
		||||
@@ -1015,7 +1019,6 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
 | 
			
		||||
    unsigned int nb_sectors;
 | 
			
		||||
 | 
			
		||||
    assert(!(flags & ~BDRV_REQ_MASK));
 | 
			
		||||
    assert(!(flags & BDRV_REQ_NO_FALLBACK));
 | 
			
		||||
 | 
			
		||||
    if (!drv) {
 | 
			
		||||
        return -ENOMEDIUM;
 | 
			
		||||
@@ -1062,7 +1065,6 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    assert(!(flags & ~BDRV_REQ_MASK));
 | 
			
		||||
    assert(!(flags & BDRV_REQ_NO_FALLBACK));
 | 
			
		||||
 | 
			
		||||
    if (!drv) {
 | 
			
		||||
        return -ENOMEDIUM;
 | 
			
		||||
@@ -1469,10 +1471,6 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
 | 
			
		||||
        return -ENOMEDIUM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((flags & ~bs->supported_zero_flags) & BDRV_REQ_NO_FALLBACK) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(alignment % bs->bl.request_alignment == 0);
 | 
			
		||||
    head = offset % alignment;
 | 
			
		||||
    tail = (offset + bytes) % alignment;
 | 
			
		||||
@@ -1516,7 +1514,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
 | 
			
		||||
            assert(!bs->supported_zero_flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) {
 | 
			
		||||
        if (ret == -ENOTSUP) {
 | 
			
		||||
            /* Fall back to bounce buffer if write zeroes is unsupported */
 | 
			
		||||
            BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE;
 | 
			
		||||
 | 
			
		||||
@@ -2955,10 +2953,6 @@ static int coroutine_fn bdrv_co_copy_range_internal(
 | 
			
		||||
    BdrvTrackedRequest req;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* TODO We can support BDRV_REQ_NO_FALLBACK here */
 | 
			
		||||
    assert(!(read_flags & BDRV_REQ_NO_FALLBACK));
 | 
			
		||||
    assert(!(write_flags & BDRV_REQ_NO_FALLBACK));
 | 
			
		||||
 | 
			
		||||
    if (!dst || !dst->bs) {
 | 
			
		||||
        return -ENOMEDIUM;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -145,8 +145,6 @@ static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768}
 | 
			
		||||
 * unallocated. */
 | 
			
		||||
#define ISCSI_CHECKALLOC_THRES 64
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
iscsi_bh_cb(void *p)
 | 
			
		||||
{
 | 
			
		||||
@@ -174,8 +172,6 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
 | 
			
		||||
    qemu_bh_schedule(acb->bh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void iscsi_co_generic_bh_cb(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    struct IscsiTask *iTask = opaque;
 | 
			
		||||
@@ -294,8 +290,6 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
 | 
			
		||||
/* Called (via iscsi_service) with QemuMutex held. */
 | 
			
		||||
static void
 | 
			
		||||
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
 | 
			
		||||
@@ -344,7 +338,6 @@ static const AIOCBInfo iscsi_aiocb_info = {
 | 
			
		||||
    .cancel_async       = iscsi_aio_cancel,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void iscsi_process_read(void *arg);
 | 
			
		||||
static void iscsi_process_write(void *arg);
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,6 @@ typedef struct MirrorBlockJob {
 | 
			
		||||
    bool initial_zeroing_ongoing;
 | 
			
		||||
    int in_active_write_counter;
 | 
			
		||||
    bool prepared;
 | 
			
		||||
    bool in_drain;
 | 
			
		||||
} MirrorBlockJob;
 | 
			
		||||
 | 
			
		||||
typedef struct MirrorBDSOpaque {
 | 
			
		||||
@@ -631,10 +630,6 @@ static int mirror_exit_common(Job *job)
 | 
			
		||||
    }
 | 
			
		||||
    s->prepared = true;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_chain_contains(src, target_bs)) {
 | 
			
		||||
        bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
 | 
			
		||||
 | 
			
		||||
    /* Make sure that the source BDS doesn't go away during bdrv_replace_node,
 | 
			
		||||
@@ -684,7 +679,6 @@ static int mirror_exit_common(Job *job)
 | 
			
		||||
 | 
			
		||||
        /* The mirror job has no requests in flight any more, but we need to
 | 
			
		||||
         * drain potential other users of the BDS before changing the graph. */
 | 
			
		||||
        assert(s->in_drain);
 | 
			
		||||
        bdrv_drained_begin(target_bs);
 | 
			
		||||
        bdrv_replace_node(to_replace, target_bs, &local_err);
 | 
			
		||||
        bdrv_drained_end(target_bs);
 | 
			
		||||
@@ -723,7 +717,6 @@ static int mirror_exit_common(Job *job)
 | 
			
		||||
    bs_opaque->job = NULL;
 | 
			
		||||
 | 
			
		||||
    bdrv_drained_end(src);
 | 
			
		||||
    s->in_drain = false;
 | 
			
		||||
    bdrv_unref(mirror_top_bs);
 | 
			
		||||
    bdrv_unref(src);
 | 
			
		||||
 | 
			
		||||
@@ -1007,12 +1000,10 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
 | 
			
		||||
             */
 | 
			
		||||
            trace_mirror_before_drain(s, cnt);
 | 
			
		||||
 | 
			
		||||
            s->in_drain = true;
 | 
			
		||||
            bdrv_drained_begin(bs);
 | 
			
		||||
            cnt = bdrv_get_dirty_count(s->dirty_bitmap);
 | 
			
		||||
            if (cnt > 0 || mirror_flush(s) < 0) {
 | 
			
		||||
                bdrv_drained_end(bs);
 | 
			
		||||
                s->in_drain = false;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -1060,7 +1051,6 @@ immediate_exit:
 | 
			
		||||
    bdrv_dirty_iter_free(s->dbi);
 | 
			
		||||
 | 
			
		||||
    if (need_drain) {
 | 
			
		||||
        s->in_drain = true;
 | 
			
		||||
        bdrv_drained_begin(bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1129,16 +1119,6 @@ static void coroutine_fn mirror_pause(Job *job)
 | 
			
		||||
static bool mirror_drained_poll(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
 | 
			
		||||
 | 
			
		||||
    /* If the job isn't paused nor cancelled, we can't be sure that it won't
 | 
			
		||||
     * issue more requests. We make an exception if we've reached this point
 | 
			
		||||
     * from one of our own drain sections, to avoid a deadlock waiting for
 | 
			
		||||
     * ourselves.
 | 
			
		||||
     */
 | 
			
		||||
    if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return !!s->in_flight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1548,8 +1528,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
    }
 | 
			
		||||
    mirror_top_bs->total_sectors = bs->total_sectors;
 | 
			
		||||
    mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
    mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
                                          BDRV_REQ_NO_FALLBACK;
 | 
			
		||||
    mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
    bs_opaque = g_new0(MirrorBDSOpaque, 1);
 | 
			
		||||
    mirror_top_bs->opaque = bs_opaque;
 | 
			
		||||
    bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs));
 | 
			
		||||
@@ -1660,10 +1639,6 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QTAILQ_INIT(&s->ops_in_flight);
 | 
			
		||||
 
 | 
			
		||||
@@ -211,8 +211,7 @@ static inline uint64_t payload_advance64(uint8_t **payload)
 | 
			
		||||
    return ldq_be_p(*payload - 8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_parse_offset_hole_payload(NBDClientSession *client,
 | 
			
		||||
                                         NBDStructuredReplyChunk *chunk,
 | 
			
		||||
static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
 | 
			
		||||
                                         uint8_t *payload, uint64_t orig_offset,
 | 
			
		||||
                                         QEMUIOVector *qiov, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
@@ -234,10 +233,6 @@ static int nbd_parse_offset_hole_payload(NBDClientSession *client,
 | 
			
		||||
                         " region");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    if (client->info.min_block &&
 | 
			
		||||
        !QEMU_IS_ALIGNED(hole_size, client->info.min_block)) {
 | 
			
		||||
        trace_nbd_structured_read_compliance("hole");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size);
 | 
			
		||||
 | 
			
		||||
@@ -245,8 +240,8 @@ static int nbd_parse_offset_hole_payload(NBDClientSession *client,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* nbd_parse_blockstatus_payload
 | 
			
		||||
 * Based on our request, we expect only one extent in reply, for the
 | 
			
		||||
 * base:allocation context.
 | 
			
		||||
 * support only one extent in reply and only for
 | 
			
		||||
 * base:allocation context
 | 
			
		||||
 */
 | 
			
		||||
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
 | 
			
		||||
                                         NBDStructuredReplyChunk *chunk,
 | 
			
		||||
@@ -255,8 +250,7 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
 | 
			
		||||
{
 | 
			
		||||
    uint32_t context_id;
 | 
			
		||||
 | 
			
		||||
    /* The server succeeded, so it must have sent [at least] one extent */
 | 
			
		||||
    if (chunk->length < sizeof(context_id) + sizeof(*extent)) {
 | 
			
		||||
    if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
 | 
			
		||||
        error_setg(errp, "Protocol error: invalid payload for "
 | 
			
		||||
                         "NBD_REPLY_TYPE_BLOCK_STATUS");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
@@ -274,50 +268,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
 | 
			
		||||
    extent->length = payload_advance32(&payload);
 | 
			
		||||
    extent->flags = payload_advance32(&payload);
 | 
			
		||||
 | 
			
		||||
    if (extent->length == 0) {
 | 
			
		||||
    if (extent->length == 0 ||
 | 
			
		||||
        (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
 | 
			
		||||
                                                    client->info.min_block))) {
 | 
			
		||||
        error_setg(errp, "Protocol error: server sent status chunk with "
 | 
			
		||||
                   "zero length");
 | 
			
		||||
                   "invalid length");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * A server sending unaligned block status is in violation of the
 | 
			
		||||
     * protocol, but as qemu-nbd 3.1 is such a server (at least for
 | 
			
		||||
     * POSIX files that are not a multiple of 512 bytes, since qemu
 | 
			
		||||
     * rounds files up to 512-byte multiples but lseek(SEEK_HOLE)
 | 
			
		||||
     * still sees an implicit hole beyond the real EOF), it's nicer to
 | 
			
		||||
     * work around the misbehaving server. If the request included
 | 
			
		||||
     * more than the final unaligned block, truncate it back to an
 | 
			
		||||
     * aligned result; if the request was only the final block, round
 | 
			
		||||
     * up to the full block and change the status to fully-allocated
 | 
			
		||||
     * (always a safe status, even if it loses information).
 | 
			
		||||
     */
 | 
			
		||||
    if (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
 | 
			
		||||
                                                   client->info.min_block)) {
 | 
			
		||||
        trace_nbd_parse_blockstatus_compliance("extent length is unaligned");
 | 
			
		||||
        if (extent->length > client->info.min_block) {
 | 
			
		||||
            extent->length = QEMU_ALIGN_DOWN(extent->length,
 | 
			
		||||
                                             client->info.min_block);
 | 
			
		||||
        } else {
 | 
			
		||||
            extent->length = client->info.min_block;
 | 
			
		||||
            extent->flags = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have
 | 
			
		||||
     * sent us any more than one extent, nor should it have included
 | 
			
		||||
     * status beyond our request in that extent. However, it's easy
 | 
			
		||||
     * enough to ignore the server's noncompliance without killing the
 | 
			
		||||
     * connection; just ignore trailing extents, and clamp things to
 | 
			
		||||
     * the length of our request.
 | 
			
		||||
     */
 | 
			
		||||
    if (chunk->length > sizeof(context_id) + sizeof(*extent)) {
 | 
			
		||||
        trace_nbd_parse_blockstatus_compliance("more than one extent");
 | 
			
		||||
    }
 | 
			
		||||
    /* The server is allowed to send us extra information on the final
 | 
			
		||||
     * extent; just clamp it to the length we requested. */
 | 
			
		||||
    if (extent->length > orig_length) {
 | 
			
		||||
        extent->length = orig_length;
 | 
			
		||||
        trace_nbd_parse_blockstatus_compliance("extent length too large");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -395,9 +357,6 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s,
 | 
			
		||||
                         " region");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) {
 | 
			
		||||
        trace_nbd_structured_read_compliance("data");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_init(&sub_qiov, qiov->niov);
 | 
			
		||||
    qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size);
 | 
			
		||||
@@ -720,7 +679,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
             * in qiov */
 | 
			
		||||
            break;
 | 
			
		||||
        case NBD_REPLY_TYPE_OFFSET_HOLE:
 | 
			
		||||
            ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload,
 | 
			
		||||
            ret = nbd_parse_offset_hole_payload(&reply.structured, payload,
 | 
			
		||||
                                                offset, qiov, &local_err);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                s->quit = true;
 | 
			
		||||
@@ -759,7 +718,9 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
 | 
			
		||||
    bool received = false;
 | 
			
		||||
 | 
			
		||||
    assert(!extent->length);
 | 
			
		||||
    NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, &reply, &payload) {
 | 
			
		||||
    NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
 | 
			
		||||
                            NULL, &reply, &payload)
 | 
			
		||||
    {
 | 
			
		||||
        int ret;
 | 
			
		||||
        NBDStructuredReplyChunk *chunk = &reply.structured;
 | 
			
		||||
 | 
			
		||||
@@ -797,9 +758,12 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
 | 
			
		||||
        payload = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!extent->length && !iter.request_ret) {
 | 
			
		||||
        error_setg(&local_err, "Server did not reply with any status extents");
 | 
			
		||||
        nbd_iter_channel_error(&iter, -EIO, &local_err);
 | 
			
		||||
    if (!extent->length && !iter.err) {
 | 
			
		||||
        error_setg(&iter.err,
 | 
			
		||||
                   "Server did not reply with any status extents");
 | 
			
		||||
        if (!iter.ret) {
 | 
			
		||||
            iter.ret = -EIO;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error_propagate(errp, iter.err);
 | 
			
		||||
@@ -856,25 +820,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    if (!bytes) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    /*
 | 
			
		||||
     * Work around the fact that the block layer doesn't do
 | 
			
		||||
     * byte-accurate sizing yet - if the read exceeds the server's
 | 
			
		||||
     * advertised size because the block layer rounded size up, then
 | 
			
		||||
     * truncate the request to the server and tail-pad with zero.
 | 
			
		||||
     */
 | 
			
		||||
    if (offset >= client->info.size) {
 | 
			
		||||
        assert(bytes < BDRV_SECTOR_SIZE);
 | 
			
		||||
        qemu_iovec_memset(qiov, 0, 0, bytes);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (offset + bytes > client->info.size) {
 | 
			
		||||
        uint64_t slop = offset + bytes - client->info.size;
 | 
			
		||||
 | 
			
		||||
        assert(slop < BDRV_SECTOR_SIZE);
 | 
			
		||||
        qemu_iovec_memset(qiov, bytes - slop, 0, slop);
 | 
			
		||||
        request.len -= slop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = nbd_co_send_request(bs, &request, NULL);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
@@ -993,35 +938,15 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
 | 
			
		||||
        .from = offset,
 | 
			
		||||
        .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
 | 
			
		||||
                                                bs->bl.request_alignment),
 | 
			
		||||
                                client->info.max_block),
 | 
			
		||||
                   MIN(bytes, client->info.size - offset)),
 | 
			
		||||
                                client->info.max_block), bytes),
 | 
			
		||||
        .flags = NBD_CMD_FLAG_REQ_ONE,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!client->info.base_allocation) {
 | 
			
		||||
        *pnum = bytes;
 | 
			
		||||
        *map = offset;
 | 
			
		||||
        *file = bs;
 | 
			
		||||
        return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
 | 
			
		||||
        return BDRV_BLOCK_DATA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Work around the fact that the block layer doesn't do
 | 
			
		||||
     * byte-accurate sizing yet - if the status request exceeds the
 | 
			
		||||
     * server's advertised size because the block layer rounded size
 | 
			
		||||
     * up, we truncated the request to the server (above), or are
 | 
			
		||||
     * called on just the hole.
 | 
			
		||||
     */
 | 
			
		||||
    if (offset >= client->info.size) {
 | 
			
		||||
        *pnum = bytes;
 | 
			
		||||
        assert(bytes < BDRV_SECTOR_SIZE);
 | 
			
		||||
        /* Intentionally don't report offset_valid for the hole */
 | 
			
		||||
        return BDRV_BLOCK_ZERO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (client->info.min_block) {
 | 
			
		||||
        assert(QEMU_IS_ALIGNED(request.len, client->info.min_block));
 | 
			
		||||
    }
 | 
			
		||||
    ret = nbd_co_send_request(bs, &request, NULL);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
@@ -1042,11 +967,8 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    assert(extent.length);
 | 
			
		||||
    *pnum = extent.length;
 | 
			
		||||
    *map = offset;
 | 
			
		||||
    *file = bs;
 | 
			
		||||
    return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
 | 
			
		||||
        (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0) |
 | 
			
		||||
        BDRV_BLOCK_OFFSET_VALID;
 | 
			
		||||
           (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								block/nbd.c
									
									
									
									
									
								
							@@ -437,24 +437,7 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    uint32_t min = s->info.min_block;
 | 
			
		||||
    uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If the server did not advertise an alignment:
 | 
			
		||||
     * - a size that is not sector-aligned implies that an alignment
 | 
			
		||||
     *   of 1 can be used to access those tail bytes
 | 
			
		||||
     * - advertisement of block status requires an alignment of 1, so
 | 
			
		||||
     *   that we don't violate block layer constraints that block
 | 
			
		||||
     *   status is always aligned (as we can't control whether the
 | 
			
		||||
     *   server will report sub-sector extents, such as a hole at EOF
 | 
			
		||||
     *   on an unaligned POSIX file)
 | 
			
		||||
     * - otherwise, assume the server is so old that we are safer avoiding
 | 
			
		||||
     *   sub-sector requests
 | 
			
		||||
     */
 | 
			
		||||
    if (!min) {
 | 
			
		||||
        min = (!QEMU_IS_ALIGNED(s->info.size, BDRV_SECTOR_SIZE) ||
 | 
			
		||||
               s->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->bl.request_alignment = min;
 | 
			
		||||
    bs->bl.request_alignment = min ? min : BDRV_SECTOR_SIZE;
 | 
			
		||||
    bs->bl.max_pdiscard = max;
 | 
			
		||||
    bs->bl.max_pwrite_zeroes = max;
 | 
			
		||||
    bs->bl.max_transfer = max;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								block/qapi.c
									
									
									
									
									
								
							@@ -493,14 +493,14 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
 | 
			
		||||
                                 &ds->has_rd_latency_histogram,
 | 
			
		||||
                                 &ds->rd_latency_histogram);
 | 
			
		||||
                                 &ds->has_x_rd_latency_histogram,
 | 
			
		||||
                                 &ds->x_rd_latency_histogram);
 | 
			
		||||
    bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
 | 
			
		||||
                                 &ds->has_wr_latency_histogram,
 | 
			
		||||
                                 &ds->wr_latency_histogram);
 | 
			
		||||
                                 &ds->has_x_wr_latency_histogram,
 | 
			
		||||
                                 &ds->x_wr_latency_histogram);
 | 
			
		||||
    bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
 | 
			
		||||
                                 &ds->has_flush_latency_histogram,
 | 
			
		||||
                                 &ds->flush_latency_histogram);
 | 
			
		||||
                                 &ds->has_x_flush_latency_histogram,
 | 
			
		||||
                                 &ds->x_flush_latency_histogram);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
 | 
			
		||||
 
 | 
			
		||||
@@ -343,15 +343,9 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
 | 
			
		||||
    uint32_t granularity;
 | 
			
		||||
    BdrvDirtyBitmap *bitmap = NULL;
 | 
			
		||||
 | 
			
		||||
    granularity = 1U << bm->granularity_bits;
 | 
			
		||||
    bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp);
 | 
			
		||||
    if (bitmap == NULL) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bm->flags & BME_FLAG_IN_USE) {
 | 
			
		||||
        /* Data is unusable, skip loading it */
 | 
			
		||||
        return bitmap;
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is in use", bm->name);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bitmap_table_load(bs, &bm->table, &bitmap_table);
 | 
			
		||||
@@ -362,6 +356,12 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    granularity = 1U << bm->granularity_bits;
 | 
			
		||||
    bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp);
 | 
			
		||||
    if (bitmap == NULL) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image",
 | 
			
		||||
@@ -462,25 +462,10 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
 | 
			
		||||
        return len;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
 | 
			
		||||
           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
 | 
			
		||||
 | 
			
		||||
    if (!(entry->flags & BME_FLAG_IN_USE) &&
 | 
			
		||||
        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits)))
 | 
			
		||||
    {
 | 
			
		||||
        /*
 | 
			
		||||
         * We've loaded a valid bitmap (IN_USE not set) or we are going to
 | 
			
		||||
         * store a valid bitmap, but the allocated bitmap table size is not
 | 
			
		||||
         * enough to store this bitmap.
 | 
			
		||||
         *
 | 
			
		||||
         * Note, that it's OK to have an invalid bitmap with invalid size due
 | 
			
		||||
         * to a bitmap that was not correctly saved after image resize.
 | 
			
		||||
         */
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
    return fail ? -EINVAL : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void bitmap_directory_to_be(uint8_t *dir, size_t size)
 | 
			
		||||
@@ -793,8 +778,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
 | 
			
		||||
     * directory in-place (actually, turn-off the extension), which is checked
 | 
			
		||||
     * in qcow2_check_metadata_overlap() */
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(
 | 
			
		||||
            bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size,
 | 
			
		||||
            false);
 | 
			
		||||
            bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -965,7 +949,6 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    Qcow2Bitmap *bm;
 | 
			
		||||
    GSList *created_dirty_bitmaps = NULL;
 | 
			
		||||
    bool header_updated = false;
 | 
			
		||||
    bool needs_update = false;
 | 
			
		||||
 | 
			
		||||
    if (s->nb_bitmaps == 0) {
 | 
			
		||||
        /* No bitmaps - nothing to do */
 | 
			
		||||
@@ -979,27 +962,24 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
 | 
			
		||||
        if (!(bm->flags & BME_FLAG_IN_USE)) {
 | 
			
		||||
            BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
 | 
			
		||||
            if (bitmap == NULL) {
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        bdrv_dirty_bitmap_set_persistence(bitmap, true);
 | 
			
		||||
        if (bm->flags & BME_FLAG_IN_USE) {
 | 
			
		||||
            bdrv_dirty_bitmap_set_inconsistent(bitmap);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* NB: updated flags only get written if can_write(bs) is true. */
 | 
			
		||||
            bm->flags |= BME_FLAG_IN_USE;
 | 
			
		||||
            needs_update = true;
 | 
			
		||||
        }
 | 
			
		||||
            if (!(bm->flags & BME_FLAG_AUTO)) {
 | 
			
		||||
                bdrv_disable_dirty_bitmap(bitmap);
 | 
			
		||||
            }
 | 
			
		||||
            bdrv_dirty_bitmap_set_persistance(bitmap, true);
 | 
			
		||||
            bm->flags |= BME_FLAG_IN_USE;
 | 
			
		||||
            created_dirty_bitmaps =
 | 
			
		||||
                    g_slist_append(created_dirty_bitmaps, bitmap);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (needs_update && can_write(bs)) {
 | 
			
		||||
    if (created_dirty_bitmaps != NULL) {
 | 
			
		||||
        if (can_write(bs)) {
 | 
			
		||||
            /* in_use flags must be updated */
 | 
			
		||||
            int ret = update_ext_header_and_dir_in_place(bs, bm_list);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
@@ -1007,12 +987,11 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
            header_updated = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!can_write(bs)) {
 | 
			
		||||
        } else {
 | 
			
		||||
            g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
 | 
			
		||||
                            (gpointer)true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_slist_free(created_dirty_bitmaps);
 | 
			
		||||
    bitmap_list_free(bm_list);
 | 
			
		||||
@@ -1133,15 +1112,16 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
 | 
			
		||||
        if (!(bm->flags & BME_FLAG_IN_USE)) {
 | 
			
		||||
            BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
 | 
			
		||||
            if (bitmap == NULL) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!bdrv_dirty_bitmap_readonly(bitmap)) {
 | 
			
		||||
            error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was "
 | 
			
		||||
                       "not marked as readonly. This is a bug, something went "
 | 
			
		||||
                       "wrong. All of the bitmaps may be corrupted", bm->name);
 | 
			
		||||
                error_setg(errp, "Bitmap %s is not readonly but not marked"
 | 
			
		||||
                                 "'IN_USE' in the image. Something went wrong,"
 | 
			
		||||
                                 "all the bitmaps may be corrupted", bm->name);
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                goto out;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1149,6 +1129,7 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
            bm->flags |= BME_FLAG_IN_USE;
 | 
			
		||||
            ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ro_dirty_bitmaps != NULL) {
 | 
			
		||||
        /* in_use flags must be updated */
 | 
			
		||||
@@ -1175,52 +1156,6 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Checks to see if it's safe to resize bitmaps */
 | 
			
		||||
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    Qcow2BitmapList *bm_list;
 | 
			
		||||
    Qcow2Bitmap *bm;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    if (s->nb_bitmaps == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
 | 
			
		||||
                               s->bitmap_directory_size, errp);
 | 
			
		||||
    if (bm_list == NULL) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
 | 
			
		||||
        BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
 | 
			
		||||
        if (bitmap == NULL) {
 | 
			
		||||
            /*
 | 
			
		||||
             * We rely on all bitmaps being in-memory to be able to resize them,
 | 
			
		||||
             * Otherwise, we'd need to resize them on disk explicitly
 | 
			
		||||
             */
 | 
			
		||||
            error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that "
 | 
			
		||||
                       "were not loaded into memory");
 | 
			
		||||
            ret = -ENOTSUP;
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * The checks against readonly and busy are redundant, but certainly
 | 
			
		||||
         * do no harm. checks against inconsistent are crucial:
 | 
			
		||||
         */
 | 
			
		||||
        if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
 | 
			
		||||
            ret = -ENOTSUP;
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    bitmap_list_free(bm_list);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* store_bitmap_data()
 | 
			
		||||
 * Store bitmap to image, filling bitmap table accordingly.
 | 
			
		||||
 */
 | 
			
		||||
@@ -1289,7 +1224,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
 | 
			
		||||
            memset(buf + write_size, 0, s->cluster_size - write_size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false);
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
 | 
			
		||||
            goto fail;
 | 
			
		||||
@@ -1357,7 +1292,7 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset,
 | 
			
		||||
                                        tb_size * sizeof(tb[0]), false);
 | 
			
		||||
                                        tb_size * sizeof(tb[0]));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
 | 
			
		||||
        goto fail;
 | 
			
		||||
@@ -1488,9 +1423,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 | 
			
		||||
        uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
 | 
			
		||||
        Qcow2Bitmap *bm;
 | 
			
		||||
 | 
			
		||||
        if (!bdrv_dirty_bitmap_get_persistence(bitmap) ||
 | 
			
		||||
            bdrv_dirty_bitmap_readonly(bitmap) ||
 | 
			
		||||
            bdrv_dirty_bitmap_inconsistent(bitmap)) {
 | 
			
		||||
        if (!bdrv_dirty_bitmap_get_persistance(bitmap) ||
 | 
			
		||||
            bdrv_dirty_bitmap_readonly(bitmap))
 | 
			
		||||
        {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1606,7 +1541,7 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
 | 
			
		||||
         bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
 | 
			
		||||
    {
 | 
			
		||||
        if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
 | 
			
		||||
        if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
 | 
			
		||||
            bdrv_dirty_bitmap_set_readonly(bitmap, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -205,13 +205,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
 | 
			
		||||
 | 
			
		||||
    if (c == s->refcount_block_cache) {
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
 | 
			
		||||
                c->entries[i].offset, c->table_size, false);
 | 
			
		||||
                c->entries[i].offset, c->table_size);
 | 
			
		||||
    } else if (c == s->l2_table_cache) {
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
 | 
			
		||||
                c->entries[i].offset, c->table_size, false);
 | 
			
		||||
                c->entries[i].offset, c->table_size);
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0,
 | 
			
		||||
                c->entries[i].offset, c->table_size, false);
 | 
			
		||||
                c->entries[i].offset, c->table_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -153,7 +153,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
 | 
			
		||||
    /* the L1 position has not yet been updated, so these clusters must
 | 
			
		||||
     * indeed be completely free */
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset,
 | 
			
		||||
                                        new_l1_size2, false);
 | 
			
		||||
                                        new_l1_size2);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -238,7 +238,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
 | 
			
		||||
            s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false);
 | 
			
		||||
            s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
@@ -380,8 +380,8 @@ fail:
 | 
			
		||||
 * as contiguous. (This allows it, for example, to stop at the first compressed
 | 
			
		||||
 * cluster which may require a different handling)
 | 
			
		||||
 */
 | 
			
		||||
static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters,
 | 
			
		||||
        int cluster_size, uint64_t *l2_slice, uint64_t stop_flags)
 | 
			
		||||
static int count_contiguous_clusters(int nb_clusters, int cluster_size,
 | 
			
		||||
        uint64_t *l2_slice, uint64_t stop_flags)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    QCow2ClusterType first_cluster_type;
 | 
			
		||||
@@ -389,12 +389,12 @@ static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters,
 | 
			
		||||
    uint64_t first_entry = be64_to_cpu(l2_slice[0]);
 | 
			
		||||
    uint64_t offset = first_entry & mask;
 | 
			
		||||
 | 
			
		||||
    first_cluster_type = qcow2_get_cluster_type(bs, first_entry);
 | 
			
		||||
    if (first_cluster_type == QCOW2_CLUSTER_UNALLOCATED) {
 | 
			
		||||
    if (!offset) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* must be allocated */
 | 
			
		||||
    first_cluster_type = qcow2_get_cluster_type(first_entry);
 | 
			
		||||
    assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
 | 
			
		||||
           first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
 | 
			
		||||
 | 
			
		||||
@@ -412,8 +412,7 @@ static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters,
 | 
			
		||||
 * Checks how many consecutive unallocated clusters in a given L2
 | 
			
		||||
 * slice have the same cluster type.
 | 
			
		||||
 */
 | 
			
		||||
static int count_contiguous_clusters_unallocated(BlockDriverState *bs,
 | 
			
		||||
                                                 int nb_clusters,
 | 
			
		||||
static int count_contiguous_clusters_unallocated(int nb_clusters,
 | 
			
		||||
                                                 uint64_t *l2_slice,
 | 
			
		||||
                                                 QCow2ClusterType wanted_type)
 | 
			
		||||
{
 | 
			
		||||
@@ -423,7 +422,7 @@ static int count_contiguous_clusters_unallocated(BlockDriverState *bs,
 | 
			
		||||
           wanted_type == QCOW2_CLUSTER_UNALLOCATED);
 | 
			
		||||
    for (i = 0; i < nb_clusters; i++) {
 | 
			
		||||
        uint64_t entry = be64_to_cpu(l2_slice[i]);
 | 
			
		||||
        QCow2ClusterType type = qcow2_get_cluster_type(bs, entry);
 | 
			
		||||
        QCow2ClusterType type = qcow2_get_cluster_type(entry);
 | 
			
		||||
 | 
			
		||||
        if (type != wanted_type) {
 | 
			
		||||
            break;
 | 
			
		||||
@@ -490,7 +489,6 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs,
 | 
			
		||||
                                             unsigned offset_in_cluster,
 | 
			
		||||
                                             QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (qiov->size == 0) {
 | 
			
		||||
@@ -498,13 +496,13 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0,
 | 
			
		||||
            cluster_offset + offset_in_cluster, qiov->size, true);
 | 
			
		||||
            cluster_offset + offset_in_cluster, qiov->size);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
 | 
			
		||||
    ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster,
 | 
			
		||||
    ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
 | 
			
		||||
                          qiov->size, qiov, 0);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
@@ -597,7 +595,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
     * true */
 | 
			
		||||
    assert(nb_clusters <= INT_MAX);
 | 
			
		||||
 | 
			
		||||
    type = qcow2_get_cluster_type(bs, *cluster_offset);
 | 
			
		||||
    type = qcow2_get_cluster_type(*cluster_offset);
 | 
			
		||||
    if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
 | 
			
		||||
                                type == QCOW2_CLUSTER_ZERO_ALLOC)) {
 | 
			
		||||
        qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
 | 
			
		||||
@@ -608,14 +606,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    }
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case QCOW2_CLUSTER_COMPRESSED:
 | 
			
		||||
        if (has_data_file(bs)) {
 | 
			
		||||
            qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster "
 | 
			
		||||
                                    "entry found in image with external data "
 | 
			
		||||
                                    "file (L2 offset: %#" PRIx64 ", L2 index: "
 | 
			
		||||
                                    "%#x)", l2_offset, l2_index);
 | 
			
		||||
            ret = -EIO;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        /* Compressed clusters can only be processed one by one */
 | 
			
		||||
        c = 1;
 | 
			
		||||
        *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
 | 
			
		||||
@@ -623,14 +613,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    case QCOW2_CLUSTER_ZERO_PLAIN:
 | 
			
		||||
    case QCOW2_CLUSTER_UNALLOCATED:
 | 
			
		||||
        /* how many empty clusters ? */
 | 
			
		||||
        c = count_contiguous_clusters_unallocated(bs, nb_clusters,
 | 
			
		||||
        c = count_contiguous_clusters_unallocated(nb_clusters,
 | 
			
		||||
                                                  &l2_slice[l2_index], type);
 | 
			
		||||
        *cluster_offset = 0;
 | 
			
		||||
        break;
 | 
			
		||||
    case QCOW2_CLUSTER_ZERO_ALLOC:
 | 
			
		||||
    case QCOW2_CLUSTER_NORMAL:
 | 
			
		||||
        /* how many allocated clusters ? */
 | 
			
		||||
        c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
 | 
			
		||||
        c = count_contiguous_clusters(nb_clusters, s->cluster_size,
 | 
			
		||||
                                      &l2_slice[l2_index], QCOW_OFLAG_ZERO);
 | 
			
		||||
        *cluster_offset &= L2E_OFFSET_MASK;
 | 
			
		||||
        if (offset_into_cluster(s, *cluster_offset)) {
 | 
			
		||||
@@ -642,17 +632,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
            ret = -EIO;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        if (has_data_file(bs) && *cluster_offset != offset - offset_in_cluster)
 | 
			
		||||
        {
 | 
			
		||||
            qcow2_signal_corruption(bs, true, -1, -1,
 | 
			
		||||
                                    "External data file host cluster offset %#"
 | 
			
		||||
                                    PRIx64 " does not match guest cluster "
 | 
			
		||||
                                    "offset: %#" PRIx64
 | 
			
		||||
                                    ", L2 index: %#x)", *cluster_offset,
 | 
			
		||||
                                    offset - offset_in_cluster, l2_index);
 | 
			
		||||
            ret = -EIO;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        abort();
 | 
			
		||||
@@ -756,16 +735,19 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
/*
 | 
			
		||||
 * alloc_compressed_cluster_offset
 | 
			
		||||
 *
 | 
			
		||||
 * For a given offset on the virtual disk, allocate a new compressed cluster
 | 
			
		||||
 * and put the host offset of the cluster into *host_offset. If a cluster is
 | 
			
		||||
 * already allocated at the offset, return an error.
 | 
			
		||||
 * For a given offset of the disk image, return cluster offset in
 | 
			
		||||
 * qcow2 file.
 | 
			
		||||
 *
 | 
			
		||||
 * If the offset is not found, allocate a new compressed cluster.
 | 
			
		||||
 *
 | 
			
		||||
 * Return the cluster offset if successful,
 | 
			
		||||
 * Return 0, otherwise.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success and -errno in error cases
 | 
			
		||||
 */
 | 
			
		||||
int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                                               uint64_t offset,
 | 
			
		||||
                                          int compressed_size,
 | 
			
		||||
                                          uint64_t *host_offset)
 | 
			
		||||
                                               int compressed_size)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int l2_index, ret;
 | 
			
		||||
@@ -773,13 +755,9 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
    int64_t cluster_offset;
 | 
			
		||||
    int nb_csectors;
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = get_cluster_table(bs, offset, &l2_slice, &l2_index);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Compression can't overwrite anything. Fail if the cluster was already
 | 
			
		||||
@@ -787,13 +765,13 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
    cluster_offset = be64_to_cpu(l2_slice[l2_index]);
 | 
			
		||||
    if (cluster_offset & L2E_OFFSET_MASK) {
 | 
			
		||||
        qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
 | 
			
		||||
        return -EIO;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
 | 
			
		||||
    if (cluster_offset < 0) {
 | 
			
		||||
        qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
 | 
			
		||||
        return cluster_offset;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) -
 | 
			
		||||
@@ -811,8 +789,7 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
    l2_slice[l2_index] = cpu_to_be64(cluster_offset);
 | 
			
		||||
    qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
 | 
			
		||||
 | 
			
		||||
    *host_offset = cluster_offset & s->cluster_offset_mask;
 | 
			
		||||
    return 0;
 | 
			
		||||
    return cluster_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
 | 
			
		||||
@@ -1036,14 +1013,14 @@ void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
 | 
			
		||||
 * write, but require COW to be performed (this includes yet unallocated space,
 | 
			
		||||
 * which must copy from the backing file)
 | 
			
		||||
 */
 | 
			
		||||
static int count_cow_clusters(BlockDriverState *bs, int nb_clusters,
 | 
			
		||||
static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
 | 
			
		||||
    uint64_t *l2_slice, int l2_index)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < nb_clusters; i++) {
 | 
			
		||||
        uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]);
 | 
			
		||||
        QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry);
 | 
			
		||||
        QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
 | 
			
		||||
 | 
			
		||||
        switch(cluster_type) {
 | 
			
		||||
        case QCOW2_CLUSTER_NORMAL:
 | 
			
		||||
@@ -1131,9 +1108,9 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Checks how many already allocated clusters that don't require a copy on
 | 
			
		||||
 * write there are at the given guest_offset (up to *bytes). If *host_offset is
 | 
			
		||||
 * not INV_OFFSET, only physically contiguous clusters beginning at this host
 | 
			
		||||
 * offset are counted.
 | 
			
		||||
 * write there are at the given guest_offset (up to *bytes). If
 | 
			
		||||
 * *host_offset is not zero, only physically contiguous clusters beginning at
 | 
			
		||||
 * this host offset are counted.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that guest_offset may not be cluster aligned. In this case, the
 | 
			
		||||
 * returned *host_offset points to exact byte referenced by guest_offset and
 | 
			
		||||
@@ -1165,7 +1142,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
    trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset,
 | 
			
		||||
                              *bytes);
 | 
			
		||||
 | 
			
		||||
    assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset)
 | 
			
		||||
    assert(*host_offset == 0 ||    offset_into_cluster(s, guest_offset)
 | 
			
		||||
                                == offset_into_cluster(s, *host_offset));
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@@ -1188,7 +1165,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
    cluster_offset = be64_to_cpu(l2_slice[l2_index]);
 | 
			
		||||
 | 
			
		||||
    /* Check how many clusters are already allocated and don't need COW */
 | 
			
		||||
    if (qcow2_get_cluster_type(bs, cluster_offset) == QCOW2_CLUSTER_NORMAL
 | 
			
		||||
    if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL
 | 
			
		||||
        && (cluster_offset & QCOW_OFLAG_COPIED))
 | 
			
		||||
    {
 | 
			
		||||
        /* If a specific host_offset is required, check it */
 | 
			
		||||
@@ -1204,7 +1181,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (*host_offset != INV_OFFSET && !offset_matches) {
 | 
			
		||||
        if (*host_offset != 0 && !offset_matches) {
 | 
			
		||||
            *bytes = 0;
 | 
			
		||||
            ret = 0;
 | 
			
		||||
            goto out;
 | 
			
		||||
@@ -1212,7 +1189,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
 | 
			
		||||
        /* We keep all QCOW_OFLAG_COPIED clusters */
 | 
			
		||||
        keep_clusters =
 | 
			
		||||
            count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
 | 
			
		||||
            count_contiguous_clusters(nb_clusters, s->cluster_size,
 | 
			
		||||
                                      &l2_slice[l2_index],
 | 
			
		||||
                                      QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO);
 | 
			
		||||
        assert(keep_clusters <= nb_clusters);
 | 
			
		||||
@@ -1247,10 +1224,10 @@ out:
 | 
			
		||||
 * contain the number of clusters that have been allocated and are contiguous
 | 
			
		||||
 * in the image file.
 | 
			
		||||
 *
 | 
			
		||||
 * If *host_offset is not INV_OFFSET, it specifies the offset in the image file
 | 
			
		||||
 * at which the new clusters must start. *nb_clusters can be 0 on return in
 | 
			
		||||
 * this case if the cluster at host_offset is already in use. If *host_offset
 | 
			
		||||
 * is INV_OFFSET, the clusters can be allocated anywhere in the image file.
 | 
			
		||||
 * If *host_offset is non-zero, it specifies the offset in the image file at
 | 
			
		||||
 * which the new clusters must start. *nb_clusters can be 0 on return in this
 | 
			
		||||
 * case if the cluster at host_offset is already in use. If *host_offset is
 | 
			
		||||
 * zero, the clusters can be allocated anywhere in the image file.
 | 
			
		||||
 *
 | 
			
		||||
 * *host_offset is updated to contain the offset into the image file at which
 | 
			
		||||
 * the first allocated cluster starts.
 | 
			
		||||
@@ -1267,16 +1244,9 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
    trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
 | 
			
		||||
                                         *host_offset, *nb_clusters);
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        assert(*host_offset == INV_OFFSET ||
 | 
			
		||||
               *host_offset == start_of_cluster(s, guest_offset));
 | 
			
		||||
        *host_offset = start_of_cluster(s, guest_offset);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Allocate new clusters */
 | 
			
		||||
    trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
 | 
			
		||||
    if (*host_offset == INV_OFFSET) {
 | 
			
		||||
    if (*host_offset == 0) {
 | 
			
		||||
        int64_t cluster_offset =
 | 
			
		||||
            qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size);
 | 
			
		||||
        if (cluster_offset < 0) {
 | 
			
		||||
@@ -1296,8 +1266,8 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Allocates new clusters for an area that either is yet unallocated or needs a
 | 
			
		||||
 * copy on write. If *host_offset is not INV_OFFSET, clusters are only
 | 
			
		||||
 * allocated if the new allocation can match the specified host offset.
 | 
			
		||||
 * copy on write. If *host_offset is non-zero, clusters are only allocated if
 | 
			
		||||
 * the new allocation can match the specified host offset.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that guest_offset may not be cluster aligned. In this case, the
 | 
			
		||||
 * returned *host_offset points to exact byte referenced by guest_offset and
 | 
			
		||||
@@ -1325,7 +1295,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
    int ret;
 | 
			
		||||
    bool keep_old_clusters = false;
 | 
			
		||||
 | 
			
		||||
    uint64_t alloc_cluster_offset = INV_OFFSET;
 | 
			
		||||
    uint64_t alloc_cluster_offset = 0;
 | 
			
		||||
 | 
			
		||||
    trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
 | 
			
		||||
                             *bytes);
 | 
			
		||||
@@ -1354,7 +1324,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
    if (entry & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
        nb_clusters = 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        nb_clusters = count_cow_clusters(bs, nb_clusters, l2_slice, l2_index);
 | 
			
		||||
        nb_clusters = count_cow_clusters(s, nb_clusters, l2_slice, l2_index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* This function is only called when there were no non-COW clusters, so if
 | 
			
		||||
@@ -1362,9 +1332,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
     * wrong with our code. */
 | 
			
		||||
    assert(nb_clusters > 0);
 | 
			
		||||
 | 
			
		||||
    if (qcow2_get_cluster_type(bs, entry) == QCOW2_CLUSTER_ZERO_ALLOC &&
 | 
			
		||||
    if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO_ALLOC &&
 | 
			
		||||
        (entry & QCOW_OFLAG_COPIED) &&
 | 
			
		||||
        (*host_offset == INV_OFFSET ||
 | 
			
		||||
        (!*host_offset ||
 | 
			
		||||
         start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
 | 
			
		||||
    {
 | 
			
		||||
        int preallocated_nb_clusters;
 | 
			
		||||
@@ -1382,7 +1352,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
         * would be fine, too, but count_cow_clusters() above has limited
 | 
			
		||||
         * nb_clusters already to a range of COW clusters */
 | 
			
		||||
        preallocated_nb_clusters =
 | 
			
		||||
            count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
 | 
			
		||||
            count_contiguous_clusters(nb_clusters, s->cluster_size,
 | 
			
		||||
                                      &l2_slice[l2_index], QCOW_OFLAG_COPIED);
 | 
			
		||||
        assert(preallocated_nb_clusters > 0);
 | 
			
		||||
 | 
			
		||||
@@ -1396,10 +1366,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
 | 
			
		||||
    qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
 | 
			
		||||
 | 
			
		||||
    if (alloc_cluster_offset == INV_OFFSET) {
 | 
			
		||||
    if (!alloc_cluster_offset) {
 | 
			
		||||
        /* Allocate, if necessary at a given offset in the image file */
 | 
			
		||||
        alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET :
 | 
			
		||||
                               start_of_cluster(s, *host_offset);
 | 
			
		||||
        alloc_cluster_offset = start_of_cluster(s, *host_offset);
 | 
			
		||||
        ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
 | 
			
		||||
                                      &nb_clusters);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
@@ -1412,7 +1381,16 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert(alloc_cluster_offset != INV_OFFSET);
 | 
			
		||||
        /* !*host_offset would overwrite the image header and is reserved for
 | 
			
		||||
         * "no host offset preferred". If 0 was a valid host offset, it'd
 | 
			
		||||
         * trigger the following overlap check; do that now to avoid having an
 | 
			
		||||
         * invalid value in *host_offset. */
 | 
			
		||||
        if (!alloc_cluster_offset) {
 | 
			
		||||
            ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
 | 
			
		||||
                                                nb_clusters * s->cluster_size);
 | 
			
		||||
            assert(ret < 0);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@@ -1504,14 +1482,14 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
again:
 | 
			
		||||
    start = offset;
 | 
			
		||||
    remaining = *bytes;
 | 
			
		||||
    cluster_offset = INV_OFFSET;
 | 
			
		||||
    *host_offset = INV_OFFSET;
 | 
			
		||||
    cluster_offset = 0;
 | 
			
		||||
    *host_offset = 0;
 | 
			
		||||
    cur_bytes = 0;
 | 
			
		||||
    *m = NULL;
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
 | 
			
		||||
        if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) {
 | 
			
		||||
        if (!*host_offset) {
 | 
			
		||||
            *host_offset = start_of_cluster(s, cluster_offset);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1519,10 +1497,7 @@ again:
 | 
			
		||||
 | 
			
		||||
        start           += cur_bytes;
 | 
			
		||||
        remaining       -= cur_bytes;
 | 
			
		||||
 | 
			
		||||
        if (cluster_offset != INV_OFFSET) {
 | 
			
		||||
        cluster_offset  += cur_bytes;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (remaining == 0) {
 | 
			
		||||
            break;
 | 
			
		||||
@@ -1594,7 +1569,7 @@ again:
 | 
			
		||||
 | 
			
		||||
    *bytes -= remaining;
 | 
			
		||||
    assert(*bytes > 0);
 | 
			
		||||
    assert(*host_offset != INV_OFFSET);
 | 
			
		||||
    assert(*host_offset != 0);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1641,7 +1616,7 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
         * If full_discard is true, the sector should not read back as zeroes,
 | 
			
		||||
         * but rather fall through to the backing file.
 | 
			
		||||
         */
 | 
			
		||||
        switch (qcow2_get_cluster_type(bs, old_l2_entry)) {
 | 
			
		||||
        switch (qcow2_get_cluster_type(old_l2_entry)) {
 | 
			
		||||
        case QCOW2_CLUSTER_UNALLOCATED:
 | 
			
		||||
            if (full_discard || !bs->backing) {
 | 
			
		||||
                continue;
 | 
			
		||||
@@ -1754,7 +1729,7 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
         * Minimize L2 changes if the cluster already reads back as
 | 
			
		||||
         * zeroes with correct allocation.
 | 
			
		||||
         */
 | 
			
		||||
        cluster_type = qcow2_get_cluster_type(bs, old_offset);
 | 
			
		||||
        cluster_type = qcow2_get_cluster_type(old_offset);
 | 
			
		||||
        if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN ||
 | 
			
		||||
            (cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) {
 | 
			
		||||
            continue;
 | 
			
		||||
@@ -1783,16 +1758,6 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    int64_t cleared;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* If we have to stay in sync with an external data file, zero out
 | 
			
		||||
     * s->data_file first. */
 | 
			
		||||
    if (data_file_is_raw(bs)) {
 | 
			
		||||
        assert(has_data_file(bs));
 | 
			
		||||
        ret = bdrv_co_pwrite_zeroes(s->data_file, offset, bytes, flags);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Caller must pass aligned values, except at image end */
 | 
			
		||||
    assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
 | 
			
		||||
    assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
 | 
			
		||||
@@ -1906,7 +1871,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
                uint64_t l2_entry = be64_to_cpu(l2_slice[j]);
 | 
			
		||||
                int64_t offset = l2_entry & L2E_OFFSET_MASK;
 | 
			
		||||
                QCow2ClusterType cluster_type =
 | 
			
		||||
                    qcow2_get_cluster_type(bs, l2_entry);
 | 
			
		||||
                    qcow2_get_cluster_type(l2_entry);
 | 
			
		||||
 | 
			
		||||
                if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN &&
 | 
			
		||||
                    cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) {
 | 
			
		||||
@@ -1960,7 +1925,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ret = qcow2_pre_write_overlap_check(bs, 0, offset,
 | 
			
		||||
                                                    s->cluster_size, true);
 | 
			
		||||
                                                    s->cluster_size);
 | 
			
		||||
                if (ret < 0) {
 | 
			
		||||
                    if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
 | 
			
		||||
                        qcow2_free_clusters(bs, offset, s->cluster_size,
 | 
			
		||||
@@ -1969,8 +1934,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
                    goto fail;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ret = bdrv_pwrite_zeroes(s->data_file, offset,
 | 
			
		||||
                                         s->cluster_size, 0);
 | 
			
		||||
                ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0);
 | 
			
		||||
                if (ret < 0) {
 | 
			
		||||
                    if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
 | 
			
		||||
                        qcow2_free_clusters(bs, offset, s->cluster_size,
 | 
			
		||||
@@ -1997,7 +1961,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
                if (l2_dirty) {
 | 
			
		||||
                    ret = qcow2_pre_write_overlap_check(
 | 
			
		||||
                        bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2,
 | 
			
		||||
                        slice_offset, slice_size2, false);
 | 
			
		||||
                        slice_offset, slice_size2);
 | 
			
		||||
                    if (ret < 0) {
 | 
			
		||||
                        goto fail;
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1156,20 +1156,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
 | 
			
		||||
                             int nb_clusters, enum qcow2_discard_type type)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry);
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        if (s->discard_passthrough[type] &&
 | 
			
		||||
            (ctype == QCOW2_CLUSTER_NORMAL ||
 | 
			
		||||
             ctype == QCOW2_CLUSTER_ZERO_ALLOC))
 | 
			
		||||
        {
 | 
			
		||||
            bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK,
 | 
			
		||||
                          nb_clusters << s->cluster_bits);
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (ctype) {
 | 
			
		||||
    switch (qcow2_get_cluster_type(l2_entry)) {
 | 
			
		||||
    case QCOW2_CLUSTER_COMPRESSED:
 | 
			
		||||
        {
 | 
			
		||||
            int nb_csectors;
 | 
			
		||||
@@ -1312,7 +1300,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
 | 
			
		||||
                    entry &= ~QCOW_OFLAG_COPIED;
 | 
			
		||||
                    offset = entry & L2E_OFFSET_MASK;
 | 
			
		||||
 | 
			
		||||
                    switch (qcow2_get_cluster_type(bs, entry)) {
 | 
			
		||||
                    switch (qcow2_get_cluster_type(entry)) {
 | 
			
		||||
                    case QCOW2_CLUSTER_COMPRESSED:
 | 
			
		||||
                        nb_csectors = ((entry >> s->csize_shift) &
 | 
			
		||||
                                       s->csize_mask) + 1;
 | 
			
		||||
@@ -1594,7 +1582,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
    for(i = 0; i < s->l2_size; i++) {
 | 
			
		||||
        l2_entry = be64_to_cpu(l2_table[i]);
 | 
			
		||||
 | 
			
		||||
        switch (qcow2_get_cluster_type(bs, l2_entry)) {
 | 
			
		||||
        switch (qcow2_get_cluster_type(l2_entry)) {
 | 
			
		||||
        case QCOW2_CLUSTER_COMPRESSED:
 | 
			
		||||
            /* Compressed clusters don't have QCOW_OFLAG_COPIED */
 | 
			
		||||
            if (l2_entry & QCOW_OFLAG_COPIED) {
 | 
			
		||||
@@ -1605,13 +1593,6 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
                res->corruptions++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (has_data_file(bs)) {
 | 
			
		||||
                fprintf(stderr, "ERROR compressed cluster %d with data file, "
 | 
			
		||||
                        "entry=0x%" PRIx64 "\n", i, l2_entry);
 | 
			
		||||
                res->corruptions++;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Mark cluster as used */
 | 
			
		||||
            nb_csectors = ((l2_entry >> s->csize_shift) &
 | 
			
		||||
                           s->csize_mask) + 1;
 | 
			
		||||
@@ -1652,7 +1633,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
 | 
			
		||||
            /* Correct offsets are cluster aligned */
 | 
			
		||||
            if (offset_into_cluster(s, offset)) {
 | 
			
		||||
                if (qcow2_get_cluster_type(bs, l2_entry) ==
 | 
			
		||||
                if (qcow2_get_cluster_type(l2_entry) ==
 | 
			
		||||
                    QCOW2_CLUSTER_ZERO_ALLOC)
 | 
			
		||||
                {
 | 
			
		||||
                    fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated zero "
 | 
			
		||||
@@ -1668,7 +1649,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
                        l2_table[i] = cpu_to_be64(l2_entry);
 | 
			
		||||
                        ret = qcow2_pre_write_overlap_check(bs,
 | 
			
		||||
                                QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2,
 | 
			
		||||
                                l2e_offset, sizeof(uint64_t), false);
 | 
			
		||||
                                l2e_offset, sizeof(uint64_t));
 | 
			
		||||
                        if (ret < 0) {
 | 
			
		||||
                            fprintf(stderr, "ERROR: Overlap check failed\n");
 | 
			
		||||
                            res->check_errors++;
 | 
			
		||||
@@ -1702,14 +1683,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Mark cluster as used */
 | 
			
		||||
            if (!has_data_file(bs)) {
 | 
			
		||||
                ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table,
 | 
			
		||||
                                               refcount_table_size,
 | 
			
		||||
            ret = qcow2_inc_refcounts_imrt(bs, res,
 | 
			
		||||
                                           refcount_table, refcount_table_size,
 | 
			
		||||
                                           offset, s->cluster_size);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1889,13 +1868,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
        for (j = 0; j < s->l2_size; j++) {
 | 
			
		||||
            uint64_t l2_entry = be64_to_cpu(l2_table[j]);
 | 
			
		||||
            uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
 | 
			
		||||
            QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry);
 | 
			
		||||
            QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
 | 
			
		||||
 | 
			
		||||
            if (cluster_type == QCOW2_CLUSTER_NORMAL ||
 | 
			
		||||
                cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) {
 | 
			
		||||
                if (has_data_file(bs)) {
 | 
			
		||||
                    refcount = 1;
 | 
			
		||||
                } else {
 | 
			
		||||
                ret = qcow2_get_refcount(bs,
 | 
			
		||||
                                         data_offset >> s->cluster_bits,
 | 
			
		||||
                                         &refcount);
 | 
			
		||||
@@ -1903,7 +1879,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
                    /* don't print message nor increment check_errors */
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                }
 | 
			
		||||
                if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
 | 
			
		||||
                    fprintf(stderr, "%s OFLAG_COPIED data cluster: "
 | 
			
		||||
                            "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
 | 
			
		||||
@@ -1923,8 +1898,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
 | 
			
		||||
        if (l2_dirty) {
 | 
			
		||||
            ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
 | 
			
		||||
                                                l2_offset, s->cluster_size,
 | 
			
		||||
                                                false);
 | 
			
		||||
                                                l2_offset, s->cluster_size);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                fprintf(stderr, "ERROR: Could not write L2 table; metadata "
 | 
			
		||||
                        "overlap check failed: %s\n", strerror(-ret));
 | 
			
		||||
@@ -2096,12 +2070,6 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* snapshots */
 | 
			
		||||
    if (has_data_file(bs) && s->nb_snapshots) {
 | 
			
		||||
        fprintf(stderr, "ERROR %d snapshots in image with data file\n",
 | 
			
		||||
                s->nb_snapshots);
 | 
			
		||||
        res->corruptions++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        sn = s->snapshots + i;
 | 
			
		||||
        if (offset_into_cluster(s, sn->l1_table_offset)) {
 | 
			
		||||
@@ -2398,7 +2366,7 @@ write_refblocks:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset,
 | 
			
		||||
                                            s->cluster_size, false);
 | 
			
		||||
                                            s->cluster_size);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
 | 
			
		||||
            goto fail;
 | 
			
		||||
@@ -2449,8 +2417,7 @@ write_refblocks:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset,
 | 
			
		||||
                                        reftable_size * sizeof(uint64_t),
 | 
			
		||||
                                        false);
 | 
			
		||||
                                        reftable_size * sizeof(uint64_t));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
 | 
			
		||||
        goto fail;
 | 
			
		||||
@@ -2784,15 +2751,10 @@ QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
 | 
			
		||||
 * overlaps; or a negative value (-errno) on error.
 | 
			
		||||
 */
 | 
			
		||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
                                  int64_t size, bool data_file)
 | 
			
		||||
                                  int64_t size)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    int ret = qcow2_check_metadata_overlap(bs, ign, offset, size);
 | 
			
		||||
 | 
			
		||||
    if (data_file && has_data_file(bs)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_check_metadata_overlap(bs, ign, offset, size);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    } else if (ret > 0) {
 | 
			
		||||
@@ -2893,8 +2855,7 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable,
 | 
			
		||||
    if (reftable_index < *reftable_size && (*reftable)[reftable_index]) {
 | 
			
		||||
        offset = (*reftable)[reftable_index];
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size,
 | 
			
		||||
                                            false);
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "Overlap check failed");
 | 
			
		||||
            return ret;
 | 
			
		||||
@@ -3160,8 +3121,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
 | 
			
		||||
 | 
			
		||||
    /* Write the new reftable */
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset,
 | 
			
		||||
                                        new_reftable_size * sizeof(uint64_t),
 | 
			
		||||
                                        false);
 | 
			
		||||
                                        new_reftable_size * sizeof(uint64_t));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Overlap check failed");
 | 
			
		||||
        goto done;
 | 
			
		||||
 
 | 
			
		||||
@@ -184,7 +184,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
 | 
			
		||||
 | 
			
		||||
    /* The snapshot list position has not yet been updated, so these clusters
 | 
			
		||||
     * must indeed be completely free */
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size, false);
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -353,10 +353,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
        return -EFBIG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(sn, 0, sizeof(*sn));
 | 
			
		||||
 | 
			
		||||
    /* Generate an ID */
 | 
			
		||||
@@ -393,7 +389,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset,
 | 
			
		||||
                                        s->l1_size * sizeof(uint64_t), false);
 | 
			
		||||
                                        s->l1_size * sizeof(uint64_t));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -470,10 +466,6 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint64_t *sn_l1_table = NULL;
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Search the snapshot */
 | 
			
		||||
    snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
 | 
			
		||||
    if (snapshot_index < 0) {
 | 
			
		||||
@@ -536,8 +528,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
 | 
			
		||||
                                        s->l1_table_offset, cur_l1_bytes,
 | 
			
		||||
                                        false);
 | 
			
		||||
                                        s->l1_table_offset, cur_l1_bytes);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -607,10 +598,6 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
 | 
			
		||||
    QCowSnapshot sn;
 | 
			
		||||
    int snapshot_index, ret;
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Search the snapshot */
 | 
			
		||||
    snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
 | 
			
		||||
    if (snapshot_index < 0) {
 | 
			
		||||
@@ -682,9 +669,6 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    if (!s->nb_snapshots) {
 | 
			
		||||
        *psn_tab = NULL;
 | 
			
		||||
        return s->nb_snapshots;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										355
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										355
									
								
								block/qcow2.c
									
									
									
									
									
								
							@@ -73,7 +73,6 @@ typedef struct {
 | 
			
		||||
#define  QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
 | 
			
		||||
#define  QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
 | 
			
		||||
#define  QCOW2_EXT_MAGIC_BITMAPS 0x23852875
 | 
			
		||||
#define  QCOW2_EXT_MAGIC_DATA_FILE 0x44415441
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
qcow2_co_preadv_compressed(BlockDriverState *bs,
 | 
			
		||||
@@ -140,7 +139,7 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
 | 
			
		||||
    /* Zero fill remaining space in cluster so it has predictable
 | 
			
		||||
     * content in case of future spec changes */
 | 
			
		||||
    clusterlen = size_to_clusters(s, headerlen) * s->cluster_size;
 | 
			
		||||
    assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0);
 | 
			
		||||
    assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen) == 0);
 | 
			
		||||
    ret = bdrv_pwrite_zeroes(bs->file,
 | 
			
		||||
                             ret + headerlen,
 | 
			
		||||
                             clusterlen - headerlen, 0);
 | 
			
		||||
@@ -398,21 +397,6 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
 | 
			
		||||
#endif
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case QCOW2_EXT_MAGIC_DATA_FILE:
 | 
			
		||||
        {
 | 
			
		||||
            s->image_data_file = g_malloc0(ext.len + 1);
 | 
			
		||||
            ret = bdrv_pread(bs->file, offset, s->image_data_file, ext.len);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                error_setg_errno(errp, -ret,
 | 
			
		||||
                                 "ERROR: Could not read data file name");
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
#ifdef DEBUG_EXT
 | 
			
		||||
            printf("Qcow2: Got external data file %s\n", s->image_data_file);
 | 
			
		||||
#endif
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            /* unknown magic - save it in case we need to rewrite the header */
 | 
			
		||||
            /* If you add a new feature, make sure to also update the fast
 | 
			
		||||
@@ -627,30 +611,6 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *const mutable_opts[] = {
 | 
			
		||||
    QCOW2_OPT_LAZY_REFCOUNTS,
 | 
			
		||||
    QCOW2_OPT_DISCARD_REQUEST,
 | 
			
		||||
    QCOW2_OPT_DISCARD_SNAPSHOT,
 | 
			
		||||
    QCOW2_OPT_DISCARD_OTHER,
 | 
			
		||||
    QCOW2_OPT_OVERLAP,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_TEMPLATE,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_MAIN_HEADER,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_ACTIVE_L1,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_ACTIVE_L2,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_INACTIVE_L1,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_INACTIVE_L2,
 | 
			
		||||
    QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
 | 
			
		||||
    QCOW2_OPT_CACHE_SIZE,
 | 
			
		||||
    QCOW2_OPT_L2_CACHE_SIZE,
 | 
			
		||||
    QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
 | 
			
		||||
    QCOW2_OPT_REFCOUNT_CACHE_SIZE,
 | 
			
		||||
    QCOW2_OPT_CACHE_CLEAN_INTERVAL,
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static QemuOptsList qcow2_runtime_opts = {
 | 
			
		||||
    .name = "qcow2",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
 | 
			
		||||
@@ -828,7 +788,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    uint64_t combined_cache_size, l2_cache_max_setting;
 | 
			
		||||
    bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
 | 
			
		||||
    bool l2_cache_entry_size_set;
 | 
			
		||||
    int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size;
 | 
			
		||||
    uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
 | 
			
		||||
    uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);
 | 
			
		||||
@@ -836,7 +795,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
    combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE);
 | 
			
		||||
    l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE);
 | 
			
		||||
    refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
 | 
			
		||||
    l2_cache_entry_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE);
 | 
			
		||||
 | 
			
		||||
    combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0);
 | 
			
		||||
    l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE,
 | 
			
		||||
@@ -883,16 +841,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If the L2 cache is not enough to cover the whole disk then
 | 
			
		||||
     * default to 4KB entries. Smaller entries reduce the cost of
 | 
			
		||||
     * loads and evictions and increase I/O performance.
 | 
			
		||||
     */
 | 
			
		||||
    if (*l2_cache_size < max_l2_cache && !l2_cache_entry_size_set) {
 | 
			
		||||
        *l2_cache_entry_size = MIN(s->cluster_size, 4096);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* l2_cache_size and refcount_cache_size are ensured to have at least
 | 
			
		||||
     * their minimum values in qcow2_update_options_prepare() */
 | 
			
		||||
 | 
			
		||||
@@ -1492,47 +1440,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Open external data file */
 | 
			
		||||
    s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_file,
 | 
			
		||||
                                   true, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
 | 
			
		||||
        if (!s->data_file && s->image_data_file) {
 | 
			
		||||
            s->data_file = bdrv_open_child(s->image_data_file, options,
 | 
			
		||||
                                           "data-file", bs, &child_file,
 | 
			
		||||
                                           false, errp);
 | 
			
		||||
            if (!s->data_file) {
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!s->data_file) {
 | 
			
		||||
            error_setg(errp, "'data-file' is required for this image");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (s->data_file) {
 | 
			
		||||
            error_setg(errp, "'data-file' can only be set for images with an "
 | 
			
		||||
                             "external data file");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        s->data_file = bs->file;
 | 
			
		||||
 | 
			
		||||
        if (data_file_is_raw(bs)) {
 | 
			
		||||
            error_setg(errp, "data-file-raw requires a data file");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* qcow2_read_extension may have set up the crypto context
 | 
			
		||||
     * if the crypt method needs a header region, some methods
 | 
			
		||||
     * don't need header extensions, so must check here
 | 
			
		||||
@@ -1704,10 +1611,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
 | 
			
		||||
    return ret;
 | 
			
		||||
 | 
			
		||||
 fail:
 | 
			
		||||
    g_free(s->image_data_file);
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        bdrv_unref_child(bs, s->data_file);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(s->unknown_header_fields);
 | 
			
		||||
    cleanup_unknown_header_ext(bs);
 | 
			
		||||
    qcow2_free_snapshots(bs);
 | 
			
		||||
@@ -1910,11 +1813,11 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    *pnum = bytes;
 | 
			
		||||
 | 
			
		||||
    if ((ret == QCOW2_CLUSTER_NORMAL || ret == QCOW2_CLUSTER_ZERO_ALLOC) &&
 | 
			
		||||
    if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
 | 
			
		||||
        !s->crypto) {
 | 
			
		||||
        index_in_cluster = offset & (s->cluster_size - 1);
 | 
			
		||||
        *map = cluster_offset | index_in_cluster;
 | 
			
		||||
        *file = s->data_file->bs;
 | 
			
		||||
        *file = bs->file->bs;
 | 
			
		||||
        status |= BDRV_BLOCK_OFFSET_VALID;
 | 
			
		||||
    }
 | 
			
		||||
    if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
 | 
			
		||||
@@ -2046,7 +1949,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                 */
 | 
			
		||||
                if (!cluster_data) {
 | 
			
		||||
                    cluster_data =
 | 
			
		||||
                        qemu_try_blockalign(s->data_file->bs,
 | 
			
		||||
                        qemu_try_blockalign(bs->file->bs,
 | 
			
		||||
                                            QCOW_MAX_CRYPT_CLUSTERS
 | 
			
		||||
                                            * s->cluster_size);
 | 
			
		||||
                    if (cluster_data == NULL) {
 | 
			
		||||
@@ -2062,7 +1965,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
 | 
			
		||||
            BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
 | 
			
		||||
            qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
            ret = bdrv_co_preadv(s->data_file,
 | 
			
		||||
            ret = bdrv_co_preadv(bs->file,
 | 
			
		||||
                                 cluster_offset + offset_in_cluster,
 | 
			
		||||
                                 cur_bytes, &hd_qiov, 0);
 | 
			
		||||
            qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
@@ -2221,7 +2124,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0,
 | 
			
		||||
                cluster_offset + offset_in_cluster, cur_bytes, true);
 | 
			
		||||
                cluster_offset + offset_in_cluster, cur_bytes);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
@@ -2235,7 +2138,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
            BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
 | 
			
		||||
            trace_qcow2_writev_data(qemu_coroutine_self(),
 | 
			
		||||
                                    cluster_offset + offset_in_cluster);
 | 
			
		||||
            ret = bdrv_co_pwritev(s->data_file,
 | 
			
		||||
            ret = bdrv_co_pwritev(bs->file,
 | 
			
		||||
                                  cluster_offset + offset_in_cluster,
 | 
			
		||||
                                  cur_bytes, &hd_qiov, 0);
 | 
			
		||||
            qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
@@ -2324,14 +2227,9 @@ static void qcow2_close(BlockDriverState *bs)
 | 
			
		||||
    g_free(s->unknown_header_fields);
 | 
			
		||||
    cleanup_unknown_header_ext(bs);
 | 
			
		||||
 | 
			
		||||
    g_free(s->image_data_file);
 | 
			
		||||
    g_free(s->image_backing_file);
 | 
			
		||||
    g_free(s->image_backing_format);
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        bdrv_unref_child(bs, s->data_file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qcow2_refcount_close(bs);
 | 
			
		||||
    qcow2_free_snapshots(bs);
 | 
			
		||||
}
 | 
			
		||||
@@ -2501,19 +2399,6 @@ int qcow2_update_header(BlockDriverState *bs)
 | 
			
		||||
        buflen -= ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* External data file header extension */
 | 
			
		||||
    if (has_data_file(bs) && s->image_data_file) {
 | 
			
		||||
        ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DATA_FILE,
 | 
			
		||||
                             s->image_data_file, strlen(s->image_data_file),
 | 
			
		||||
                             buflen);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buf += ret;
 | 
			
		||||
        buflen -= ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Full disk encryption header pointer extension */
 | 
			
		||||
    if (s->crypto_header.offset != 0) {
 | 
			
		||||
        s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset);
 | 
			
		||||
@@ -2543,11 +2428,6 @@ int qcow2_update_header(BlockDriverState *bs)
 | 
			
		||||
                .bit  = QCOW2_INCOMPAT_CORRUPT_BITNR,
 | 
			
		||||
                .name = "corrupt bit",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                .type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
 | 
			
		||||
                .bit  = QCOW2_INCOMPAT_DATA_FILE_BITNR,
 | 
			
		||||
                .name = "external data file",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                .type = QCOW2_FEAT_TYPE_COMPATIBLE,
 | 
			
		||||
                .bit  = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
 | 
			
		||||
@@ -2636,12 +2516,6 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    /* Adding a backing file means that the external data file alone won't be
 | 
			
		||||
     * enough to make sense of the content */
 | 
			
		||||
    if (backing_file && data_file_is_raw(bs)) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (backing_file && strlen(backing_file) > 1023) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
@@ -2955,7 +2829,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
     */
 | 
			
		||||
    BlockBackend *blk = NULL;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    BlockDriverState *data_bs = NULL;
 | 
			
		||||
    QCowHeader *header;
 | 
			
		||||
    size_t cluster_size;
 | 
			
		||||
    int version;
 | 
			
		||||
@@ -3052,32 +2925,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
    refcount_order = ctz32(qcow2_opts->refcount_bits);
 | 
			
		||||
 | 
			
		||||
    if (qcow2_opts->data_file_raw && !qcow2_opts->data_file) {
 | 
			
		||||
        error_setg(errp, "data-file-raw requires data-file");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    if (qcow2_opts->data_file_raw && qcow2_opts->has_backing_file) {
 | 
			
		||||
        error_setg(errp, "Backing file and data-file-raw cannot be used at "
 | 
			
		||||
                   "the same time");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qcow2_opts->data_file) {
 | 
			
		||||
        if (version < 3) {
 | 
			
		||||
            error_setg(errp, "External data files are only supported with "
 | 
			
		||||
                       "compatibility level 1.1 and above (use version=v3 or "
 | 
			
		||||
                       "greater)");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        data_bs = bdrv_open_blockdev_ref(qcow2_opts->data_file, errp);
 | 
			
		||||
        if (data_bs == NULL) {
 | 
			
		||||
            ret = -EIO;
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create BlockBackend to write to the image */
 | 
			
		||||
    blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
 | 
			
		||||
@@ -3093,6 +2940,19 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qcow2_opts->preallocation == PREALLOC_MODE_FULL ||
 | 
			
		||||
        qcow2_opts->preallocation == PREALLOC_MODE_FALLOC)
 | 
			
		||||
    {
 | 
			
		||||
        int64_t prealloc_size =
 | 
			
		||||
            qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size,
 | 
			
		||||
                                     refcount_order);
 | 
			
		||||
 | 
			
		||||
        ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Write the header */
 | 
			
		||||
    QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
 | 
			
		||||
    header = g_malloc0(cluster_size);
 | 
			
		||||
@@ -3116,14 +2976,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
        header->compatible_features |=
 | 
			
		||||
            cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
 | 
			
		||||
    }
 | 
			
		||||
    if (data_bs) {
 | 
			
		||||
        header->incompatible_features |=
 | 
			
		||||
            cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE);
 | 
			
		||||
    }
 | 
			
		||||
    if (qcow2_opts->data_file_raw) {
 | 
			
		||||
        header->autoclear_features |=
 | 
			
		||||
            cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = blk_pwrite(blk, 0, header, cluster_size, 0);
 | 
			
		||||
    g_free(header);
 | 
			
		||||
@@ -3154,9 +3006,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
    options = qdict_new();
 | 
			
		||||
    qdict_put_str(options, "driver", "qcow2");
 | 
			
		||||
    qdict_put_str(options, "file", bs->node_name);
 | 
			
		||||
    if (data_bs) {
 | 
			
		||||
        qdict_put_str(options, "data-file", data_bs->node_name);
 | 
			
		||||
    }
 | 
			
		||||
    blk = blk_new_open(NULL, NULL, options,
 | 
			
		||||
                       BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
 | 
			
		||||
                       &local_err);
 | 
			
		||||
@@ -3177,12 +3026,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set the external data file if necessary */
 | 
			
		||||
    if (data_bs) {
 | 
			
		||||
        BDRVQcow2State *s = blk_bs(blk)->opaque;
 | 
			
		||||
        s->image_data_file = g_strdup(data_bs->filename);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create a full header (including things like feature table) */
 | 
			
		||||
    ret = qcow2_update_header(blk_bs(blk));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
@@ -3191,7 +3034,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Okay, now that we have a valid image, let's give it the right size */
 | 
			
		||||
    ret = blk_truncate(blk, qcow2_opts->size, qcow2_opts->preallocation, errp);
 | 
			
		||||
    ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_prepend(errp, "Could not resize image: ");
 | 
			
		||||
        goto out;
 | 
			
		||||
@@ -3223,6 +3066,19 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* And if we're supposed to preallocate metadata, do that now */
 | 
			
		||||
    if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) {
 | 
			
		||||
        BDRVQcow2State *s = blk_bs(blk)->opaque;
 | 
			
		||||
        qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
        ret = preallocate_co(blk_bs(blk), 0, qcow2_opts->size);
 | 
			
		||||
        qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "Could not preallocate metadata");
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk_unref(blk);
 | 
			
		||||
    blk = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -3235,9 +3091,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
    options = qdict_new();
 | 
			
		||||
    qdict_put_str(options, "driver", "qcow2");
 | 
			
		||||
    qdict_put_str(options, "file", bs->node_name);
 | 
			
		||||
    if (data_bs) {
 | 
			
		||||
        qdict_put_str(options, "data-file", data_bs->node_name);
 | 
			
		||||
    }
 | 
			
		||||
    blk = blk_new_open(NULL, NULL, options,
 | 
			
		||||
                       BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
 | 
			
		||||
                       &local_err);
 | 
			
		||||
@@ -3251,7 +3104,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
out:
 | 
			
		||||
    blk_unref(blk);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    bdrv_unref(data_bs);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -3262,7 +3114,6 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
 | 
			
		||||
    QDict *qdict;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    BlockDriverState *data_bs = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    const char *val;
 | 
			
		||||
    int ret;
 | 
			
		||||
@@ -3305,7 +3156,6 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
 | 
			
		||||
        { BLOCK_OPT_REFCOUNT_BITS,      "refcount-bits" },
 | 
			
		||||
        { BLOCK_OPT_ENCRYPT,            BLOCK_OPT_ENCRYPT_FORMAT },
 | 
			
		||||
        { BLOCK_OPT_COMPAT_LEVEL,       "version" },
 | 
			
		||||
        { BLOCK_OPT_DATA_FILE_RAW,      "data-file-raw" },
 | 
			
		||||
        { NULL, NULL },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -3327,26 +3177,6 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create and open an external data file (protocol layer) */
 | 
			
		||||
    val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        ret = bdrv_create_file(val, opts, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto finish;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        data_bs = bdrv_open(val, NULL, NULL,
 | 
			
		||||
                            BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
 | 
			
		||||
                            errp);
 | 
			
		||||
        if (data_bs == NULL) {
 | 
			
		||||
            ret = -EIO;
 | 
			
		||||
            goto finish;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        qdict_del(qdict, BLOCK_OPT_DATA_FILE);
 | 
			
		||||
        qdict_put_str(qdict, "data-file", data_bs->node_name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set 'driver' and 'node' options */
 | 
			
		||||
    qdict_put_str(qdict, "driver", "qcow2");
 | 
			
		||||
    qdict_put_str(qdict, "file", bs->node_name);
 | 
			
		||||
@@ -3381,7 +3211,6 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
 | 
			
		||||
finish:
 | 
			
		||||
    qobject_unref(qdict);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    bdrv_unref(data_bs);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -3532,7 +3361,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs,
 | 
			
		||||
            goto out;
 | 
			
		||||
 | 
			
		||||
        case QCOW2_CLUSTER_NORMAL:
 | 
			
		||||
            child = s->data_file;
 | 
			
		||||
            child = bs->file;
 | 
			
		||||
            copy_offset += offset_into_cluster(s, src_offset);
 | 
			
		||||
            if ((copy_offset & 511) != 0) {
 | 
			
		||||
                ret = -EIO;
 | 
			
		||||
@@ -3602,14 +3431,14 @@ qcow2_co_copy_range_to(BlockDriverState *bs,
 | 
			
		||||
        assert((cluster_offset & 511) == 0);
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0,
 | 
			
		||||
                cluster_offset + offset_in_cluster, cur_bytes, true);
 | 
			
		||||
                cluster_offset + offset_in_cluster, cur_bytes);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
        ret = bdrv_co_copy_range_to(src, src_offset,
 | 
			
		||||
                                    s->data_file,
 | 
			
		||||
                                    bs->file,
 | 
			
		||||
                                    cluster_offset + offset_in_cluster,
 | 
			
		||||
                                    cur_bytes, read_flags, write_flags);
 | 
			
		||||
        qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
@@ -3670,7 +3499,9 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* cannot proceed if image has bitmaps */
 | 
			
		||||
    if (qcow2_truncate_bitmaps_check(bs, errp)) {
 | 
			
		||||
    if (s->nb_bitmaps) {
 | 
			
		||||
        /* TODO: resize bitmaps in the image */
 | 
			
		||||
        error_setg(errp, "Can't resize an image which has bitmaps");
 | 
			
		||||
        ret = -ENOTSUP;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -3762,17 +3593,6 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
        int64_t old_file_size, new_file_size;
 | 
			
		||||
        uint64_t nb_new_data_clusters, nb_new_l2_tables;
 | 
			
		||||
 | 
			
		||||
        /* With a data file, preallocation means just allocating the metadata
 | 
			
		||||
         * and forwarding the truncate request to the data file */
 | 
			
		||||
        if (has_data_file(bs)) {
 | 
			
		||||
            ret = preallocate_co(bs, old_length, offset);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                error_setg_errno(errp, -ret, "Preallocation failed");
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        old_file_size = bdrv_getlength(bs->file->bs);
 | 
			
		||||
        if (old_file_size < 0) {
 | 
			
		||||
            error_setg_errno(errp, -old_file_size,
 | 
			
		||||
@@ -3881,16 +3701,6 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
 | 
			
		||||
    bs->total_sectors = offset / BDRV_SECTOR_SIZE;
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        if (prealloc == PREALLOC_MODE_METADATA) {
 | 
			
		||||
            prealloc = PREALLOC_MODE_OFF;
 | 
			
		||||
        }
 | 
			
		||||
        ret = bdrv_co_truncate(s->data_file, offset, prealloc, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* write updated header.size */
 | 
			
		||||
    offset = cpu_to_be64(offset);
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
 | 
			
		||||
@@ -4091,20 +3901,17 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    int ret;
 | 
			
		||||
    size_t out_len;
 | 
			
		||||
    uint8_t *buf, *out_buf;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    int64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    if (bytes == 0) {
 | 
			
		||||
        /* align end of file to a sector boundary to ease reading with
 | 
			
		||||
           sector based I/Os */
 | 
			
		||||
        int64_t len = bdrv_getlength(bs->file->bs);
 | 
			
		||||
        if (len < 0) {
 | 
			
		||||
            return len;
 | 
			
		||||
        cluster_offset = bdrv_getlength(bs->file->bs);
 | 
			
		||||
        if (cluster_offset < 0) {
 | 
			
		||||
            return cluster_offset;
 | 
			
		||||
        }
 | 
			
		||||
        return bdrv_co_truncate(bs->file, len, PREALLOC_MODE_OFF, NULL);
 | 
			
		||||
        return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF,
 | 
			
		||||
                                NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (offset_into_cluster(s, offset)) {
 | 
			
		||||
@@ -4141,14 +3948,16 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
    ret = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len,
 | 
			
		||||
                                                &cluster_offset);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
    cluster_offset =
 | 
			
		||||
        qcow2_alloc_compressed_cluster_offset(bs, offset, out_len);
 | 
			
		||||
    if (!cluster_offset) {
 | 
			
		||||
        qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    cluster_offset &= s->cluster_offset_mask;
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len, true);
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
 | 
			
		||||
    qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
@@ -4156,8 +3965,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED);
 | 
			
		||||
    ret = bdrv_co_pwritev(s->data_file, cluster_offset, out_len, &hd_qiov, 0);
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
 | 
			
		||||
    ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -4670,10 +4479,6 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
 | 
			
		||||
            .refcount_bits      = s->refcount_bits,
 | 
			
		||||
            .has_bitmaps        = !!bitmaps,
 | 
			
		||||
            .bitmaps            = bitmaps,
 | 
			
		||||
            .has_data_file      = !!s->image_data_file,
 | 
			
		||||
            .data_file          = g_strdup(s->image_data_file),
 | 
			
		||||
            .has_data_file_raw  = has_data_file(bs),
 | 
			
		||||
            .data_file_raw      = data_file_is_raw(bs),
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        /* if this assertion fails, this probably means a new version was
 | 
			
		||||
@@ -4750,11 +4555,6 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (has_data_file(bs)) {
 | 
			
		||||
        error_setg(errp, "Cannot downgrade an image with a data file");
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* clear incompatible features */
 | 
			
		||||
    if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
 | 
			
		||||
        ret = qcow2_mark_clean(bs);
 | 
			
		||||
@@ -4876,9 +4676,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int old_version = s->qcow_version, new_version = old_version;
 | 
			
		||||
    uint64_t new_size = 0;
 | 
			
		||||
    const char *backing_file = NULL, *backing_format = NULL, *data_file = NULL;
 | 
			
		||||
    const char *backing_file = NULL, *backing_format = NULL;
 | 
			
		||||
    bool lazy_refcounts = s->use_lazy_refcounts;
 | 
			
		||||
    bool data_file_raw = data_file_is_raw(bs);
 | 
			
		||||
    const char *compat = NULL;
 | 
			
		||||
    uint64_t cluster_size = s->cluster_size;
 | 
			
		||||
    bool encrypt;
 | 
			
		||||
@@ -4959,21 +4758,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
                           "may not exceed 64 bits");
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE)) {
 | 
			
		||||
            data_file = qemu_opt_get(opts, BLOCK_OPT_DATA_FILE);
 | 
			
		||||
            if (data_file && !has_data_file(bs)) {
 | 
			
		||||
                error_setg(errp, "data-file can only be set for images that "
 | 
			
		||||
                                 "use an external data file");
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE_RAW)) {
 | 
			
		||||
            data_file_raw = qemu_opt_get_bool(opts, BLOCK_OPT_DATA_FILE_RAW,
 | 
			
		||||
                                              data_file_raw);
 | 
			
		||||
            if (data_file_raw && !data_file_is_raw(bs)) {
 | 
			
		||||
                error_setg(errp, "data-file-raw cannot be set on existing "
 | 
			
		||||
                                 "images");
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            /* if this point is reached, this probably means a new option was
 | 
			
		||||
             * added without having it covered here */
 | 
			
		||||
@@ -5020,24 +4804,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* data-file-raw blocks backing files, so clear it first if requested */
 | 
			
		||||
    if (data_file_raw) {
 | 
			
		||||
        s->autoclear_features |= QCOW2_AUTOCLEAR_DATA_FILE_RAW;
 | 
			
		||||
    } else {
 | 
			
		||||
        s->autoclear_features &= ~QCOW2_AUTOCLEAR_DATA_FILE_RAW;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (data_file) {
 | 
			
		||||
        g_free(s->image_data_file);
 | 
			
		||||
        s->image_data_file = *data_file ? g_strdup(data_file) : NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_update_header(bs);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Failed to update the image header");
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (backing_file || backing_format) {
 | 
			
		||||
        ret = qcow2_change_backing_file(bs,
 | 
			
		||||
                    backing_file ?: s->image_backing_file,
 | 
			
		||||
@@ -5185,16 +4951,6 @@ static QemuOptsList qcow2_create_opts = {
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "Image format of the base image"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_DATA_FILE,
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "File name of an external data file"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_DATA_FILE_RAW,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "The external data file must stay valid as a raw image"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_ENCRYPT,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
@@ -5297,7 +5053,6 @@ BlockDriver bdrv_qcow2 = {
 | 
			
		||||
 | 
			
		||||
    .create_opts         = &qcow2_create_opts,
 | 
			
		||||
    .strong_runtime_opts = qcow2_strong_runtime_opts,
 | 
			
		||||
    .mutable_opts        = mutable_opts,
 | 
			
		||||
    .bdrv_co_check       = qcow2_co_check,
 | 
			
		||||
    .bdrv_amend_options  = qcow2_amend_options,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,6 @@
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_CLUSTER_SIZE 65536
 | 
			
		||||
 | 
			
		||||
#define QCOW2_OPT_DATA_FILE "data-file"
 | 
			
		||||
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
 | 
			
		||||
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
 | 
			
		||||
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
 | 
			
		||||
@@ -200,14 +199,11 @@ enum {
 | 
			
		||||
enum {
 | 
			
		||||
    QCOW2_INCOMPAT_DIRTY_BITNR   = 0,
 | 
			
		||||
    QCOW2_INCOMPAT_CORRUPT_BITNR = 1,
 | 
			
		||||
    QCOW2_INCOMPAT_DATA_FILE_BITNR  = 2,
 | 
			
		||||
    QCOW2_INCOMPAT_DIRTY         = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
 | 
			
		||||
    QCOW2_INCOMPAT_CORRUPT       = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR,
 | 
			
		||||
    QCOW2_INCOMPAT_DATA_FILE        = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR,
 | 
			
		||||
 | 
			
		||||
    QCOW2_INCOMPAT_MASK          = QCOW2_INCOMPAT_DIRTY
 | 
			
		||||
                                    | QCOW2_INCOMPAT_CORRUPT
 | 
			
		||||
                                    | QCOW2_INCOMPAT_DATA_FILE,
 | 
			
		||||
                                 | QCOW2_INCOMPAT_CORRUPT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Compatible feature bits */
 | 
			
		||||
@@ -221,12 +217,9 @@ enum {
 | 
			
		||||
/* Autoclear feature bits */
 | 
			
		||||
enum {
 | 
			
		||||
    QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0,
 | 
			
		||||
    QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR = 1,
 | 
			
		||||
    QCOW2_AUTOCLEAR_BITMAPS       = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR,
 | 
			
		||||
    QCOW2_AUTOCLEAR_DATA_FILE_RAW       = 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR,
 | 
			
		||||
 | 
			
		||||
    QCOW2_AUTOCLEAR_MASK                = QCOW2_AUTOCLEAR_BITMAPS
 | 
			
		||||
                                        | QCOW2_AUTOCLEAR_DATA_FILE_RAW,
 | 
			
		||||
    QCOW2_AUTOCLEAR_MASK          = QCOW2_AUTOCLEAR_BITMAPS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum qcow2_discard_type {
 | 
			
		||||
@@ -344,12 +337,9 @@ typedef struct BDRVQcow2State {
 | 
			
		||||
     * override) */
 | 
			
		||||
    char *image_backing_file;
 | 
			
		||||
    char *image_backing_format;
 | 
			
		||||
    char *image_data_file;
 | 
			
		||||
 | 
			
		||||
    CoQueue compress_wait_queue;
 | 
			
		||||
    int nb_compress_threads;
 | 
			
		||||
 | 
			
		||||
    BdrvChild *data_file;
 | 
			
		||||
} BDRVQcow2State;
 | 
			
		||||
 | 
			
		||||
typedef struct Qcow2COWRegion {
 | 
			
		||||
@@ -467,20 +457,6 @@ typedef enum QCow2MetadataOverlap {
 | 
			
		||||
 | 
			
		||||
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
 | 
			
		||||
 | 
			
		||||
#define INV_OFFSET (-1ULL)
 | 
			
		||||
 | 
			
		||||
static inline bool has_data_file(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    return (s->data_file != bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool data_file_is_raw(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    return !!(s->autoclear_features & QCOW2_AUTOCLEAR_DATA_FILE_RAW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset)
 | 
			
		||||
{
 | 
			
		||||
    return offset & ~(s->cluster_size - 1);
 | 
			
		||||
@@ -522,8 +498,7 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
 | 
			
		||||
    return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs,
 | 
			
		||||
                                                      uint64_t l2_entry)
 | 
			
		||||
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
 | 
			
		||||
{
 | 
			
		||||
    if (l2_entry & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
        return QCOW2_CLUSTER_COMPRESSED;
 | 
			
		||||
@@ -533,15 +508,7 @@ static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs,
 | 
			
		||||
        }
 | 
			
		||||
        return QCOW2_CLUSTER_ZERO_PLAIN;
 | 
			
		||||
    } else if (!(l2_entry & L2E_OFFSET_MASK)) {
 | 
			
		||||
        /* Offset 0 generally means unallocated, but it is ambiguous with
 | 
			
		||||
         * external data files because 0 is a valid offset there. However, all
 | 
			
		||||
         * clusters in external data files always have refcount 1, so we can
 | 
			
		||||
         * rely on QCOW_OFLAG_COPIED to disambiguate. */
 | 
			
		||||
        if (has_data_file(bs) && (l2_entry & QCOW_OFLAG_COPIED)) {
 | 
			
		||||
            return QCOW2_CLUSTER_NORMAL;
 | 
			
		||||
        } else {
 | 
			
		||||
        return QCOW2_CLUSTER_UNALLOCATED;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return QCOW2_CLUSTER_NORMAL;
 | 
			
		||||
    }
 | 
			
		||||
@@ -632,7 +599,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret);
 | 
			
		||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
                                 int64_t size);
 | 
			
		||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
                                  int64_t size, bool data_file);
 | 
			
		||||
                                  int64_t size);
 | 
			
		||||
int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
                             void **refcount_table,
 | 
			
		||||
                             int64_t *refcount_table_size,
 | 
			
		||||
@@ -657,10 +624,9 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                               unsigned int *bytes, uint64_t *host_offset,
 | 
			
		||||
                               QCowL2Meta **m);
 | 
			
		||||
int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                                         uint64_t offset,
 | 
			
		||||
                                          int compressed_size,
 | 
			
		||||
                                          uint64_t *host_offset);
 | 
			
		||||
                                         int compressed_size);
 | 
			
		||||
 | 
			
		||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
 | 
			
		||||
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m);
 | 
			
		||||
@@ -723,7 +689,6 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
 | 
			
		||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
                                 Error **errp);
 | 
			
		||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
 | 
			
		||||
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp);
 | 
			
		||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
 | 
			
		||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
 | 
			
		||||
bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,6 @@ typedef struct BDRVRawState {
 | 
			
		||||
    bool has_size;
 | 
			
		||||
} BDRVRawState;
 | 
			
		||||
 | 
			
		||||
static const char *const mutable_opts[] = { "offset", "size", NULL };
 | 
			
		||||
 | 
			
		||||
static QemuOptsList raw_runtime_opts = {
 | 
			
		||||
    .name = "raw",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
 | 
			
		||||
@@ -434,7 +432,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
            bs->file->bs->supported_zero_flags);
 | 
			
		||||
 | 
			
		||||
    if (bs->probed && !bdrv_is_read_only(bs)) {
 | 
			
		||||
@@ -572,7 +570,6 @@ BlockDriver bdrv_raw = {
 | 
			
		||||
    .create_opts          = &raw_create_opts,
 | 
			
		||||
    .bdrv_has_zero_init   = &raw_has_zero_init,
 | 
			
		||||
    .strong_runtime_opts  = raw_strong_runtime_opts,
 | 
			
		||||
    .mutable_opts         = mutable_opts,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_raw_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -374,18 +374,19 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
 | 
			
		||||
        QDict *opts = qdict_new();
 | 
			
		||||
        qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
 | 
			
		||||
        reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
 | 
			
		||||
                                         opts, true);
 | 
			
		||||
                                         opts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->orig_secondary_read_only) {
 | 
			
		||||
        QDict *opts = qdict_new();
 | 
			
		||||
        qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
 | 
			
		||||
        reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
 | 
			
		||||
                                         opts, true);
 | 
			
		||||
                                         opts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (reopen_queue) {
 | 
			
		||||
        bdrv_reopen_multiple(reopen_queue, &local_err);
 | 
			
		||||
        bdrv_reopen_multiple(bdrv_get_aio_context(bs),
 | 
			
		||||
                             reopen_queue, &local_err);
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -682,7 +683,7 @@ static const char *const replication_strong_runtime_opts[] = {
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_replication = {
 | 
			
		||||
BlockDriver bdrv_replication = {
 | 
			
		||||
    .format_name                = "replication",
 | 
			
		||||
    .instance_size              = sizeof(BDRVReplicationState),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,6 @@ typedef struct StreamBlockJob {
 | 
			
		||||
    BlockdevOnError on_error;
 | 
			
		||||
    char *backing_file_str;
 | 
			
		||||
    bool bs_read_only;
 | 
			
		||||
    bool chain_frozen;
 | 
			
		||||
} StreamBlockJob;
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn stream_populate(BlockBackend *blk,
 | 
			
		||||
@@ -50,16 +49,6 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
 | 
			
		||||
    return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void stream_abort(Job *job)
 | 
			
		||||
{
 | 
			
		||||
    StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
 | 
			
		||||
 | 
			
		||||
    if (s->chain_frozen) {
 | 
			
		||||
        BlockJob *bjob = &s->common;
 | 
			
		||||
        bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int stream_prepare(Job *job)
 | 
			
		||||
{
 | 
			
		||||
    StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
 | 
			
		||||
@@ -69,9 +58,6 @@ static int stream_prepare(Job *job)
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    bdrv_unfreeze_backing_chain(bs, base);
 | 
			
		||||
    s->chain_frozen = false;
 | 
			
		||||
 | 
			
		||||
    if (bs->backing) {
 | 
			
		||||
        const char *base_id = NULL, *base_fmt = NULL;
 | 
			
		||||
        if (base) {
 | 
			
		||||
@@ -222,7 +208,6 @@ static const BlockJobDriver stream_job_driver = {
 | 
			
		||||
        .free          = block_job_free,
 | 
			
		||||
        .run           = stream_run,
 | 
			
		||||
        .prepare       = stream_prepare,
 | 
			
		||||
        .abort         = stream_abort,
 | 
			
		||||
        .clean         = stream_clean,
 | 
			
		||||
        .user_resume   = block_job_user_resume,
 | 
			
		||||
        .drain         = block_job_drain,
 | 
			
		||||
@@ -238,16 +223,11 @@ void stream_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
    BlockDriverState *iter;
 | 
			
		||||
    bool bs_read_only;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_freeze_backing_chain(bs, base, errp) < 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Make sure that the image is opened in read-write mode */
 | 
			
		||||
    bs_read_only = bdrv_is_read_only(bs);
 | 
			
		||||
    if (bs_read_only) {
 | 
			
		||||
        if (bdrv_reopen_set_read_only(bs, false, errp) != 0) {
 | 
			
		||||
            bs_read_only = false;
 | 
			
		||||
            goto fail;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -277,7 +257,6 @@ void stream_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
    s->base = base;
 | 
			
		||||
    s->backing_file_str = g_strdup(backing_file_str);
 | 
			
		||||
    s->bs_read_only = bs_read_only;
 | 
			
		||||
    s->chain_frozen = true;
 | 
			
		||||
 | 
			
		||||
    s->on_error = on_error;
 | 
			
		||||
    trace_stream_start(bs, base, s);
 | 
			
		||||
@@ -288,5 +267,4 @@ fail:
 | 
			
		||||
    if (bs_read_only) {
 | 
			
		||||
        bdrv_reopen_set_read_only(bs, true, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    bdrv_unfreeze_backing_chain(bs, base);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
 | 
			
		||||
# ../block.c
 | 
			
		||||
# block.c
 | 
			
		||||
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
 | 
			
		||||
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
 | 
			
		||||
 | 
			
		||||
# block-backend.c
 | 
			
		||||
# block/block-backend.c
 | 
			
		||||
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
 | 
			
		||||
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
 | 
			
		||||
blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p"
 | 
			
		||||
blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p"
 | 
			
		||||
 | 
			
		||||
# io.c
 | 
			
		||||
# block/io.c
 | 
			
		||||
bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
 | 
			
		||||
bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
 | 
			
		||||
bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x"
 | 
			
		||||
@@ -18,15 +18,15 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t c
 | 
			
		||||
bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x"
 | 
			
		||||
bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x"
 | 
			
		||||
 | 
			
		||||
# stream.c
 | 
			
		||||
# block/stream.c
 | 
			
		||||
stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
 | 
			
		||||
stream_start(void *bs, void *base, void *s) "bs %p base %p s %p"
 | 
			
		||||
 | 
			
		||||
# commit.c
 | 
			
		||||
# block/commit.c
 | 
			
		||||
commit_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
 | 
			
		||||
commit_start(void *bs, void *base, void *top, void *s) "bs %p base %p top %p s %p"
 | 
			
		||||
 | 
			
		||||
# mirror.c
 | 
			
		||||
# block/mirror.c
 | 
			
		||||
mirror_start(void *bs, void *s, void *opaque) "bs %p s %p opaque %p"
 | 
			
		||||
mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64
 | 
			
		||||
mirror_before_flush(void *s) "s %p"
 | 
			
		||||
@@ -37,7 +37,7 @@ mirror_iteration_done(void *s, int64_t offset, uint64_t bytes, int ret) "s %p of
 | 
			
		||||
mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d"
 | 
			
		||||
mirror_yield_in_flight(void *s, int64_t offset, int in_flight) "s %p offset %" PRId64 " in_flight %d"
 | 
			
		||||
 | 
			
		||||
# backup.c
 | 
			
		||||
# block/backup.c
 | 
			
		||||
backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64
 | 
			
		||||
backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d"
 | 
			
		||||
backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
 | 
			
		||||
@@ -46,7 +46,7 @@ backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId6
 | 
			
		||||
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
 | 
			
		||||
backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
 | 
			
		||||
 | 
			
		||||
# ../blockdev.c
 | 
			
		||||
# blockdev.c
 | 
			
		||||
qmp_block_job_cancel(void *job) "job %p"
 | 
			
		||||
qmp_block_job_pause(void *job) "job %p"
 | 
			
		||||
qmp_block_job_resume(void *job) "job %p"
 | 
			
		||||
@@ -55,12 +55,13 @@ qmp_block_job_finalize(void *job) "job %p"
 | 
			
		||||
qmp_block_job_dismiss(void *job) "job %p"
 | 
			
		||||
qmp_block_stream(void *bs, void *job) "bs %p job %p"
 | 
			
		||||
 | 
			
		||||
# file-posix.c
 | 
			
		||||
# file-win32.c
 | 
			
		||||
# block/file-win32.c
 | 
			
		||||
# block/file-posix.c
 | 
			
		||||
file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d"
 | 
			
		||||
file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
 | 
			
		||||
file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64
 | 
			
		||||
 | 
			
		||||
# qcow2.c
 | 
			
		||||
# block/qcow2.c
 | 
			
		||||
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
 | 
			
		||||
qcow2_writev_done_req(void *co, int ret) "co %p ret %d"
 | 
			
		||||
qcow2_writev_start_part(void *co) "co %p"
 | 
			
		||||
@@ -69,7 +70,7 @@ qcow2_writev_data(void *co, uint64_t offset) "co %p offset 0x%" PRIx64
 | 
			
		||||
qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int count) "co %p offset 0x%" PRIx64 " count %d"
 | 
			
		||||
qcow2_pwrite_zeroes(void *co, int64_t offset, int count) "co %p offset 0x%" PRIx64 " count %d"
 | 
			
		||||
 | 
			
		||||
# qcow2-cluster.c
 | 
			
		||||
# block/qcow2-cluster.c
 | 
			
		||||
qcow2_alloc_clusters_offset(void *co, uint64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
 | 
			
		||||
qcow2_handle_copied(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset 0x%" PRIx64 " host_offset 0x%" PRIx64 " bytes 0x%" PRIx64
 | 
			
		||||
qcow2_handle_alloc(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset 0x%" PRIx64 " host_offset 0x%" PRIx64 " bytes 0x%" PRIx64
 | 
			
		||||
@@ -83,7 +84,7 @@ qcow2_l2_allocate_write_l2(void *bs, int l1_index) "bs %p l1_index %d"
 | 
			
		||||
qcow2_l2_allocate_write_l1(void *bs, int l1_index) "bs %p l1_index %d"
 | 
			
		||||
qcow2_l2_allocate_done(void *bs, int l1_index, int ret) "bs %p l1_index %d ret %d"
 | 
			
		||||
 | 
			
		||||
# qcow2-cache.c
 | 
			
		||||
# block/qcow2-cache.c
 | 
			
		||||
qcow2_cache_get(void *co, int c, uint64_t offset, bool read_from_disk) "co %p is_l2_cache %d offset 0x%" PRIx64 " read_from_disk %d"
 | 
			
		||||
qcow2_cache_get_replace_entry(void *co, int c, int i) "co %p is_l2_cache %d index %d"
 | 
			
		||||
qcow2_cache_get_read(void *co, int c, int i) "co %p is_l2_cache %d index %d"
 | 
			
		||||
@@ -91,18 +92,18 @@ qcow2_cache_get_done(void *co, int c, int i) "co %p is_l2_cache %d index %d"
 | 
			
		||||
qcow2_cache_flush(void *co, int c) "co %p is_l2_cache %d"
 | 
			
		||||
qcow2_cache_entry_flush(void *co, int c, int i) "co %p is_l2_cache %d index %d"
 | 
			
		||||
 | 
			
		||||
# qed-l2-cache.c
 | 
			
		||||
# block/qed-l2-cache.c
 | 
			
		||||
qed_alloc_l2_cache_entry(void *l2_cache, void *entry) "l2_cache %p entry %p"
 | 
			
		||||
qed_unref_l2_cache_entry(void *entry, int ref) "entry %p ref %d"
 | 
			
		||||
qed_find_l2_cache_entry(void *l2_cache, void *entry, uint64_t offset, int ref) "l2_cache %p entry %p offset %"PRIu64" ref %d"
 | 
			
		||||
 | 
			
		||||
# qed-table.c
 | 
			
		||||
# block/qed-table.c
 | 
			
		||||
qed_read_table(void *s, uint64_t offset, void *table) "s %p offset %"PRIu64" table %p"
 | 
			
		||||
qed_read_table_cb(void *s, void *table, int ret) "s %p table %p ret %d"
 | 
			
		||||
qed_write_table(void *s, uint64_t offset, void *table, unsigned int index, unsigned int n) "s %p offset %"PRIu64" table %p index %u n %u"
 | 
			
		||||
qed_write_table_cb(void *s, void *table, int flush, int ret) "s %p table %p flush %d ret %d"
 | 
			
		||||
 | 
			
		||||
# qed.c
 | 
			
		||||
# block/qed.c
 | 
			
		||||
qed_need_check_timer_cb(void *s) "s %p"
 | 
			
		||||
qed_start_need_check_timer(void *s) "s %p"
 | 
			
		||||
qed_cancel_need_check_timer(void *s) "s %p"
 | 
			
		||||
@@ -115,7 +116,7 @@ qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t o
 | 
			
		||||
qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
 | 
			
		||||
qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
 | 
			
		||||
 | 
			
		||||
# vxhs.c
 | 
			
		||||
# block/vxhs.c
 | 
			
		||||
vxhs_iio_callback(int error) "ctx is NULL: error %d"
 | 
			
		||||
vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d"
 | 
			
		||||
vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d"
 | 
			
		||||
@@ -132,7 +133,7 @@ vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d"
 | 
			
		||||
vxhs_close(char *vdisk_guid) "Closing vdisk %s"
 | 
			
		||||
vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s"
 | 
			
		||||
 | 
			
		||||
# nvme.c
 | 
			
		||||
# block/nvme.c
 | 
			
		||||
nvme_kick(void *s, int queue) "s %p queue %d"
 | 
			
		||||
nvme_dma_flush_queue_wait(void *s) "s %p"
 | 
			
		||||
nvme_error(int cmd_specific, int sq_head, int sqid, int cid, int status) "cmd_specific %d sq_head %d sqid %d cid %d status 0x%x"
 | 
			
		||||
@@ -153,16 +154,14 @@ nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p
 | 
			
		||||
nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
 | 
			
		||||
nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d"
 | 
			
		||||
 | 
			
		||||
# iscsi.c
 | 
			
		||||
# block/iscsi.c
 | 
			
		||||
iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d"
 | 
			
		||||
 | 
			
		||||
# nbd-client.c
 | 
			
		||||
nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s"
 | 
			
		||||
nbd_structured_read_compliance(const char *type) "server sent non-compliant unaligned read %s chunk"
 | 
			
		||||
# block/nbd-client.c
 | 
			
		||||
nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s"
 | 
			
		||||
nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s"
 | 
			
		||||
 | 
			
		||||
# ssh.c
 | 
			
		||||
# block/ssh.c
 | 
			
		||||
ssh_restart_coroutine(void *co) "co=%p"
 | 
			
		||||
ssh_flush(void) "fsync"
 | 
			
		||||
ssh_check_host_key_knownhosts(const char *key) "host key OK: %s"
 | 
			
		||||
@@ -179,7 +178,7 @@ ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu"
 | 
			
		||||
ssh_write_return(ssize_t ret) "sftp_write returned %zd"
 | 
			
		||||
ssh_seek(int64_t offset) "seeking to offset=%" PRIi64
 | 
			
		||||
 | 
			
		||||
# curl.c
 | 
			
		||||
# block/curl.c
 | 
			
		||||
curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld"
 | 
			
		||||
curl_sock_cb(int action, int fd) "sock action %d on fd %d"
 | 
			
		||||
curl_read_cb(size_t realsize) "just reading %zu bytes"
 | 
			
		||||
@@ -188,14 +187,14 @@ curl_open_size(uint64_t size) "size = %" PRIu64
 | 
			
		||||
curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "reading %" PRIu64 " at %" PRIu64 " (%s)"
 | 
			
		||||
curl_close(void) "close"
 | 
			
		||||
 | 
			
		||||
# file-posix.c
 | 
			
		||||
# block/file-posix.c
 | 
			
		||||
file_xfs_write_zeroes(const char *error) "cannot write zero range (%s)"
 | 
			
		||||
file_xfs_discard(const char *error) "cannot punch hole (%s)"
 | 
			
		||||
file_FindEjectableOpticalMedia(const char *media) "Matching using %s"
 | 
			
		||||
file_setup_cdrom(const char *partition) "Using %s as optical disc"
 | 
			
		||||
file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d"
 | 
			
		||||
 | 
			
		||||
# sheepdog.c
 | 
			
		||||
# block/sheepdog.c
 | 
			
		||||
sheepdog_reconnect_to_sdog(void) "Wait for connection to be established"
 | 
			
		||||
sheepdog_aio_read_response(void) "disable cache since the server doesn't support it"
 | 
			
		||||
sheepdog_open(uint32_t vid) "0x%" PRIx32 " snapshot inode was open"
 | 
			
		||||
 
 | 
			
		||||
@@ -195,15 +195,13 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
            }
 | 
			
		||||
            if (end - p >= strlen("version=X\n")) {
 | 
			
		||||
                if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 ||
 | 
			
		||||
                    strncmp("version=2\n", p, strlen("version=2\n")) == 0 ||
 | 
			
		||||
                    strncmp("version=3\n", p, strlen("version=3\n")) == 0) {
 | 
			
		||||
                    strncmp("version=2\n", p, strlen("version=2\n")) == 0) {
 | 
			
		||||
                    return 100;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (end - p >= strlen("version=X\r\n")) {
 | 
			
		||||
                if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 ||
 | 
			
		||||
                    strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0 ||
 | 
			
		||||
                    strncmp("version=3\r\n", p, strlen("version=3\r\n")) == 0) {
 | 
			
		||||
                    strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) {
 | 
			
		||||
                    return 100;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@
 | 
			
		||||
typedef struct NBDServerData {
 | 
			
		||||
    QIONetListener *listener;
 | 
			
		||||
    QCryptoTLSCreds *tlscreds;
 | 
			
		||||
    char *tlsauthz;
 | 
			
		||||
} NBDServerData;
 | 
			
		||||
 | 
			
		||||
static NBDServerData *nbd_server;
 | 
			
		||||
@@ -37,7 +36,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
 | 
			
		||||
                       gpointer opaque)
 | 
			
		||||
{
 | 
			
		||||
    qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
 | 
			
		||||
    nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
 | 
			
		||||
    nbd_client_new(cioc, nbd_server->tlscreds, NULL,
 | 
			
		||||
                   nbd_blockdev_client_closed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +52,6 @@ static void nbd_server_free(NBDServerData *server)
 | 
			
		||||
    if (server->tlscreds) {
 | 
			
		||||
        object_unref(OBJECT(server->tlscreds));
 | 
			
		||||
    }
 | 
			
		||||
    g_free(server->tlsauthz);
 | 
			
		||||
 | 
			
		||||
    g_free(server);
 | 
			
		||||
}
 | 
			
		||||
@@ -89,7 +87,7 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
 | 
			
		||||
                      const char *tls_authz, Error **errp)
 | 
			
		||||
                      Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    if (nbd_server) {
 | 
			
		||||
        error_setg(errp, "NBD server already running");
 | 
			
		||||
@@ -119,8 +117,6 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nbd_server->tlsauthz = g_strdup(tls_authz);
 | 
			
		||||
 | 
			
		||||
    qio_net_listener_set_client_func(nbd_server->listener,
 | 
			
		||||
                                     nbd_accept,
 | 
			
		||||
                                     NULL,
 | 
			
		||||
@@ -135,12 +131,11 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
 | 
			
		||||
 | 
			
		||||
void qmp_nbd_server_start(SocketAddressLegacy *addr,
 | 
			
		||||
                          bool has_tls_creds, const char *tls_creds,
 | 
			
		||||
                          bool has_tls_authz, const char *tls_authz,
 | 
			
		||||
                          Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    SocketAddress *addr_flat = socket_address_flatten(addr);
 | 
			
		||||
 | 
			
		||||
    nbd_server_start(addr_flat, tls_creds, tls_authz, errp);
 | 
			
		||||
    nbd_server_start(addr_flat, tls_creds, errp);
 | 
			
		||||
    qapi_free_SocketAddress(addr_flat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										175
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								blockdev.c
									
									
									
									
									
								
							@@ -531,9 +531,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "format")) != NULL) {
 | 
			
		||||
        if (is_help_option(buf)) {
 | 
			
		||||
            error_printf("Supported formats:");
 | 
			
		||||
            bdrv_iterate_format(bdrv_format_print, NULL, false);
 | 
			
		||||
            error_printf("\nSupported formats (read-only):");
 | 
			
		||||
            bdrv_iterate_format(bdrv_format_print, NULL, true);
 | 
			
		||||
            bdrv_iterate_format(bdrv_format_print, NULL);
 | 
			
		||||
            error_printf("\n");
 | 
			
		||||
            goto early_err;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1257,6 +1255,7 @@ out_aio_context:
 | 
			
		||||
 * @node: The name of the BDS node to search for bitmaps
 | 
			
		||||
 * @name: The name of the bitmap to search for
 | 
			
		||||
 * @pbs: Output pointer for BDS lookup, if desired. Can be NULL.
 | 
			
		||||
 * @paio: Output pointer for aio_context acquisition, if desired. Can be NULL.
 | 
			
		||||
 * @errp: Output pointer for error information. Can be NULL.
 | 
			
		||||
 *
 | 
			
		||||
 * @return: A bitmap object on success, or NULL on failure.
 | 
			
		||||
@@ -2010,7 +2009,11 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
 | 
			
		||||
        error_setg(errp, "Cannot modify a bitmap in use by another operation");
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
 | 
			
		||||
        error_setg(errp, "Cannot clear a readonly bitmap");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2055,7 +2058,10 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                   " and cannot be enabled", action->name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2093,7 +2099,10 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                   " and cannot be disabled", action->name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2864,7 +2873,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
 | 
			
		||||
        bdrv_disable_dirty_bitmap(bitmap);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_dirty_bitmap_set_persistence(bitmap, persistent);
 | 
			
		||||
    bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
 | 
			
		||||
 out:
 | 
			
		||||
    if (aio_context) {
 | 
			
		||||
        aio_context_release(aio_context);
 | 
			
		||||
@@ -2884,12 +2893,14 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO,
 | 
			
		||||
                                errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(bitmap)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Bitmap '%s' is currently in use by another operation and"
 | 
			
		||||
                   " cannot be removed", name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
 | 
			
		||||
        aio_context = bdrv_get_aio_context(bs);
 | 
			
		||||
        aio_context_acquire(aio_context);
 | 
			
		||||
        bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err);
 | 
			
		||||
@@ -2921,7 +2932,13 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(bitmap)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                   " and cannot be cleared", name);
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (bdrv_dirty_bitmap_readonly(bitmap)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2939,7 +2956,10 @@ void qmp_block_dirty_bitmap_enable(const char *node, const char *name,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(bitmap)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                   " and cannot be enabled", name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2957,7 +2977,10 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(bitmap)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                   " and cannot be disabled", name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -3536,7 +3559,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
 | 
			
		||||
            bdrv_unref(target_bs);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) {
 | 
			
		||||
        if (bdrv_dirty_bitmap_user_locked(bmap)) {
 | 
			
		||||
            error_setg(errp,
 | 
			
		||||
                       "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                       " and cannot be used for backup", backup->bitmap);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -3646,7 +3672,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
 | 
			
		||||
            error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) {
 | 
			
		||||
        if (bdrv_dirty_bitmap_user_locked(bmap)) {
 | 
			
		||||
            error_setg(errp,
 | 
			
		||||
                       "Bitmap '%s' is currently in use by another operation"
 | 
			
		||||
                       " and cannot be used for backup", backup->bitmap);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -3756,39 +3785,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
        sync = MIRROR_SYNC_MODE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (has_replaces) {
 | 
			
		||||
        BlockDriverState *to_replace_bs;
 | 
			
		||||
        AioContext *replace_aio_context;
 | 
			
		||||
        int64_t bs_size, replace_size;
 | 
			
		||||
 | 
			
		||||
        bs_size = bdrv_getlength(bs);
 | 
			
		||||
        if (bs_size < 0) {
 | 
			
		||||
            error_setg_errno(errp, -bs_size, "Failed to query device's size");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        to_replace_bs = check_to_replace_node(bs, replaces, errp);
 | 
			
		||||
        if (!to_replace_bs) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        replace_aio_context = bdrv_get_aio_context(to_replace_bs);
 | 
			
		||||
        aio_context_acquire(replace_aio_context);
 | 
			
		||||
        replace_size = bdrv_getlength(to_replace_bs);
 | 
			
		||||
        aio_context_release(replace_aio_context);
 | 
			
		||||
 | 
			
		||||
        if (replace_size < 0) {
 | 
			
		||||
            error_setg_errno(errp, -replace_size,
 | 
			
		||||
                             "Failed to query the replacement node's size");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (bs_size != replace_size) {
 | 
			
		||||
            error_setg(errp, "cannot replace image with a mirror image of "
 | 
			
		||||
                             "different size");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* pass the node name to replace to mirror start since it's loose coupling
 | 
			
		||||
     * and will allow to check whether the node still exist at mirror completion
 | 
			
		||||
     */
 | 
			
		||||
@@ -3849,11 +3845,33 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (arg->has_replaces) {
 | 
			
		||||
        BlockDriverState *to_replace_bs;
 | 
			
		||||
        AioContext *replace_aio_context;
 | 
			
		||||
        int64_t replace_size;
 | 
			
		||||
 | 
			
		||||
        if (!arg->has_node_name) {
 | 
			
		||||
            error_setg(errp, "a node-name must be provided when replacing a"
 | 
			
		||||
                             " named node of the graph");
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err);
 | 
			
		||||
 | 
			
		||||
        if (!to_replace_bs) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        replace_aio_context = bdrv_get_aio_context(to_replace_bs);
 | 
			
		||||
        aio_context_acquire(replace_aio_context);
 | 
			
		||||
        replace_size = bdrv_getlength(to_replace_bs);
 | 
			
		||||
        aio_context_release(replace_aio_context);
 | 
			
		||||
 | 
			
		||||
        if (size != replace_size) {
 | 
			
		||||
            error_setg(errp, "cannot replace image with a mirror image of "
 | 
			
		||||
                             "different size");
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
 | 
			
		||||
@@ -4267,53 +4285,6 @@ fail:
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    AioContext *ctx;
 | 
			
		||||
    QObject *obj;
 | 
			
		||||
    Visitor *v = qobject_output_visitor_new(&obj);
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    BlockReopenQueue *queue;
 | 
			
		||||
    QDict *qdict;
 | 
			
		||||
 | 
			
		||||
    /* Check for the selected node name */
 | 
			
		||||
    if (!options->has_node_name) {
 | 
			
		||||
        error_setg(errp, "Node name not specified");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_find_node(options->node_name);
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        error_setg(errp, "Cannot find node named '%s'", options->node_name);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Put all options in a QDict and flatten it */
 | 
			
		||||
    visit_type_BlockdevOptions(v, NULL, &options, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_complete(v, &obj);
 | 
			
		||||
    qdict = qobject_to(QDict, obj);
 | 
			
		||||
 | 
			
		||||
    qdict_flatten(qdict);
 | 
			
		||||
 | 
			
		||||
    /* Perform the reopen operation */
 | 
			
		||||
    ctx = bdrv_get_aio_context(bs);
 | 
			
		||||
    aio_context_acquire(ctx);
 | 
			
		||||
    bdrv_subtree_drained_begin(bs);
 | 
			
		||||
    queue = bdrv_reopen_queue(NULL, bs, qdict, false);
 | 
			
		||||
    bdrv_reopen_multiple(queue, errp);
 | 
			
		||||
    bdrv_subtree_drained_end(bs);
 | 
			
		||||
    aio_context_release(ctx);
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmp_blockdev_del(const char *node_name, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    AioContext *aio_context;
 | 
			
		||||
@@ -4479,22 +4450,22 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
 | 
			
		||||
    aio_context_release(old_context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmp_block_latency_histogram_set(
 | 
			
		||||
    const char *id,
 | 
			
		||||
void qmp_x_block_latency_histogram_set(
 | 
			
		||||
    const char *device,
 | 
			
		||||
    bool has_boundaries, uint64List *boundaries,
 | 
			
		||||
    bool has_boundaries_read, uint64List *boundaries_read,
 | 
			
		||||
    bool has_boundaries_write, uint64List *boundaries_write,
 | 
			
		||||
    bool has_boundaries_flush, uint64List *boundaries_flush,
 | 
			
		||||
    Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackend *blk = qmp_get_blk(NULL, id, errp);
 | 
			
		||||
    BlockBackend *blk = blk_by_name(device);
 | 
			
		||||
    BlockAcctStats *stats;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (!blk) {
 | 
			
		||||
        error_setg(errp, "Device '%s' not found", device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stats = blk_get_stats(blk);
 | 
			
		||||
 | 
			
		||||
    if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
 | 
			
		||||
@@ -4509,7 +4480,7 @@ void qmp_block_latency_histogram_set(
 | 
			
		||||
            stats, BLOCK_ACCT_READ,
 | 
			
		||||
            has_boundaries_read ? boundaries_read : boundaries);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            error_setg(errp, "Device '%s' set read boundaries fail", id);
 | 
			
		||||
            error_setg(errp, "Device '%s' set read boundaries fail", device);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -4519,7 +4490,7 @@ void qmp_block_latency_histogram_set(
 | 
			
		||||
            stats, BLOCK_ACCT_WRITE,
 | 
			
		||||
            has_boundaries_write ? boundaries_write : boundaries);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            error_setg(errp, "Device '%s' set write boundaries fail", id);
 | 
			
		||||
            error_setg(errp, "Device '%s' set write boundaries fail", device);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -4529,7 +4500,7 @@ void qmp_block_latency_histogram_set(
 | 
			
		||||
            stats, BLOCK_ACCT_FLUSH,
 | 
			
		||||
            has_boundaries_flush ? boundaries_flush : boundaries);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            error_setg(errp, "Device '%s' set flush boundaries fail", id);
 | 
			
		||||
            error_setg(errp, "Device '%s' set flush boundaries fail", device);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -501,11 +501,9 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
 | 
			
		||||
                                        action);
 | 
			
		||||
    }
 | 
			
		||||
    if (action == BLOCK_ERROR_ACTION_STOP) {
 | 
			
		||||
        if (!job->job.user_paused) {
 | 
			
		||||
        job_pause(&job->job);
 | 
			
		||||
        /* make the pause user visible, which will be resumed from QMP. */
 | 
			
		||||
        job->job.user_paused = true;
 | 
			
		||||
        }
 | 
			
		||||
        block_job_iostatus_set_err(job, error);
 | 
			
		||||
    }
 | 
			
		||||
    return action;
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,6 @@ typedef struct {
 | 
			
		||||
    QIONetListener *listener;
 | 
			
		||||
    GSource *hup_source;
 | 
			
		||||
    QCryptoTLSCreds *tls_creds;
 | 
			
		||||
    char *tls_authz;
 | 
			
		||||
    TCPChardevState state;
 | 
			
		||||
    int max_size;
 | 
			
		||||
    int do_telnetopt;
 | 
			
		||||
@@ -633,7 +632,7 @@ static void tcp_chr_update_read_handler(Chardev *chr)
 | 
			
		||||
{
 | 
			
		||||
    SocketChardev *s = SOCKET_CHARDEV(chr);
 | 
			
		||||
 | 
			
		||||
    if (s->listener && s->state == TCP_CHARDEV_STATE_DISCONNECTED) {
 | 
			
		||||
    if (s->listener) {
 | 
			
		||||
        /*
 | 
			
		||||
         * It's possible that chardev context is changed in
 | 
			
		||||
         * qemu_chr_be_update_read_handlers().  Reset it for QIO net
 | 
			
		||||
@@ -808,7 +807,7 @@ static void tcp_chr_tls_init(Chardev *chr)
 | 
			
		||||
    if (s->is_listen) {
 | 
			
		||||
        tioc = qio_channel_tls_new_server(
 | 
			
		||||
            s->ioc, s->tls_creds,
 | 
			
		||||
            s->tls_authz,
 | 
			
		||||
            NULL, /* XXX Use an ACL */
 | 
			
		||||
            &err);
 | 
			
		||||
    } else {
 | 
			
		||||
        tioc = qio_channel_tls_new_client(
 | 
			
		||||
@@ -1056,7 +1055,6 @@ static void char_socket_finalize(Object *obj)
 | 
			
		||||
    if (s->tls_creds) {
 | 
			
		||||
        object_unref(OBJECT(s->tls_creds));
 | 
			
		||||
    }
 | 
			
		||||
    g_free(s->tls_authz);
 | 
			
		||||
 | 
			
		||||
    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
			
		||||
}
 | 
			
		||||
@@ -1244,11 +1242,6 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock,
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sock->has_tls_authz && !sock->has_tls_creds) {
 | 
			
		||||
        error_setg(errp, "'tls_authz' option requires 'tls_creds' option");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Validate any options which have a dependancy on client vs server */
 | 
			
		||||
    if (!sock->has_server || sock->server) {
 | 
			
		||||
        if (sock->has_reconnect) {
 | 
			
		||||
@@ -1327,7 +1320,6 @@ static void qmp_chardev_open_socket(Chardev *chr,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    s->tls_authz = g_strdup(sock->tls_authz);
 | 
			
		||||
 | 
			
		||||
    s->addr = addr = socket_address_flatten(sock->addr);
 | 
			
		||||
 | 
			
		||||
@@ -1407,8 +1399,6 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
 | 
			
		||||
    sock->reconnect = qemu_opt_get_number(opts, "reconnect", 0);
 | 
			
		||||
    sock->has_tls_creds = qemu_opt_get(opts, "tls-creds");
 | 
			
		||||
    sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
 | 
			
		||||
    sock->has_tls_authz = qemu_opt_get(opts, "tls-authz");
 | 
			
		||||
    sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
 | 
			
		||||
 | 
			
		||||
    addr = g_new0(SocketAddressLegacy, 1);
 | 
			
		||||
    if (path) {
 | 
			
		||||
 
 | 
			
		||||
@@ -880,9 +880,6 @@ QemuOptsList qemu_chardev_opts = {
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "tls-creds",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "tls-authz",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "websocket",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
 | 
			
		||||
# wctablet.c
 | 
			
		||||
# chardev/wctablet.c
 | 
			
		||||
wct_init(void) ""
 | 
			
		||||
wct_cmd_re(void) ""
 | 
			
		||||
wct_cmd_st(void) ""
 | 
			
		||||
@@ -9,7 +9,7 @@ wct_cmd_ts(int input) "0x%02x"
 | 
			
		||||
wct_cmd_other(const char *cmd) "%s"
 | 
			
		||||
wct_speed(int speed) "%d"
 | 
			
		||||
 | 
			
		||||
# spice.c
 | 
			
		||||
# chardev/spice.c
 | 
			
		||||
spice_chr_discard_write(int len) "spice chr write discarded %d"
 | 
			
		||||
spice_vmc_write(ssize_t out, int len) "spice wrote %zd of requested %d"
 | 
			
		||||
spice_vmc_read(int bytes, int len) "spice read %d of requested %d"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										368
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										368
									
								
								configure
									
									
									
									
										vendored
									
									
								
							@@ -327,7 +327,6 @@ git="git"
 | 
			
		||||
 | 
			
		||||
# Don't accept a target_list environment variable.
 | 
			
		||||
unset target_list
 | 
			
		||||
unset target_list_exclude
 | 
			
		||||
 | 
			
		||||
# Default value for a variable defining feature "foo".
 | 
			
		||||
#  * foo="no"  feature will only be used if --enable-foo arg is given
 | 
			
		||||
@@ -369,10 +368,10 @@ libattr=""
 | 
			
		||||
xfs=""
 | 
			
		||||
tcg="yes"
 | 
			
		||||
membarrier=""
 | 
			
		||||
vhost_net=""
 | 
			
		||||
vhost_crypto=""
 | 
			
		||||
vhost_scsi=""
 | 
			
		||||
vhost_vsock=""
 | 
			
		||||
vhost_net="no"
 | 
			
		||||
vhost_crypto="no"
 | 
			
		||||
vhost_scsi="no"
 | 
			
		||||
vhost_vsock="no"
 | 
			
		||||
vhost_user=""
 | 
			
		||||
kvm="no"
 | 
			
		||||
hax="no"
 | 
			
		||||
@@ -407,7 +406,7 @@ includedir="\${prefix}/include"
 | 
			
		||||
sysconfdir="\${prefix}/etc"
 | 
			
		||||
local_statedir="\${prefix}/var"
 | 
			
		||||
confsuffix="/qemu"
 | 
			
		||||
slirp=""
 | 
			
		||||
slirp="yes"
 | 
			
		||||
oss_lib=""
 | 
			
		||||
bsd="no"
 | 
			
		||||
linux="no"
 | 
			
		||||
@@ -457,8 +456,6 @@ glusterfs_xlator_opt="no"
 | 
			
		||||
glusterfs_discard="no"
 | 
			
		||||
glusterfs_fallocate="no"
 | 
			
		||||
glusterfs_zerofill="no"
 | 
			
		||||
glusterfs_ftruncate_has_stat="no"
 | 
			
		||||
glusterfs_iocb_has_stat="no"
 | 
			
		||||
gtk=""
 | 
			
		||||
gtk_gl="no"
 | 
			
		||||
tls_priority="NORMAL"
 | 
			
		||||
@@ -469,7 +466,7 @@ gcrypt_hmac="no"
 | 
			
		||||
auth_pam=""
 | 
			
		||||
vte=""
 | 
			
		||||
virglrenderer=""
 | 
			
		||||
tpm=""
 | 
			
		||||
tpm="yes"
 | 
			
		||||
libssh2=""
 | 
			
		||||
live_block_migration="yes"
 | 
			
		||||
numa=""
 | 
			
		||||
@@ -490,7 +487,7 @@ libxml2=""
 | 
			
		||||
docker="no"
 | 
			
		||||
debug_mutex="no"
 | 
			
		||||
libpmem=""
 | 
			
		||||
default_devices="yes"
 | 
			
		||||
libudev="no"
 | 
			
		||||
 | 
			
		||||
# cross compilers defaults, can be overridden with --cross-cc-ARCH
 | 
			
		||||
cross_cc_aarch64="aarch64-linux-gnu-gcc"
 | 
			
		||||
@@ -786,7 +783,6 @@ case $targetos in
 | 
			
		||||
MINGW32*)
 | 
			
		||||
  mingw32="yes"
 | 
			
		||||
  hax="yes"
 | 
			
		||||
  vhost_user="no"
 | 
			
		||||
  audio_possible_drivers="dsound sdl"
 | 
			
		||||
  if check_include dsound.h; then
 | 
			
		||||
    audio_drv_list="dsound"
 | 
			
		||||
@@ -883,11 +879,15 @@ Haiku)
 | 
			
		||||
  LIBS="-lposix_error_mapper -lnetwork $LIBS"
 | 
			
		||||
;;
 | 
			
		||||
Linux)
 | 
			
		||||
  audio_drv_list="try-pa oss"
 | 
			
		||||
  audio_drv_list="try-pa try-alsa try-sdl oss"
 | 
			
		||||
  audio_possible_drivers="oss alsa sdl pa"
 | 
			
		||||
  linux="yes"
 | 
			
		||||
  linux_user="yes"
 | 
			
		||||
  kvm="yes"
 | 
			
		||||
  vhost_net="yes"
 | 
			
		||||
  vhost_crypto="yes"
 | 
			
		||||
  vhost_scsi="yes"
 | 
			
		||||
  vhost_vsock="yes"
 | 
			
		||||
  QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$PWD/linux-headers $QEMU_INCLUDES"
 | 
			
		||||
  supported_os="yes"
 | 
			
		||||
  libudev="yes"
 | 
			
		||||
@@ -991,14 +991,6 @@ for opt do
 | 
			
		||||
  --cpu=*)
 | 
			
		||||
  ;;
 | 
			
		||||
  --target-list=*) target_list="$optarg"
 | 
			
		||||
                   if test "$target_list_exclude"; then
 | 
			
		||||
                       error_exit "Can't mix --target-list with --target-list-exclude"
 | 
			
		||||
                   fi
 | 
			
		||||
  ;;
 | 
			
		||||
  --target-list-exclude=*) target_list_exclude="$optarg"
 | 
			
		||||
                   if test "$target_list"; then
 | 
			
		||||
                       error_exit "Can't mix --target-list-exclude with --target-list"
 | 
			
		||||
                   fi
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-trace-backends=*) trace_backends="$optarg"
 | 
			
		||||
  ;;
 | 
			
		||||
@@ -1007,10 +999,6 @@ for opt do
 | 
			
		||||
  ;;
 | 
			
		||||
  --with-trace-file=*) trace_file="$optarg"
 | 
			
		||||
  ;;
 | 
			
		||||
  --with-default-devices) default_devices="yes"
 | 
			
		||||
  ;;
 | 
			
		||||
  --without-default-devices) default_devices="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-gprof) gprof="yes"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-gcov) gcov="yes"
 | 
			
		||||
@@ -1120,8 +1108,6 @@ for opt do
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-slirp) slirp="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-slirp=system) slirp="system"
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-vde) vde="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-vde) vde="yes"
 | 
			
		||||
@@ -1231,10 +1217,6 @@ for opt do
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-curses) curses="yes"
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-iconv) iconv="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-iconv) iconv="yes"
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-curl) curl="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-curl) curl="yes"
 | 
			
		||||
@@ -1281,7 +1263,11 @@ for opt do
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-vhost-crypto) vhost_crypto="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-vhost-crypto) vhost_crypto="yes"
 | 
			
		||||
  --enable-vhost-crypto)
 | 
			
		||||
      vhost_crypto="yes"
 | 
			
		||||
      if test "$mingw32" = "yes"; then
 | 
			
		||||
          error_exit "vhost-crypto isn't available on win32"
 | 
			
		||||
      fi
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-vhost-scsi) vhost_scsi="no"
 | 
			
		||||
  ;;
 | 
			
		||||
@@ -1490,11 +1476,11 @@ for opt do
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-vhost-user) vhost_user="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-vhost-user) vhost_user="yes"
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-vhost-kernel) vhost_kernel="no"
 | 
			
		||||
  ;;
 | 
			
		||||
  --enable-vhost-kernel) vhost_kernel="yes"
 | 
			
		||||
  --enable-vhost-user)
 | 
			
		||||
      vhost_user="yes"
 | 
			
		||||
      if test "$mingw32" = "yes"; then
 | 
			
		||||
          error_exit "vhost-user isn't available on win32"
 | 
			
		||||
      fi
 | 
			
		||||
  ;;
 | 
			
		||||
  --disable-capstone) capstone="no"
 | 
			
		||||
  ;;
 | 
			
		||||
@@ -1526,6 +1512,14 @@ for opt do
 | 
			
		||||
  esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if test "$vhost_user" = ""; then
 | 
			
		||||
    if test "$mingw32" = "yes"; then
 | 
			
		||||
        vhost_user="no"
 | 
			
		||||
    else
 | 
			
		||||
        vhost_user="yes"
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
case "$cpu" in
 | 
			
		||||
    ppc)
 | 
			
		||||
           CPU_CFLAGS="-m32"
 | 
			
		||||
@@ -1610,26 +1604,9 @@ if [ "$bsd_user" = "yes" ]; then
 | 
			
		||||
    mak_wilds="${mak_wilds} $source_path/default-configs/*-bsd-user.mak"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test -z "$target_list_exclude"; then
 | 
			
		||||
for config in $mak_wilds; do
 | 
			
		||||
    default_target_list="${default_target_list} $(basename "$config" .mak)"
 | 
			
		||||
done
 | 
			
		||||
else
 | 
			
		||||
    exclude_list=$(echo "$target_list_exclude" | sed -e 's/,/ /g')
 | 
			
		||||
    for config in $mak_wilds; do
 | 
			
		||||
        target="$(basename "$config" .mak)"
 | 
			
		||||
        exclude="no"
 | 
			
		||||
        for excl in $exclude_list; do
 | 
			
		||||
            if test "$excl" = "$target"; then
 | 
			
		||||
                exclude="yes"
 | 
			
		||||
                break;
 | 
			
		||||
            fi
 | 
			
		||||
        done
 | 
			
		||||
        if test "$exclude" = "no"; then
 | 
			
		||||
            default_target_list="${default_target_list} $target"
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Enumerate public trace backends for --help output
 | 
			
		||||
trace_backend_list=$(echo $(grep -le '^PUBLIC = True$' "$source_path"/scripts/tracetool/backend/*.py | sed -e 's/^.*\/\(.*\)\.py$/\1/'))
 | 
			
		||||
@@ -1648,7 +1625,6 @@ Standard options:
 | 
			
		||||
  --target-list=LIST       set target list (default: build everything)
 | 
			
		||||
$(echo Available targets: $default_target_list | \
 | 
			
		||||
  fold -s -w 53 | sed -e 's/^/                           /')
 | 
			
		||||
  --target-list-exclude=LIST exclude a set of targets from the default target-list
 | 
			
		||||
 | 
			
		||||
Advanced options (experts only):
 | 
			
		||||
  --source-path=PATH       path of source code [$source_path]
 | 
			
		||||
@@ -1742,7 +1718,6 @@ disabled with --disable-FEATURE, default is enabled if available:
 | 
			
		||||
  gtk             gtk UI
 | 
			
		||||
  vte             vte support for the gtk UI
 | 
			
		||||
  curses          curses UI
 | 
			
		||||
  iconv           font glyph conversion support
 | 
			
		||||
  vnc             VNC UI support
 | 
			
		||||
  vnc-sasl        SASL encryption for VNC server
 | 
			
		||||
  vnc-jpeg        JPEG lossy compression for VNC server
 | 
			
		||||
@@ -1768,12 +1743,8 @@ disabled with --disable-FEATURE, default is enabled if available:
 | 
			
		||||
  linux-aio       Linux AIO support
 | 
			
		||||
  cap-ng          libcap-ng support
 | 
			
		||||
  attr            attr and xattr support
 | 
			
		||||
  vhost-net       vhost-net kernel acceleration support
 | 
			
		||||
  vhost-vsock     virtio sockets device support
 | 
			
		||||
  vhost-scsi      vhost-scsi kernel target support
 | 
			
		||||
  vhost-crypto    vhost-user-crypto backend support
 | 
			
		||||
  vhost-kernel    vhost kernel backend support
 | 
			
		||||
  vhost-user      vhost-user backend support
 | 
			
		||||
  vhost-net       vhost-net acceleration support
 | 
			
		||||
  vhost-crypto    vhost-crypto acceleration support
 | 
			
		||||
  spice           spice
 | 
			
		||||
  rbd             rados block device (rbd)
 | 
			
		||||
  libiscsi        iscsi support
 | 
			
		||||
@@ -1799,6 +1770,7 @@ disabled with --disable-FEATURE, default is enabled if available:
 | 
			
		||||
  jemalloc        jemalloc support
 | 
			
		||||
  avx2            AVX2 optimization support
 | 
			
		||||
  replication     replication support
 | 
			
		||||
  vhost-vsock     virtio sockets device support
 | 
			
		||||
  opengl          opengl support
 | 
			
		||||
  virglrenderer   virgl rendering support
 | 
			
		||||
  xfsctl          xfsctl support
 | 
			
		||||
@@ -1815,6 +1787,7 @@ disabled with --disable-FEATURE, default is enabled if available:
 | 
			
		||||
  parallels       parallels image format support
 | 
			
		||||
  sheepdog        sheepdog block driver support
 | 
			
		||||
  crypto-afalg    Linux AF_ALG crypto backend driver
 | 
			
		||||
  vhost-user      vhost-user support
 | 
			
		||||
  capstone        capstone disassembler support
 | 
			
		||||
  debug-mutex     mutex debugging support
 | 
			
		||||
  libpmem         libpmem support
 | 
			
		||||
@@ -1868,7 +1841,7 @@ fi
 | 
			
		||||
# Consult white-list to determine whether to enable werror
 | 
			
		||||
# by default.  Only enable by default for git builds
 | 
			
		||||
if test -z "$werror" ; then
 | 
			
		||||
    if test -e "$source_path/.git" && \
 | 
			
		||||
    if test -d "$source_path/.git" && \
 | 
			
		||||
        { test "$linux" = "yes" || test "$mingw32" = "yes"; }; then
 | 
			
		||||
        werror="yes"
 | 
			
		||||
    else
 | 
			
		||||
@@ -2204,45 +2177,6 @@ else
 | 
			
		||||
  l2tpv3=no
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
#########################################
 | 
			
		||||
# vhost interdependencies and host support
 | 
			
		||||
 | 
			
		||||
# vhost backends
 | 
			
		||||
test "$vhost_user" = "" && vhost_user=yes
 | 
			
		||||
if test "$vhost_user" = "yes" && test "$mingw32" = "yes"; then
 | 
			
		||||
  error_exit "vhost-user isn't available on win32"
 | 
			
		||||
fi
 | 
			
		||||
test "$vhost_kernel" = "" && vhost_kernel=$linux
 | 
			
		||||
if test "$vhost_kernel" = "yes" && test "$linux" != "yes"; then
 | 
			
		||||
  error_exit "vhost-kernel is only available on Linux"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# vhost-kernel devices
 | 
			
		||||
test "$vhost_scsi" = "" && vhost_scsi=$vhost_kernel
 | 
			
		||||
if test "$vhost_scsi" = "yes" && test "$vhost_kernel" != "yes"; then
 | 
			
		||||
  error_exit "--enable-vhost-scsi requires --enable-vhost-kernel"
 | 
			
		||||
fi
 | 
			
		||||
test "$vhost_vsock" = "" && vhost_vsock=$vhost_kernel
 | 
			
		||||
if test "$vhost_vsock" = "yes" && test "$vhost_kernel" != "yes"; then
 | 
			
		||||
  error_exit "--enable-vhost-vsock requires --enable-vhost-kernel"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# vhost-user backends
 | 
			
		||||
test "$vhost_net_user" = "" && vhost_net_user=$vhost_user
 | 
			
		||||
if test "$vhost_net_user" = "yes" && test "$vhost_user" = "no"; then
 | 
			
		||||
  error_exit "--enable-vhost-net-user requires --enable-vhost-user"
 | 
			
		||||
fi
 | 
			
		||||
test "$vhost_crypto" = "" && vhost_crypto=$vhost_user
 | 
			
		||||
if test "$vhost_crypto" = "yes" && test "$vhost_user" = "no"; then
 | 
			
		||||
  error_exit "--enable-vhost-crypto requires --enable-vhost-user"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# OR the vhost-kernel and vhost-user values for simplicity
 | 
			
		||||
if test "$vhost_net" = ""; then
 | 
			
		||||
  test "$vhost_net_user" = "yes" && vhost_net=yes
 | 
			
		||||
  test "$vhost_kernel" = "yes" && vhost_net=yes
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# MinGW / Mingw-w64 localtime_r/gmtime_r check
 | 
			
		||||
 | 
			
		||||
@@ -3464,52 +3398,8 @@ EOF
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# iconv probe
 | 
			
		||||
if test "$iconv" != "no" ; then
 | 
			
		||||
  cat > $TMPC << EOF
 | 
			
		||||
#include <iconv.h>
 | 
			
		||||
int main(void) {
 | 
			
		||||
  iconv_t conv = iconv_open("WCHAR_T", "UCS-2");
 | 
			
		||||
  return conv != (iconv_t) -1;
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
  iconv_prefix_list="/usr/local:/usr"
 | 
			
		||||
  iconv_lib_list=":-liconv"
 | 
			
		||||
  IFS=:
 | 
			
		||||
  for iconv_prefix in $iconv_prefix_list; do
 | 
			
		||||
    IFS=:
 | 
			
		||||
    iconv_cflags="-I$iconv_prefix/include"
 | 
			
		||||
    iconv_ldflags="-L$iconv_prefix/lib"
 | 
			
		||||
    for iconv_link in $iconv_lib_list; do
 | 
			
		||||
      unset IFS
 | 
			
		||||
      iconv_lib="$iconv_ldflags $iconv_link"
 | 
			
		||||
      echo "looking at iconv in '$iconv_cflags' '$iconv_lib'" >> config.log
 | 
			
		||||
      if compile_prog "$iconv_cflags" "$iconv_lib" ; then
 | 
			
		||||
        iconv_found=yes
 | 
			
		||||
        break
 | 
			
		||||
      fi
 | 
			
		||||
    done
 | 
			
		||||
    if test "$iconv_found" = yes ; then
 | 
			
		||||
      break
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  if test "$iconv_found" = "yes" ; then
 | 
			
		||||
    iconv=yes
 | 
			
		||||
  else
 | 
			
		||||
    if test "$iconv" = "yes" ; then
 | 
			
		||||
      feature_not_found "iconv" "Install iconv devel"
 | 
			
		||||
    fi
 | 
			
		||||
    iconv=no
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# curses probe
 | 
			
		||||
if test "$iconv" = "no" ; then
 | 
			
		||||
  # curses will need iconv
 | 
			
		||||
  curses=no
 | 
			
		||||
fi
 | 
			
		||||
if test "$curses" != "no" ; then
 | 
			
		||||
  if test "$mingw32" = "yes" ; then
 | 
			
		||||
    curses_inc_list="$($pkg_config --cflags ncurses 2>/dev/null):"
 | 
			
		||||
@@ -3523,17 +3413,14 @@ if test "$curses" != "no" ; then
 | 
			
		||||
#include <locale.h>
 | 
			
		||||
#include <curses.h>
 | 
			
		||||
#include <wchar.h>
 | 
			
		||||
#include <langinfo.h>
 | 
			
		||||
int main(void) {
 | 
			
		||||
  const char *codeset;
 | 
			
		||||
  wchar_t wch = L'w';
 | 
			
		||||
  setlocale(LC_ALL, "");
 | 
			
		||||
  resize_term(0, 0);
 | 
			
		||||
  addwstr(L"wide chars\n");
 | 
			
		||||
  addnwstr(&wch, 1);
 | 
			
		||||
  add_wch(WACS_DEGREE);
 | 
			
		||||
  codeset = nl_langinfo(CODESET);
 | 
			
		||||
  return codeset != 0;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
  IFS=:
 | 
			
		||||
@@ -3960,20 +3847,20 @@ EOF
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# TPM emulation is only on POSIX
 | 
			
		||||
# TPM passthrough is only on x86 Linux
 | 
			
		||||
 | 
			
		||||
if test "$tpm" = ""; then
 | 
			
		||||
  if test "$mingw32" = "yes"; then
 | 
			
		||||
    tpm=no
 | 
			
		||||
if test "$targetos" = Linux && { test "$cpu" = i386 || test "$cpu" = x86_64; }; then
 | 
			
		||||
  tpm_passthrough=$tpm
 | 
			
		||||
else
 | 
			
		||||
    tpm=yes
 | 
			
		||||
  fi
 | 
			
		||||
elif test "$tpm" = "yes"; then
 | 
			
		||||
  if test "$mingw32" = "yes" ; then
 | 
			
		||||
    error_exit "TPM emulation only available on POSIX systems"
 | 
			
		||||
  fi
 | 
			
		||||
  tpm_passthrough=no
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# TPM emulator is for all posix systems
 | 
			
		||||
if test "$mingw32" != "yes"; then
 | 
			
		||||
  tpm_emulator=$tpm
 | 
			
		||||
else
 | 
			
		||||
  tpm_emulator=no
 | 
			
		||||
fi
 | 
			
		||||
##########################################
 | 
			
		||||
# attr probe
 | 
			
		||||
 | 
			
		||||
@@ -4170,38 +4057,6 @@ if test "$glusterfs" != "no" ; then
 | 
			
		||||
      glusterfs_fallocate="yes"
 | 
			
		||||
      glusterfs_zerofill="yes"
 | 
			
		||||
    fi
 | 
			
		||||
    cat > $TMPC << EOF
 | 
			
		||||
#include <glusterfs/api/glfs.h>
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main(void)
 | 
			
		||||
{
 | 
			
		||||
	/* new glfs_ftruncate() passes two additional args */
 | 
			
		||||
	return glfs_ftruncate(NULL, 0, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
    if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
 | 
			
		||||
      glusterfs_ftruncate_has_stat="yes"
 | 
			
		||||
    fi
 | 
			
		||||
    cat > $TMPC << EOF
 | 
			
		||||
#include <glusterfs/api/glfs.h>
 | 
			
		||||
 | 
			
		||||
/* new glfs_io_cbk() passes two additional glfs_stat structs */
 | 
			
		||||
static void
 | 
			
		||||
glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main(void)
 | 
			
		||||
{
 | 
			
		||||
	glfs_io_cbk iocb = &glusterfs_iocb;
 | 
			
		||||
	iocb(NULL, 0 , NULL, NULL, NULL);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
    if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
 | 
			
		||||
      glusterfs_iocb_has_stat="yes"
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    if test "$glusterfs" = "yes" ; then
 | 
			
		||||
      feature_not_found "GlusterFS backend support" \
 | 
			
		||||
@@ -4708,24 +4563,13 @@ if compile_prog "" "" ; then
 | 
			
		||||
  syncfs=yes
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Check we have a new enough version of sphinx-build
 | 
			
		||||
has_sphinx_build() {
 | 
			
		||||
    # This is a bit awkward but works: create a trivial document and
 | 
			
		||||
    # try to run it with our configuration file (which enforces a
 | 
			
		||||
    # version requirement). This will fail if either
 | 
			
		||||
    # sphinx-build doesn't exist at all or if it is too old.
 | 
			
		||||
    mkdir -p "$TMPDIR1/sphinx"
 | 
			
		||||
    touch "$TMPDIR1/sphinx/index.rst"
 | 
			
		||||
    sphinx-build -c "$source_path/docs" -b html "$TMPDIR1/sphinx" "$TMPDIR1/sphinx/out" >/dev/null 2>&1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if tools are available to build documentation.
 | 
			
		||||
if test "$docs" != "no" ; then
 | 
			
		||||
  if has makeinfo && has pod2man && has_sphinx_build; then
 | 
			
		||||
  if has makeinfo && has pod2man; then
 | 
			
		||||
    docs=yes
 | 
			
		||||
  else
 | 
			
		||||
    if test "$docs" = "yes" ; then
 | 
			
		||||
      feature_not_found "docs" "Install texinfo, Perl/perl-podlators and python-sphinx"
 | 
			
		||||
      feature_not_found "docs" "Install texinfo and Perl/perl-podlators"
 | 
			
		||||
    fi
 | 
			
		||||
    docs=no
 | 
			
		||||
  fi
 | 
			
		||||
@@ -4961,7 +4805,7 @@ int main(void) {
 | 
			
		||||
EOF
 | 
			
		||||
  if compile_prog "" "" ; then
 | 
			
		||||
    guest_agent_ntddscsi=yes
 | 
			
		||||
    libs_qga="-lsetupapi -lcfgmgr32 $libs_qga"
 | 
			
		||||
    libs_qga="-lsetupapi $libs_qga"
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
@@ -5884,50 +5728,6 @@ if test "$libpmem" != "no"; then
 | 
			
		||||
	fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# check for slirp
 | 
			
		||||
 | 
			
		||||
case "$slirp" in
 | 
			
		||||
  "" | yes)
 | 
			
		||||
    if $pkg_config slirp; then
 | 
			
		||||
      slirp=system
 | 
			
		||||
    elif test -e "${source_path}/slirp/Makefile" ; then
 | 
			
		||||
      slirp=internal
 | 
			
		||||
    elif test -z "$slirp" ; then
 | 
			
		||||
      slirp=no
 | 
			
		||||
    else
 | 
			
		||||
      feature_not_found "slirp" "Install slirp devel or git submodule"
 | 
			
		||||
    fi
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  system)
 | 
			
		||||
    if ! $pkg_config slirp; then
 | 
			
		||||
      feature_not_found "slirp" "Install slirp devel"
 | 
			
		||||
    fi
 | 
			
		||||
    ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
case "$slirp" in
 | 
			
		||||
  internal)
 | 
			
		||||
    mkdir -p slirp
 | 
			
		||||
    slirp_cflags="-I\$(SRC_PATH)/slirp/src -I\$(BUILD_DIR)/slirp/src"
 | 
			
		||||
    slirp_libs="-L\$(BUILD_DIR)/slirp -lslirp"
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  system)
 | 
			
		||||
    slirp_version=$($pkg_config --modversion slirp 2>/dev/null)
 | 
			
		||||
    slirp_cflags=$($pkg_config --cflags slirp 2>/dev/null)
 | 
			
		||||
    slirp_libs=$($pkg_config --libs slirp 2>/dev/null)
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  no)
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    error_exit "Unknown state for slirp: $slirp"
 | 
			
		||||
    ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# End of CC checks
 | 
			
		||||
# After here, no more $cc or $ld runs
 | 
			
		||||
@@ -6009,17 +5809,6 @@ if test "$mingw32" = "yes" ; then
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Disable OpenBSD W^X if available
 | 
			
		||||
if test "$tcg" = "yes" && test "$targetos" = "OpenBSD"; then
 | 
			
		||||
    cat > $TMPC <<EOF
 | 
			
		||||
    int main(void) { return 0; }
 | 
			
		||||
EOF
 | 
			
		||||
    wx_ldflags="-Wl,-z,wxneeded"
 | 
			
		||||
    if compile_prog "" "$wx_ldflags"; then
 | 
			
		||||
        QEMU_LDFLAGS="$QEMU_LDFLAGS $wx_ldflags"
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
qemu_confdir=$sysconfdir$confsuffix
 | 
			
		||||
qemu_moddir=$libdir$confsuffix
 | 
			
		||||
qemu_datadir=$datadir$confsuffix
 | 
			
		||||
@@ -6296,8 +6085,7 @@ echo "QEMU_LDFLAGS      $QEMU_LDFLAGS"
 | 
			
		||||
echo "make              $make"
 | 
			
		||||
echo "install           $install"
 | 
			
		||||
echo "python            $python ($python_version)"
 | 
			
		||||
echo "slirp support     $slirp $(echo_version $slirp $slirp_version)"
 | 
			
		||||
if test "$slirp" != "no" ; then
 | 
			
		||||
if test "$slirp" = "yes" ; then
 | 
			
		||||
    echo "smbd              $smbd"
 | 
			
		||||
fi
 | 
			
		||||
echo "module support    $modules"
 | 
			
		||||
@@ -6323,7 +6111,6 @@ echo "libgcrypt         $gcrypt"
 | 
			
		||||
echo "nettle            $nettle $(echo_version $nettle $nettle_version)"
 | 
			
		||||
echo "libtasn1          $tasn1"
 | 
			
		||||
echo "PAM               $auth_pam"
 | 
			
		||||
echo "iconv support     $iconv"
 | 
			
		||||
echo "curses support    $curses"
 | 
			
		||||
echo "virgl support     $virglrenderer $(echo_version $virglrenderer $virgl_version)"
 | 
			
		||||
echo "curl support      $curl"
 | 
			
		||||
@@ -6437,7 +6224,6 @@ echo "capstone          $capstone"
 | 
			
		||||
echo "docker            $docker"
 | 
			
		||||
echo "libpmem support   $libpmem"
 | 
			
		||||
echo "libudev           $libudev"
 | 
			
		||||
echo "default devices   $default_devices"
 | 
			
		||||
 | 
			
		||||
if test "$supported_cpu" = "no"; then
 | 
			
		||||
    echo
 | 
			
		||||
@@ -6499,11 +6285,6 @@ echo "GIT_UPDATE=$git_update" >> $config_host_mak
 | 
			
		||||
 | 
			
		||||
echo "ARCH=$ARCH" >> $config_host_mak
 | 
			
		||||
 | 
			
		||||
if test "$default_devices" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_MINIKCONF_MODE=--defconfig" >> $config_host_mak
 | 
			
		||||
else
 | 
			
		||||
  echo "CONFIG_MINIKCONF_MODE=--allnoconfig" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$debug_tcg" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
@@ -6565,14 +6346,9 @@ fi
 | 
			
		||||
if test "$profiler" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_PROFILER=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$slirp" != "no"; then
 | 
			
		||||
if test "$slirp" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_SLIRP=y" >> $config_host_mak
 | 
			
		||||
  echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak
 | 
			
		||||
  echo "SLIRP_CFLAGS=$slirp_cflags" >> $config_host_mak
 | 
			
		||||
  echo "SLIRP_LIBS=$slirp_libs" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if [ "$slirp" = "internal" ]; then
 | 
			
		||||
    echo "config-host.h: subdir-slirp" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vde" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VDE=y" >> $config_host_mak
 | 
			
		||||
@@ -6659,11 +6435,6 @@ fi
 | 
			
		||||
if test "$cocoa" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_COCOA=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$iconv" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_ICONV=y" >> $config_host_mak
 | 
			
		||||
  echo "ICONV_CFLAGS=$iconv_cflags" >> $config_host_mak
 | 
			
		||||
  echo "ICONV_LIBS=$iconv_lib" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$curses" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_CURSES=m" >> $config_host_mak
 | 
			
		||||
  echo "CURSES_CFLAGS=$curses_inc" >> $config_host_mak
 | 
			
		||||
@@ -6851,11 +6622,8 @@ fi
 | 
			
		||||
if test "$vhost_scsi" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vhost_net" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_NET=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vhost_net_user" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_NET_USER=y" >> $config_host_mak
 | 
			
		||||
if test "$vhost_net" = "yes" && test "$vhost_user" = "yes"; then
 | 
			
		||||
  echo "CONFIG_VHOST_NET_USED=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vhost_crypto" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_CRYPTO=y" >> $config_host_mak
 | 
			
		||||
@@ -6863,9 +6631,6 @@ fi
 | 
			
		||||
if test "$vhost_vsock" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_VSOCK=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vhost_kernel" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_KERNEL=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vhost_user" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VHOST_USER=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
@@ -7088,14 +6853,6 @@ if test "$glusterfs_zerofill" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "$glusterfs_ftruncate_has_stat" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "$glusterfs_iocb_has_stat" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "$libssh2" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_LIBSSH2=m" >> $config_host_mak
 | 
			
		||||
  echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
 | 
			
		||||
@@ -7541,14 +7298,12 @@ case "$target_name" in
 | 
			
		||||
    TARGET_BASE_ARCH=riscv
 | 
			
		||||
    TARGET_ABI_DIR=riscv
 | 
			
		||||
    mttcg=yes
 | 
			
		||||
    gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-32bit-csr.xml"
 | 
			
		||||
    target_compiler=$cross_cc_riscv32
 | 
			
		||||
  ;;
 | 
			
		||||
  riscv64)
 | 
			
		||||
    TARGET_BASE_ARCH=riscv
 | 
			
		||||
    TARGET_ABI_DIR=riscv
 | 
			
		||||
    mttcg=yes
 | 
			
		||||
    gdb_xml_files="riscv-64bit-cpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml"
 | 
			
		||||
    target_compiler=$cross_cc_riscv64
 | 
			
		||||
  ;;
 | 
			
		||||
  sh4|sh4eb)
 | 
			
		||||
@@ -7640,18 +7395,18 @@ fi
 | 
			
		||||
 | 
			
		||||
if supported_xen_target $target; then
 | 
			
		||||
    echo "CONFIG_XEN=y" >> $config_target_mak
 | 
			
		||||
    echo "$target/config-devices.mak: CONFIG_XEN=y" >> $config_host_mak
 | 
			
		||||
    if test "$xen_pci_passthrough" = yes; then
 | 
			
		||||
        echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak"
 | 
			
		||||
    fi
 | 
			
		||||
else
 | 
			
		||||
    echo "$target/config-devices.mak: CONFIG_XEN=n" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if supported_kvm_target $target; then
 | 
			
		||||
    echo "CONFIG_KVM=y" >> $config_target_mak
 | 
			
		||||
    echo "$target/config-devices.mak: CONFIG_KVM=y" >> $config_host_mak
 | 
			
		||||
else
 | 
			
		||||
    echo "$target/config-devices.mak: CONFIG_KVM=n" >> $config_host_mak
 | 
			
		||||
    if test "$vhost_net" = "yes" ; then
 | 
			
		||||
        echo "CONFIG_VHOST_NET=y" >> $config_target_mak
 | 
			
		||||
        if test "$vhost_user" = "yes" ; then
 | 
			
		||||
            echo "CONFIG_VHOST_USER_NET_TEST_$target_name=y" >> $config_host_mak
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
if supported_hax_target $target; then
 | 
			
		||||
    echo "CONFIG_HAX=y" >> $config_target_mak
 | 
			
		||||
@@ -7866,11 +7621,11 @@ fi
 | 
			
		||||
# tests might fail. Prefer to keep the relevant files in their own
 | 
			
		||||
# directory and symlink the directory instead.
 | 
			
		||||
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 tests/fp"
 | 
			
		||||
DIRS="$DIRS docs docs/interop fsdev scsi"
 | 
			
		||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
 | 
			
		||||
DIRS="$DIRS roms/seabios roms/vgabios"
 | 
			
		||||
LINKS="Makefile tests/tcg/Makefile"
 | 
			
		||||
LINKS="Makefile tests/tcg/Makefile qdict-test-data.txt"
 | 
			
		||||
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"
 | 
			
		||||
@@ -7882,7 +7637,6 @@ LINKS="$LINKS pc-bios/qemu-icon.bmp"
 | 
			
		||||
LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit
 | 
			
		||||
LINKS="$LINKS tests/acceptance tests/data"
 | 
			
		||||
LINKS="$LINKS tests/qemu-iotests/check"
 | 
			
		||||
LINKS="$LINKS python"
 | 
			
		||||
for bios_file in \
 | 
			
		||||
    $source_path/pc-bios/*.bin \
 | 
			
		||||
    $source_path/pc-bios/*.lid \
 | 
			
		||||
 
 | 
			
		||||
@@ -524,12 +524,6 @@ int main(int argc, char *argv[])
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!nt_start_addr) {
 | 
			
		||||
        eprintf("Failed to find NT kernel image\n");
 | 
			
		||||
        err = 1;
 | 
			
		||||
        goto out_ps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    printf("KernBase = 0x%016"PRIx64", signature is \'%.2s\'\n", KernBase,
 | 
			
		||||
            (char *)nt_start_addr);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,10 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
amd.com         AMD
 | 
			
		||||
citrix.com      Citrix
 | 
			
		||||
greensocs.com   GreenSocs
 | 
			
		||||
fujitsu.com     Fujitsu
 | 
			
		||||
huawei.com      Huawei
 | 
			
		||||
ibm.com         IBM
 | 
			
		||||
igalia.com      Igalia
 | 
			
		||||
intel.com       Intel
 | 
			
		||||
linaro.org      Linaro
 | 
			
		||||
microsoft.com   Microsoft
 | 
			
		||||
nokia.com       Nokia
 | 
			
		||||
oracle.com      Oracle
 | 
			
		||||
proxmox.com     Proxmox
 | 
			
		||||
 
 | 
			
		||||
@@ -11,4 +11,3 @@ groug@kaod.org
 | 
			
		||||
jcfaracco@gmail.com
 | 
			
		||||
joel@jms.id.au
 | 
			
		||||
sjitindarsingh@gmail.com
 | 
			
		||||
tommusta@gmail.com
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,3 @@
 | 
			
		||||
f4bug@amsat.org
 | 
			
		||||
mjt@tls.msk.ru
 | 
			
		||||
mark.cave-ayland@ilande.co.uk
 | 
			
		||||
rth@twiddle.net
 | 
			
		||||
noring@nocrew.org
 | 
			
		||||
samuel.thibault@ens-lyon.org
 | 
			
		||||
aurelien@aurel32.net
 | 
			
		||||
balaton@eik.bme.hu
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# Janus Technologies contributors using non-corporate email
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
marcel.apfelbaum@gmail.com
 | 
			
		||||
@@ -5,25 +5,13 @@
 | 
			
		||||
 | 
			
		||||
aleksandar.markovic@imgtec.com
 | 
			
		||||
aleksandar.markovic@mips.com
 | 
			
		||||
alex.smith@imgtec.com
 | 
			
		||||
andrew.bennett@imgtec.com
 | 
			
		||||
amarkovic@wavecomp.com
 | 
			
		||||
arikalo@wavecomp.com
 | 
			
		||||
chris@mips.com
 | 
			
		||||
dnikolic@wavecomp.com
 | 
			
		||||
ericj@mips.com
 | 
			
		||||
goran.ferenc@imgtec.com
 | 
			
		||||
james.cowgill@mips.com
 | 
			
		||||
james.hogan@imgtec.com
 | 
			
		||||
james.hogan@mips.com
 | 
			
		||||
leon.alrae@imgtec.com
 | 
			
		||||
matt.redfearn@imgtec.com
 | 
			
		||||
matthew.fortune@mips.com
 | 
			
		||||
miodrag.dinic@imgtec.com
 | 
			
		||||
paul@archlinuxmips.org
 | 
			
		||||
paul.burton@imgtec.com
 | 
			
		||||
petar.jovanovic@imgtec.com
 | 
			
		||||
petarj@mips.com
 | 
			
		||||
pburton@wavecomp.com
 | 
			
		||||
smarkovic@wavecomp.com
 | 
			
		||||
yongbok.kim@imgtec.com
 | 
			
		||||
 
 | 
			
		||||
@@ -68,16 +68,15 @@ static GSourceFuncs vug_src_funcs = {
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GSource *
 | 
			
		||||
vug_source_new(VugDev *gdev, int fd, GIOCondition cond,
 | 
			
		||||
static GSource *
 | 
			
		||||
vug_source_new(VuDev *dev, int fd, GIOCondition cond,
 | 
			
		||||
               vu_watch_cb vu_cb, gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    VuDev *dev = &gdev->parent;
 | 
			
		||||
    GSource *gsrc;
 | 
			
		||||
    VugSrc *src;
 | 
			
		||||
    guint id;
 | 
			
		||||
 | 
			
		||||
    g_assert(gdev);
 | 
			
		||||
    g_assert(dev);
 | 
			
		||||
    g_assert(fd >= 0);
 | 
			
		||||
    g_assert(vu_cb);
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +106,7 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb, void *pvt)
 | 
			
		||||
    g_assert(cb);
 | 
			
		||||
 | 
			
		||||
    dev = container_of(vu_dev, VugDev, parent);
 | 
			
		||||
    src = vug_source_new(dev, fd, vu_evt, cb, pvt);
 | 
			
		||||
    src = vug_source_new(vu_dev, fd, vu_evt, cb, pvt);
 | 
			
		||||
    g_hash_table_replace(dev->fdmap, GINT_TO_POINTER(fd), src);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -142,7 +141,7 @@ vug_init(VugDev *dev, int socket,
 | 
			
		||||
    dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL,
 | 
			
		||||
                                       (GDestroyNotify) g_source_destroy);
 | 
			
		||||
 | 
			
		||||
    dev->src = vug_source_new(dev, socket, G_IO_IN, vug_watch, NULL);
 | 
			
		||||
    dev->src = vug_source_new(&dev->parent, socket, G_IO_IN, vug_watch, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,4 @@ void vug_init(VugDev *dev, int socket,
 | 
			
		||||
              vu_panic_cb panic, const VuDevIface *iface);
 | 
			
		||||
void vug_deinit(VugDev *dev);
 | 
			
		||||
 | 
			
		||||
GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond,
 | 
			
		||||
                        vu_watch_cb vu_cb, gpointer data);
 | 
			
		||||
 | 
			
		||||
#endif /* LIBVHOST_USER_GLIB_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +41,6 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "qemu/atomic.h"
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu/memfd.h"
 | 
			
		||||
 | 
			
		||||
#include "libvhost-user.h"
 | 
			
		||||
 | 
			
		||||
@@ -55,18 +53,6 @@
 | 
			
		||||
            _min1 < _min2 ? _min1 : _min2; })
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Round number down to multiple */
 | 
			
		||||
#define ALIGN_DOWN(n, m) ((n) / (m) * (m))
 | 
			
		||||
 | 
			
		||||
/* Round number up to multiple */
 | 
			
		||||
#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m))
 | 
			
		||||
 | 
			
		||||
/* Align each region to cache line size in inflight buffer */
 | 
			
		||||
#define INFLIGHT_ALIGNMENT 64
 | 
			
		||||
 | 
			
		||||
/* The version of inflight buffer */
 | 
			
		||||
#define INFLIGHT_VERSION 1
 | 
			
		||||
 | 
			
		||||
#define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64)
 | 
			
		||||
 | 
			
		||||
/* The version of the protocol we support */
 | 
			
		||||
@@ -80,20 +66,6 @@
 | 
			
		||||
        }                                       \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
static inline
 | 
			
		||||
bool has_feature(uint64_t features, unsigned int fbit)
 | 
			
		||||
{
 | 
			
		||||
    assert(fbit < 64);
 | 
			
		||||
    return !!(features & (1ULL << fbit));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline
 | 
			
		||||
bool vu_has_feature(VuDev *dev,
 | 
			
		||||
                    unsigned int fbit)
 | 
			
		||||
{
 | 
			
		||||
    return has_feature(dev->features, fbit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *
 | 
			
		||||
vu_request_to_string(unsigned int req)
 | 
			
		||||
{
 | 
			
		||||
@@ -128,8 +100,6 @@ vu_request_to_string(unsigned int req)
 | 
			
		||||
        REQ(VHOST_USER_POSTCOPY_ADVISE),
 | 
			
		||||
        REQ(VHOST_USER_POSTCOPY_LISTEN),
 | 
			
		||||
        REQ(VHOST_USER_POSTCOPY_END),
 | 
			
		||||
        REQ(VHOST_USER_GET_INFLIGHT_FD),
 | 
			
		||||
        REQ(VHOST_USER_SET_INFLIGHT_FD),
 | 
			
		||||
        REQ(VHOST_USER_MAX),
 | 
			
		||||
    };
 | 
			
		||||
#undef REQ
 | 
			
		||||
@@ -920,91 +890,6 @@ vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
inflight_desc_compare(const void *a, const void *b)
 | 
			
		||||
{
 | 
			
		||||
    VuVirtqInflightDesc *desc0 = (VuVirtqInflightDesc *)a,
 | 
			
		||||
                        *desc1 = (VuVirtqInflightDesc *)b;
 | 
			
		||||
 | 
			
		||||
    if (desc1->counter > desc0->counter &&
 | 
			
		||||
        (desc1->counter - desc0->counter) < VIRTQUEUE_MAX_SIZE * 2) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
vu_check_queue_inflights(VuDev *dev, VuVirtq *vq)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    if (!has_feature(dev->protocol_features,
 | 
			
		||||
        VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unlikely(!vq->inflight)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unlikely(!vq->inflight->version)) {
 | 
			
		||||
        /* initialize the buffer */
 | 
			
		||||
        vq->inflight->version = INFLIGHT_VERSION;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vq->used_idx = vq->vring.used->idx;
 | 
			
		||||
    vq->resubmit_num = 0;
 | 
			
		||||
    vq->resubmit_list = NULL;
 | 
			
		||||
    vq->counter = 0;
 | 
			
		||||
 | 
			
		||||
    if (unlikely(vq->inflight->used_idx != vq->used_idx)) {
 | 
			
		||||
        vq->inflight->desc[vq->inflight->last_batch_head].inflight = 0;
 | 
			
		||||
 | 
			
		||||
        barrier();
 | 
			
		||||
 | 
			
		||||
        vq->inflight->used_idx = vq->used_idx;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < vq->inflight->desc_num; i++) {
 | 
			
		||||
        if (vq->inflight->desc[i].inflight == 1) {
 | 
			
		||||
            vq->inuse++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vq->shadow_avail_idx = vq->last_avail_idx = vq->inuse + vq->used_idx;
 | 
			
		||||
 | 
			
		||||
    if (vq->inuse) {
 | 
			
		||||
        vq->resubmit_list = malloc(sizeof(VuVirtqInflightDesc) * vq->inuse);
 | 
			
		||||
        if (!vq->resubmit_list) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < vq->inflight->desc_num; i++) {
 | 
			
		||||
            if (vq->inflight->desc[i].inflight) {
 | 
			
		||||
                vq->resubmit_list[vq->resubmit_num].index = i;
 | 
			
		||||
                vq->resubmit_list[vq->resubmit_num].counter =
 | 
			
		||||
                                        vq->inflight->desc[i].counter;
 | 
			
		||||
                vq->resubmit_num++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (vq->resubmit_num > 1) {
 | 
			
		||||
            qsort(vq->resubmit_list, vq->resubmit_num,
 | 
			
		||||
                  sizeof(VuVirtqInflightDesc), inflight_desc_compare);
 | 
			
		||||
        }
 | 
			
		||||
        vq->counter = vq->resubmit_list[0].counter + 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* in case of I/O hang after reconnecting */
 | 
			
		||||
    if (eventfd_write(vq->kick_fd, 1)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
{
 | 
			
		||||
@@ -1022,8 +907,10 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
        dev->vq[index].kick_fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
 | 
			
		||||
        dev->vq[index].kick_fd = vmsg->fds[0];
 | 
			
		||||
        DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dev->vq[index].started = true;
 | 
			
		||||
    if (dev->iface->queue_set_started) {
 | 
			
		||||
@@ -1038,10 +925,6 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
               dev->vq[index].kick_fd, index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vu_check_queue_inflights(dev, &dev->vq[index])) {
 | 
			
		||||
        vu_panic(dev, "Failed to check inflights for vq: %d\n", index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1112,11 +995,8 @@ vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
        dev->vq[index].call_fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
 | 
			
		||||
        dev->vq[index].call_fd = vmsg->fds[0];
 | 
			
		||||
 | 
			
		||||
    /* in case of I/O hang after reconnecting */
 | 
			
		||||
    if (eventfd_write(vmsg->fds[0], 1)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINT("Got call_fd: %d for vq: %d\n", vmsg->fds[0], index);
 | 
			
		||||
@@ -1140,7 +1020,9 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
        dev->vq[index].err_fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
 | 
			
		||||
        dev->vq[index].err_fd = vmsg->fds[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
@@ -1333,116 +1215,6 @@ vu_set_postcopy_end(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint64_t
 | 
			
		||||
vu_inflight_queue_size(uint16_t queue_size)
 | 
			
		||||
{
 | 
			
		||||
    return ALIGN_UP(sizeof(VuDescStateSplit) * queue_size +
 | 
			
		||||
           sizeof(uint16_t), INFLIGHT_ALIGNMENT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
    void *addr;
 | 
			
		||||
    uint64_t mmap_size;
 | 
			
		||||
    uint16_t num_queues, queue_size;
 | 
			
		||||
 | 
			
		||||
    if (vmsg->size != sizeof(vmsg->payload.inflight)) {
 | 
			
		||||
        vu_panic(dev, "Invalid get_inflight_fd message:%d", vmsg->size);
 | 
			
		||||
        vmsg->payload.inflight.mmap_size = 0;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    num_queues = vmsg->payload.inflight.num_queues;
 | 
			
		||||
    queue_size = vmsg->payload.inflight.queue_size;
 | 
			
		||||
 | 
			
		||||
    DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues);
 | 
			
		||||
    DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size);
 | 
			
		||||
 | 
			
		||||
    mmap_size = vu_inflight_queue_size(queue_size) * num_queues;
 | 
			
		||||
 | 
			
		||||
    addr = qemu_memfd_alloc("vhost-inflight", mmap_size,
 | 
			
		||||
                            F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
 | 
			
		||||
                            &fd, NULL);
 | 
			
		||||
 | 
			
		||||
    if (!addr) {
 | 
			
		||||
        vu_panic(dev, "Failed to alloc vhost inflight area");
 | 
			
		||||
        vmsg->payload.inflight.mmap_size = 0;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(addr, 0, mmap_size);
 | 
			
		||||
 | 
			
		||||
    dev->inflight_info.addr = addr;
 | 
			
		||||
    dev->inflight_info.size = vmsg->payload.inflight.mmap_size = mmap_size;
 | 
			
		||||
    dev->inflight_info.fd = vmsg->fds[0] = fd;
 | 
			
		||||
    vmsg->fd_num = 1;
 | 
			
		||||
    vmsg->payload.inflight.mmap_offset = 0;
 | 
			
		||||
 | 
			
		||||
    DPRINT("send inflight mmap_size: %"PRId64"\n",
 | 
			
		||||
           vmsg->payload.inflight.mmap_size);
 | 
			
		||||
    DPRINT("send inflight mmap offset: %"PRId64"\n",
 | 
			
		||||
           vmsg->payload.inflight.mmap_offset);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
{
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    uint64_t mmap_size, mmap_offset;
 | 
			
		||||
    uint16_t num_queues, queue_size;
 | 
			
		||||
    void *rc;
 | 
			
		||||
 | 
			
		||||
    if (vmsg->fd_num != 1 ||
 | 
			
		||||
        vmsg->size != sizeof(vmsg->payload.inflight)) {
 | 
			
		||||
        vu_panic(dev, "Invalid set_inflight_fd message size:%d fds:%d",
 | 
			
		||||
                 vmsg->size, vmsg->fd_num);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fd = vmsg->fds[0];
 | 
			
		||||
    mmap_size = vmsg->payload.inflight.mmap_size;
 | 
			
		||||
    mmap_offset = vmsg->payload.inflight.mmap_offset;
 | 
			
		||||
    num_queues = vmsg->payload.inflight.num_queues;
 | 
			
		||||
    queue_size = vmsg->payload.inflight.queue_size;
 | 
			
		||||
 | 
			
		||||
    DPRINT("set_inflight_fd mmap_size: %"PRId64"\n", mmap_size);
 | 
			
		||||
    DPRINT("set_inflight_fd mmap_offset: %"PRId64"\n", mmap_offset);
 | 
			
		||||
    DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues);
 | 
			
		||||
    DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size);
 | 
			
		||||
 | 
			
		||||
    rc = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
 | 
			
		||||
              fd, mmap_offset);
 | 
			
		||||
 | 
			
		||||
    if (rc == MAP_FAILED) {
 | 
			
		||||
        vu_panic(dev, "set_inflight_fd mmap error: %s", strerror(errno));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dev->inflight_info.fd) {
 | 
			
		||||
        close(dev->inflight_info.fd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dev->inflight_info.addr) {
 | 
			
		||||
        munmap(dev->inflight_info.addr, dev->inflight_info.size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dev->inflight_info.fd = fd;
 | 
			
		||||
    dev->inflight_info.addr = rc;
 | 
			
		||||
    dev->inflight_info.size = mmap_size;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < num_queues; i++) {
 | 
			
		||||
        dev->vq[i].inflight = (VuVirtqInflight *)rc;
 | 
			
		||||
        dev->vq[i].inflight->desc_num = queue_size;
 | 
			
		||||
        rc = (void *)((char *)rc + vu_inflight_queue_size(queue_size));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
{
 | 
			
		||||
@@ -1513,18 +1285,13 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
 | 
			
		||||
    case VHOST_USER_SET_CONFIG:
 | 
			
		||||
        return vu_set_config(dev, vmsg);
 | 
			
		||||
    case VHOST_USER_NONE:
 | 
			
		||||
        /* if you need processing before exit, override iface->process_msg */
 | 
			
		||||
        exit(0);
 | 
			
		||||
        break;
 | 
			
		||||
    case VHOST_USER_POSTCOPY_ADVISE:
 | 
			
		||||
        return vu_set_postcopy_advise(dev, vmsg);
 | 
			
		||||
    case VHOST_USER_POSTCOPY_LISTEN:
 | 
			
		||||
        return vu_set_postcopy_listen(dev, vmsg);
 | 
			
		||||
    case VHOST_USER_POSTCOPY_END:
 | 
			
		||||
        return vu_set_postcopy_end(dev, vmsg);
 | 
			
		||||
    case VHOST_USER_GET_INFLIGHT_FD:
 | 
			
		||||
        return vu_get_inflight_fd(dev, vmsg);
 | 
			
		||||
    case VHOST_USER_SET_INFLIGHT_FD:
 | 
			
		||||
        return vu_set_inflight_fd(dev, vmsg);
 | 
			
		||||
    default:
 | 
			
		||||
        vmsg_close_fds(vmsg);
 | 
			
		||||
        vu_panic(dev, "Unhandled request: %d", vmsg->request);
 | 
			
		||||
@@ -1592,24 +1359,8 @@ vu_deinit(VuDev *dev)
 | 
			
		||||
            close(vq->err_fd);
 | 
			
		||||
            vq->err_fd = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (vq->resubmit_list) {
 | 
			
		||||
            free(vq->resubmit_list);
 | 
			
		||||
            vq->resubmit_list = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        vq->inflight = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dev->inflight_info.addr) {
 | 
			
		||||
        munmap(dev->inflight_info.addr, dev->inflight_info.size);
 | 
			
		||||
        dev->inflight_info.addr = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dev->inflight_info.fd > 0) {
 | 
			
		||||
        close(dev->inflight_info.fd);
 | 
			
		||||
        dev->inflight_info.fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vu_close_log(dev);
 | 
			
		||||
    if (dev->slave_fd != -1) {
 | 
			
		||||
@@ -1936,6 +1687,20 @@ vu_queue_empty(VuDev *dev, VuVirtq *vq)
 | 
			
		||||
    return vring_avail_idx(vq) == vq->last_avail_idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline
 | 
			
		||||
bool has_feature(uint64_t features, unsigned int fbit)
 | 
			
		||||
{
 | 
			
		||||
    assert(fbit < 64);
 | 
			
		||||
    return !!(features & (1ULL << fbit));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline
 | 
			
		||||
bool vu_has_feature(VuDev *dev,
 | 
			
		||||
                    unsigned int fbit)
 | 
			
		||||
{
 | 
			
		||||
    return has_feature(dev->features, fbit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
vring_notify(VuDev *dev, VuVirtq *vq)
 | 
			
		||||
{
 | 
			
		||||
@@ -2064,6 +1829,12 @@ virtqueue_map_desc(VuDev *dev,
 | 
			
		||||
    *p_num_sg = num_sg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Round number down to multiple */
 | 
			
		||||
#define ALIGN_DOWN(n, m) ((n) / (m) * (m))
 | 
			
		||||
 | 
			
		||||
/* Round number up to multiple */
 | 
			
		||||
#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m))
 | 
			
		||||
 | 
			
		||||
static void *
 | 
			
		||||
virtqueue_alloc_element(size_t sz,
 | 
			
		||||
                                     unsigned out_num, unsigned in_num)
 | 
			
		||||
@@ -2082,20 +1853,49 @@ virtqueue_alloc_element(size_t sz,
 | 
			
		||||
    return elem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *
 | 
			
		||||
vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz)
 | 
			
		||||
void *
 | 
			
		||||
vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
    struct vring_desc *desc = vq->vring.desc;
 | 
			
		||||
    unsigned int i, head, max, desc_len;
 | 
			
		||||
    uint64_t desc_addr, read_len;
 | 
			
		||||
    unsigned int desc_len;
 | 
			
		||||
    unsigned int max = vq->vring.num;
 | 
			
		||||
    unsigned int i = idx;
 | 
			
		||||
    VuVirtqElement *elem;
 | 
			
		||||
    unsigned int out_num = 0, in_num = 0;
 | 
			
		||||
    unsigned out_num, in_num;
 | 
			
		||||
    struct iovec iov[VIRTQUEUE_MAX_SIZE];
 | 
			
		||||
    struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE];
 | 
			
		||||
    struct vring_desc *desc;
 | 
			
		||||
    int rc;
 | 
			
		||||
 | 
			
		||||
    if (unlikely(dev->broken) ||
 | 
			
		||||
        unlikely(!vq->vring.avail)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vu_queue_empty(dev, vq)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    /* Needed after virtio_queue_empty(), see comment in
 | 
			
		||||
     * virtqueue_num_heads(). */
 | 
			
		||||
    smp_rmb();
 | 
			
		||||
 | 
			
		||||
    /* When we start there are none of either input nor output. */
 | 
			
		||||
    out_num = in_num = 0;
 | 
			
		||||
 | 
			
		||||
    max = vq->vring.num;
 | 
			
		||||
    if (vq->inuse >= vq->vring.num) {
 | 
			
		||||
        vu_panic(dev, "Virtqueue size exceeded");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!virtqueue_get_head(dev, vq, vq->last_avail_idx++, &head)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vu_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
 | 
			
		||||
        vring_set_avail_event(vq, vq->last_avail_idx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i = head;
 | 
			
		||||
    desc = vq->vring.desc;
 | 
			
		||||
    if (desc[i].flags & VRING_DESC_F_INDIRECT) {
 | 
			
		||||
        if (desc[i].len % sizeof(struct vring_desc)) {
 | 
			
		||||
            vu_panic(dev, "Invalid size for indirect buffer table");
 | 
			
		||||
@@ -2147,13 +1947,12 @@ vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz)
 | 
			
		||||
    } while (rc == VIRTQUEUE_READ_DESC_MORE);
 | 
			
		||||
 | 
			
		||||
    if (rc == VIRTQUEUE_READ_DESC_ERROR) {
 | 
			
		||||
        vu_panic(dev, "read descriptor error");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now copy what we have collected and mapped */
 | 
			
		||||
    elem = virtqueue_alloc_element(sz, out_num, in_num);
 | 
			
		||||
    elem->index = idx;
 | 
			
		||||
    elem->index = head;
 | 
			
		||||
    for (i = 0; i < out_num; i++) {
 | 
			
		||||
        elem->out_sg[i] = iov[i];
 | 
			
		||||
    }
 | 
			
		||||
@@ -2161,142 +1960,11 @@ vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz)
 | 
			
		||||
        elem->in_sg[i] = iov[out_num + i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return elem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
vu_queue_inflight_get(VuDev *dev, VuVirtq *vq, int desc_idx)
 | 
			
		||||
{
 | 
			
		||||
    if (!has_feature(dev->protocol_features,
 | 
			
		||||
        VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unlikely(!vq->inflight)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vq->inflight->desc[desc_idx].counter = vq->counter++;
 | 
			
		||||
    vq->inflight->desc[desc_idx].inflight = 1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
vu_queue_inflight_pre_put(VuDev *dev, VuVirtq *vq, int desc_idx)
 | 
			
		||||
{
 | 
			
		||||
    if (!has_feature(dev->protocol_features,
 | 
			
		||||
        VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unlikely(!vq->inflight)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vq->inflight->last_batch_head = desc_idx;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
vu_queue_inflight_post_put(VuDev *dev, VuVirtq *vq, int desc_idx)
 | 
			
		||||
{
 | 
			
		||||
    if (!has_feature(dev->protocol_features,
 | 
			
		||||
        VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unlikely(!vq->inflight)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    barrier();
 | 
			
		||||
 | 
			
		||||
    vq->inflight->desc[desc_idx].inflight = 0;
 | 
			
		||||
 | 
			
		||||
    barrier();
 | 
			
		||||
 | 
			
		||||
    vq->inflight->used_idx = vq->used_idx;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *
 | 
			
		||||
vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    unsigned int head;
 | 
			
		||||
    VuVirtqElement *elem;
 | 
			
		||||
 | 
			
		||||
    if (unlikely(dev->broken) ||
 | 
			
		||||
        unlikely(!vq->vring.avail)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unlikely(vq->resubmit_list && vq->resubmit_num > 0)) {
 | 
			
		||||
        i = (--vq->resubmit_num);
 | 
			
		||||
        elem = vu_queue_map_desc(dev, vq, vq->resubmit_list[i].index, sz);
 | 
			
		||||
 | 
			
		||||
        if (!vq->resubmit_num) {
 | 
			
		||||
            free(vq->resubmit_list);
 | 
			
		||||
            vq->resubmit_list = NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return elem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vu_queue_empty(dev, vq)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    /*
 | 
			
		||||
     * Needed after virtio_queue_empty(), see comment in
 | 
			
		||||
     * virtqueue_num_heads().
 | 
			
		||||
     */
 | 
			
		||||
    smp_rmb();
 | 
			
		||||
 | 
			
		||||
    if (vq->inuse >= vq->vring.num) {
 | 
			
		||||
        vu_panic(dev, "Virtqueue size exceeded");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!virtqueue_get_head(dev, vq, vq->last_avail_idx++, &head)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vu_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
 | 
			
		||||
        vring_set_avail_event(vq, vq->last_avail_idx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    elem = vu_queue_map_desc(dev, vq, head, sz);
 | 
			
		||||
 | 
			
		||||
    if (!elem) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vq->inuse++;
 | 
			
		||||
 | 
			
		||||
    vu_queue_inflight_get(dev, vq, head);
 | 
			
		||||
 | 
			
		||||
    return elem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
vu_queue_detach_element(VuDev *dev, VuVirtq *vq, VuVirtqElement *elem,
 | 
			
		||||
                        size_t len)
 | 
			
		||||
{
 | 
			
		||||
    vq->inuse--;
 | 
			
		||||
    /* unmap, when DMA support is added */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
vu_queue_unpop(VuDev *dev, VuVirtq *vq, VuVirtqElement *elem,
 | 
			
		||||
               size_t len)
 | 
			
		||||
{
 | 
			
		||||
    vq->last_avail_idx--;
 | 
			
		||||
    vu_queue_detach_element(dev, vq, elem, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
vu_queue_rewind(VuDev *dev, VuVirtq *vq, unsigned int num)
 | 
			
		||||
{
 | 
			
		||||
@@ -2438,7 +2106,5 @@ vu_queue_push(VuDev *dev, VuVirtq *vq,
 | 
			
		||||
              const VuVirtqElement *elem, unsigned int len)
 | 
			
		||||
{
 | 
			
		||||
    vu_queue_fill(dev, vq, elem, len, 0);
 | 
			
		||||
    vu_queue_inflight_pre_put(dev, vq, elem->index);
 | 
			
		||||
    vu_queue_flush(dev, vq, 1);
 | 
			
		||||
    vu_queue_inflight_post_put(dev, vq, elem->index);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,6 @@ enum VhostUserProtocolFeature {
 | 
			
		||||
    VHOST_USER_PROTOCOL_F_CONFIG = 9,
 | 
			
		||||
    VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
 | 
			
		||||
    VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
 | 
			
		||||
    VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12,
 | 
			
		||||
 | 
			
		||||
    VHOST_USER_PROTOCOL_F_MAX
 | 
			
		||||
};
 | 
			
		||||
@@ -92,8 +91,6 @@ typedef enum VhostUserRequest {
 | 
			
		||||
    VHOST_USER_POSTCOPY_ADVISE  = 28,
 | 
			
		||||
    VHOST_USER_POSTCOPY_LISTEN  = 29,
 | 
			
		||||
    VHOST_USER_POSTCOPY_END     = 30,
 | 
			
		||||
    VHOST_USER_GET_INFLIGHT_FD = 31,
 | 
			
		||||
    VHOST_USER_SET_INFLIGHT_FD = 32,
 | 
			
		||||
    VHOST_USER_MAX
 | 
			
		||||
} VhostUserRequest;
 | 
			
		||||
 | 
			
		||||
@@ -141,13 +138,6 @@ typedef struct VhostUserVringArea {
 | 
			
		||||
    uint64_t offset;
 | 
			
		||||
} VhostUserVringArea;
 | 
			
		||||
 | 
			
		||||
typedef struct VhostUserInflight {
 | 
			
		||||
    uint64_t mmap_size;
 | 
			
		||||
    uint64_t mmap_offset;
 | 
			
		||||
    uint16_t num_queues;
 | 
			
		||||
    uint16_t queue_size;
 | 
			
		||||
} VhostUserInflight;
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
# define VU_PACKED __attribute__((gcc_struct, packed))
 | 
			
		||||
#else
 | 
			
		||||
@@ -155,7 +145,7 @@ typedef struct VhostUserInflight {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct VhostUserMsg {
 | 
			
		||||
    int request;
 | 
			
		||||
    VhostUserRequest request;
 | 
			
		||||
 | 
			
		||||
#define VHOST_USER_VERSION_MASK     (0x3)
 | 
			
		||||
#define VHOST_USER_REPLY_MASK       (0x1 << 2)
 | 
			
		||||
@@ -173,7 +163,6 @@ typedef struct VhostUserMsg {
 | 
			
		||||
        VhostUserLog log;
 | 
			
		||||
        VhostUserConfig config;
 | 
			
		||||
        VhostUserVringArea area;
 | 
			
		||||
        VhostUserInflight inflight;
 | 
			
		||||
    } payload;
 | 
			
		||||
 | 
			
		||||
    int fds[VHOST_MEMORY_MAX_NREGIONS];
 | 
			
		||||
@@ -245,61 +234,9 @@ typedef struct VuRing {
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
} VuRing;
 | 
			
		||||
 | 
			
		||||
typedef struct VuDescStateSplit {
 | 
			
		||||
    /* Indicate whether this descriptor is inflight or not.
 | 
			
		||||
     * Only available for head-descriptor. */
 | 
			
		||||
    uint8_t inflight;
 | 
			
		||||
 | 
			
		||||
    /* Padding */
 | 
			
		||||
    uint8_t padding[5];
 | 
			
		||||
 | 
			
		||||
    /* Maintain a list for the last batch of used descriptors.
 | 
			
		||||
     * Only available when batching is used for submitting */
 | 
			
		||||
    uint16_t next;
 | 
			
		||||
 | 
			
		||||
    /* Used to preserve the order of fetching available descriptors.
 | 
			
		||||
     * Only available for head-descriptor. */
 | 
			
		||||
    uint64_t counter;
 | 
			
		||||
} VuDescStateSplit;
 | 
			
		||||
 | 
			
		||||
typedef struct VuVirtqInflight {
 | 
			
		||||
    /* The feature flags of this region. Now it's initialized to 0. */
 | 
			
		||||
    uint64_t features;
 | 
			
		||||
 | 
			
		||||
    /* The version of this region. It's 1 currently.
 | 
			
		||||
     * Zero value indicates a vm reset happened. */
 | 
			
		||||
    uint16_t version;
 | 
			
		||||
 | 
			
		||||
    /* The size of VuDescStateSplit array. It's equal to the virtqueue
 | 
			
		||||
     * size. Slave could get it from queue size field of VhostUserInflight. */
 | 
			
		||||
    uint16_t desc_num;
 | 
			
		||||
 | 
			
		||||
    /* The head of list that track the last batch of used descriptors. */
 | 
			
		||||
    uint16_t last_batch_head;
 | 
			
		||||
 | 
			
		||||
    /* Storing the idx value of used ring */
 | 
			
		||||
    uint16_t used_idx;
 | 
			
		||||
 | 
			
		||||
    /* Used to track the state of each descriptor in descriptor table */
 | 
			
		||||
    VuDescStateSplit desc[0];
 | 
			
		||||
} VuVirtqInflight;
 | 
			
		||||
 | 
			
		||||
typedef struct VuVirtqInflightDesc {
 | 
			
		||||
    uint16_t index;
 | 
			
		||||
    uint64_t counter;
 | 
			
		||||
} VuVirtqInflightDesc;
 | 
			
		||||
 | 
			
		||||
typedef struct VuVirtq {
 | 
			
		||||
    VuRing vring;
 | 
			
		||||
 | 
			
		||||
    VuVirtqInflight *inflight;
 | 
			
		||||
 | 
			
		||||
    VuVirtqInflightDesc *resubmit_list;
 | 
			
		||||
 | 
			
		||||
    uint16_t resubmit_num;
 | 
			
		||||
 | 
			
		||||
    uint64_t counter;
 | 
			
		||||
 | 
			
		||||
    /* Next head to pop */
 | 
			
		||||
    uint16_t last_avail_idx;
 | 
			
		||||
 | 
			
		||||
@@ -342,18 +279,11 @@ typedef void (*vu_set_watch_cb) (VuDev *dev, int fd, int condition,
 | 
			
		||||
                                 vu_watch_cb cb, void *data);
 | 
			
		||||
typedef void (*vu_remove_watch_cb) (VuDev *dev, int fd);
 | 
			
		||||
 | 
			
		||||
typedef struct VuDevInflightInfo {
 | 
			
		||||
    int fd;
 | 
			
		||||
    void *addr;
 | 
			
		||||
    uint64_t size;
 | 
			
		||||
} VuDevInflightInfo;
 | 
			
		||||
 | 
			
		||||
struct VuDev {
 | 
			
		||||
    int sock;
 | 
			
		||||
    uint32_t nregions;
 | 
			
		||||
    VuDevRegion regions[VHOST_MEMORY_MAX_NREGIONS];
 | 
			
		||||
    VuVirtq vq[VHOST_MAX_NR_VIRTQUEUE];
 | 
			
		||||
    VuDevInflightInfo inflight_info;
 | 
			
		||||
    int log_call_fd;
 | 
			
		||||
    int slave_fd;
 | 
			
		||||
    uint64_t log_size;
 | 
			
		||||
@@ -528,20 +458,6 @@ void vu_queue_notify(VuDev *dev, VuVirtq *vq);
 | 
			
		||||
 */
 | 
			
		||||
void *vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * vu_queue_unpop:
 | 
			
		||||
 * @dev: a VuDev context
 | 
			
		||||
 * @vq: a VuVirtq queue
 | 
			
		||||
 * @elem: The #VuVirtqElement
 | 
			
		||||
 * @len: number of bytes written
 | 
			
		||||
 *
 | 
			
		||||
 * Pretend the most recent element wasn't popped from the virtqueue.  The next
 | 
			
		||||
 * call to vu_queue_pop() will refetch the element.
 | 
			
		||||
 */
 | 
			
		||||
void vu_queue_unpop(VuDev *dev, VuVirtq *vq, VuVirtqElement *elem,
 | 
			
		||||
                    size_t len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * vu_queue_rewind:
 | 
			
		||||
 * @dev: a VuDev context
 | 
			
		||||
 
 | 
			
		||||
@@ -300,7 +300,7 @@ static void hash_tbl_remove_fd_ifid_pair(int fd)
 | 
			
		||||
    pthread_rwlock_unlock(&server.lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid)
 | 
			
		||||
static int get_fd(const char *mad, int *fd, __be64 *gid_ifid)
 | 
			
		||||
{
 | 
			
		||||
    struct umad_hdr *hdr = (struct umad_hdr *)mad;
 | 
			
		||||
    char *data = (char *)hdr + sizeof(*hdr);
 | 
			
		||||
@@ -308,35 +308,13 @@ static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid)
 | 
			
		||||
    uint16_t attr_id = be16toh(hdr->attr_id);
 | 
			
		||||
    int rc = 0;
 | 
			
		||||
 | 
			
		||||
    if (umad_len <= sizeof(*hdr)) {
 | 
			
		||||
        rc = -EINVAL;
 | 
			
		||||
        syslog(LOG_DEBUG, "Ignoring MAD packets with header only\n");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (attr_id) {
 | 
			
		||||
    case UMAD_CM_ATTR_REQ:
 | 
			
		||||
        if (unlikely(umad_len < sizeof(*hdr) + CM_REQ_DGID_POS +
 | 
			
		||||
            sizeof(*gid_ifid))) {
 | 
			
		||||
            rc = -EINVAL;
 | 
			
		||||
            syslog(LOG_WARNING,
 | 
			
		||||
                   "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len,
 | 
			
		||||
                    attr_id);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(gid_ifid, data + CM_REQ_DGID_POS, sizeof(*gid_ifid));
 | 
			
		||||
        rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case UMAD_CM_ATTR_SIDR_REQ:
 | 
			
		||||
        if (unlikely(umad_len < sizeof(*hdr) + CM_SIDR_REQ_DGID_POS +
 | 
			
		||||
            sizeof(*gid_ifid))) {
 | 
			
		||||
            rc = -EINVAL;
 | 
			
		||||
            syslog(LOG_WARNING,
 | 
			
		||||
                   "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len,
 | 
			
		||||
                    attr_id);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(gid_ifid, data + CM_SIDR_REQ_DGID_POS, sizeof(*gid_ifid));
 | 
			
		||||
        rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid);
 | 
			
		||||
        break;
 | 
			
		||||
@@ -353,13 +331,6 @@ static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid)
 | 
			
		||||
        data += sizeof(comm_id);
 | 
			
		||||
        /* Fall through */
 | 
			
		||||
    case UMAD_CM_ATTR_SIDR_REP:
 | 
			
		||||
        if (unlikely(umad_len < sizeof(*hdr) + sizeof(comm_id))) {
 | 
			
		||||
            rc = -EINVAL;
 | 
			
		||||
            syslog(LOG_WARNING,
 | 
			
		||||
                   "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len,
 | 
			
		||||
                   attr_id);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(&comm_id, data, sizeof(comm_id));
 | 
			
		||||
        if (comm_id) {
 | 
			
		||||
            rc = hash_tbl_search_fd_by_comm_id(comm_id, fd, gid_ifid);
 | 
			
		||||
@@ -373,7 +344,6 @@ static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid)
 | 
			
		||||
 | 
			
		||||
    syslog(LOG_DEBUG, "mad_to_vm: %d 0x%x 0x%x\n", *fd, attr_id, comm_id);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -402,8 +372,7 @@ static void *umad_recv_thread_func(void *args)
 | 
			
		||||
        } while (rc && server.run);
 | 
			
		||||
 | 
			
		||||
        if (server.run) {
 | 
			
		||||
            rc = get_fd(msg.umad.mad, msg.umad_len, &fd,
 | 
			
		||||
                        &msg.hdr.sgid.global.interface_id);
 | 
			
		||||
            rc = get_fd(msg.umad.mad, &fd, &msg.hdr.sgid.global.interface_id);
 | 
			
		||||
            if (rc) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -146,7 +146,7 @@ struct QCryptoBlockLUKSKeySlot {
 | 
			
		||||
    uint32_t key_offset;
 | 
			
		||||
    /* number of anti-forensic stripes */
 | 
			
		||||
    uint32_t stripes;
 | 
			
		||||
};
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
 | 
			
		||||
QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48);
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +191,7 @@ struct QCryptoBlockLUKSHeader {
 | 
			
		||||
 | 
			
		||||
    /* key slots */
 | 
			
		||||
    QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS];
 | 
			
		||||
};
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
 | 
			
		||||
QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
# See docs/devel/tracing.txt for syntax documentation.
 | 
			
		||||
 | 
			
		||||
# tlscreds.c
 | 
			
		||||
# crypto/tlscreds.c
 | 
			
		||||
qcrypto_tls_creds_load_dh(void *creds, const char *filename) "TLS creds load DH creds=%p filename=%s"
 | 
			
		||||
qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) "TLS creds path creds=%p filename=%s path=%s"
 | 
			
		||||
 | 
			
		||||
# tlscredsanon.c
 | 
			
		||||
# crypto/tlscredsanon.c
 | 
			
		||||
qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s"
 | 
			
		||||
 | 
			
		||||
# tlscredspsk.c
 | 
			
		||||
# crypto/tlscredspsk.c
 | 
			
		||||
qcrypto_tls_creds_psk_load(void *creds, const char *dir) "TLS creds psk load creds=%p dir=%s"
 | 
			
		||||
 | 
			
		||||
# tlscredsx509.c
 | 
			
		||||
# crypto/tlscredsx509.c
 | 
			
		||||
qcrypto_tls_creds_x509_load(void *creds, const char *dir) "TLS creds x509 load creds=%p dir=%s"
 | 
			
		||||
qcrypto_tls_creds_x509_check_basic_constraints(void *creds, const char *file, int status) "TLS creds x509 check basic constraints creds=%p file=%s status=%d"
 | 
			
		||||
qcrypto_tls_creds_x509_check_key_usage(void *creds, const char *file, int status, int usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%d critical=%d"
 | 
			
		||||
@@ -18,6 +18,6 @@ qcrypto_tls_creds_x509_check_key_purpose(void *creds, const char *file, int stat
 | 
			
		||||
qcrypto_tls_creds_x509_load_cert(void *creds, int isServer, const char *file) "TLS creds x509 load cert creds=%p isServer=%d file=%s"
 | 
			
		||||
qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds x509 load cert list creds=%p file=%s"
 | 
			
		||||
 | 
			
		||||
# tlssession.c
 | 
			
		||||
# crypto/tlssession.c
 | 
			
		||||
qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d"
 | 
			
		||||
qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,22 @@
 | 
			
		||||
# Default configuration for alpha-softmmu
 | 
			
		||||
 | 
			
		||||
# Uncomment the following lines to disable these optional devices:
 | 
			
		||||
#
 | 
			
		||||
#CONFIG_PCI_DEVICES=n
 | 
			
		||||
#CONFIG_TEST_DEVICES=n
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
include pci.mak
 | 
			
		||||
include usb.mak
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_SERIAL_ISA=y
 | 
			
		||||
CONFIG_I82374=y
 | 
			
		||||
CONFIG_I8254=y
 | 
			
		||||
CONFIG_I8257=y
 | 
			
		||||
CONFIG_PARALLEL=y
 | 
			
		||||
CONFIG_FDC=y
 | 
			
		||||
CONFIG_PCKBD=y
 | 
			
		||||
CONFIG_VGA_CIRRUS=y
 | 
			
		||||
CONFIG_IDE_CORE=y
 | 
			
		||||
CONFIG_IDE_QDEV=y
 | 
			
		||||
CONFIG_VMWARE_VGA=y
 | 
			
		||||
CONFIG_IDE_CMD646=y
 | 
			
		||||
CONFIG_I8259=y
 | 
			
		||||
CONFIG_MC146818RTC=y
 | 
			
		||||
CONFIG_ISA_TESTDEV=y
 | 
			
		||||
CONFIG_SMC37C669=y
 | 
			
		||||
CONFIG_DP264=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,13 @@
 | 
			
		||||
# Default configuration for arm-softmmu
 | 
			
		||||
 | 
			
		||||
CONFIG_PCI=y
 | 
			
		||||
CONFIG_PCI_DEVICES=y
 | 
			
		||||
CONFIG_PCI_TESTDEV=y
 | 
			
		||||
include pci.mak
 | 
			
		||||
include usb.mak
 | 
			
		||||
CONFIG_VGA=y
 | 
			
		||||
CONFIG_NAND=y
 | 
			
		||||
CONFIG_ECC=y
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_PTIMER=y
 | 
			
		||||
CONFIG_SD=y
 | 
			
		||||
CONFIG_MAX7310=y
 | 
			
		||||
CONFIG_WM8750=y
 | 
			
		||||
CONFIG_TWL92230=y
 | 
			
		||||
@@ -24,6 +25,7 @@ CONFIG_DDC=y
 | 
			
		||||
CONFIG_SII9022=y
 | 
			
		||||
CONFIG_ADS7846=y
 | 
			
		||||
CONFIG_MAX111X=y
 | 
			
		||||
CONFIG_SSI=y
 | 
			
		||||
CONFIG_SSI_SD=y
 | 
			
		||||
CONFIG_SSI_M25P80=y
 | 
			
		||||
CONFIG_LAN9118=y
 | 
			
		||||
@@ -35,6 +37,7 @@ CONFIG_DS1338=y
 | 
			
		||||
CONFIG_PFLASH_CFI01=y
 | 
			
		||||
CONFIG_PFLASH_CFI02=y
 | 
			
		||||
CONFIG_MICRODRIVE=y
 | 
			
		||||
CONFIG_USB=y
 | 
			
		||||
CONFIG_USB_MUSB=y
 | 
			
		||||
CONFIG_USB_EHCI_SYSBUS=y
 | 
			
		||||
CONFIG_PLATFORM_BUS=y
 | 
			
		||||
@@ -48,6 +51,7 @@ CONFIG_ARM_V7M=y
 | 
			
		||||
CONFIG_NETDUINO2=y
 | 
			
		||||
 | 
			
		||||
CONFIG_ARM_GIC=y
 | 
			
		||||
CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
 | 
			
		||||
CONFIG_ARM_TIMER=y
 | 
			
		||||
CONFIG_ARM_MPTIMER=y
 | 
			
		||||
CONFIG_A9_GTIMER=y
 | 
			
		||||
@@ -67,6 +71,7 @@ CONFIG_CADENCE=y
 | 
			
		||||
CONFIG_XGMAC=y
 | 
			
		||||
CONFIG_EXYNOS4=y
 | 
			
		||||
CONFIG_PXA2XX=y
 | 
			
		||||
CONFIG_I2C=y
 | 
			
		||||
CONFIG_BITBANG_I2C=y
 | 
			
		||||
CONFIG_FRAMEBUFFER=y
 | 
			
		||||
CONFIG_XILINX_SPIPS=y
 | 
			
		||||
@@ -114,14 +119,16 @@ CONFIG_IOTKIT_SECCTL=y
 | 
			
		||||
CONFIG_IOTKIT_SYSCTL=y
 | 
			
		||||
CONFIG_IOTKIT_SYSINFO=y
 | 
			
		||||
CONFIG_ARMSSE_CPUID=y
 | 
			
		||||
CONFIG_ARMSSE_MHU=y
 | 
			
		||||
 | 
			
		||||
CONFIG_VERSATILE=y
 | 
			
		||||
CONFIG_VERSATILE_PCI=y
 | 
			
		||||
CONFIG_VERSATILE_I2C=y
 | 
			
		||||
 | 
			
		||||
CONFIG_PCI_EXPRESS=y
 | 
			
		||||
CONFIG_PCI_EXPRESS_GENERIC_BRIDGE=y
 | 
			
		||||
CONFIG_VFIO=$(CONFIG_LINUX)
 | 
			
		||||
CONFIG_VFIO_PLATFORM=y
 | 
			
		||||
CONFIG_VFIO_XGMAC=y
 | 
			
		||||
CONFIG_VFIO_AMD_XGBE=y
 | 
			
		||||
 | 
			
		||||
CONFIG_SDHCI=y
 | 
			
		||||
CONFIG_INTEGRATOR=y
 | 
			
		||||
@@ -157,6 +164,3 @@ CONFIG_PCI_EXPRESS_DESIGNWARE=y
 | 
			
		||||
CONFIG_STRONGARM=y
 | 
			
		||||
CONFIG_HIGHBANK=y
 | 
			
		||||
CONFIG_MUSICPAL=y
 | 
			
		||||
 | 
			
		||||
# for realview and versatilepb
 | 
			
		||||
CONFIG_LSI_SCSI_PCI=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
# Default configuration for cris-softmmu
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
CONFIG_ETRAXFS=y
 | 
			
		||||
CONFIG_NAND=y
 | 
			
		||||
CONFIG_PTIMER=y
 | 
			
		||||
CONFIG_PFLASH_CFI02=y
 | 
			
		||||
CONFIG_AXIS=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,13 @@
 | 
			
		||||
# Default configuration for hppa-softmmu
 | 
			
		||||
 | 
			
		||||
# Uncomment the following lines to disable these optional devices:
 | 
			
		||||
#
 | 
			
		||||
#CONFIG_PCI_DEVICES=n
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
include pci.mak
 | 
			
		||||
include usb.mak
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_SERIAL_ISA=y
 | 
			
		||||
CONFIG_ISA_BUS=y
 | 
			
		||||
CONFIG_I8259=y
 | 
			
		||||
CONFIG_E1000_PCI=y
 | 
			
		||||
CONFIG_IDE_ISA=y
 | 
			
		||||
CONFIG_IDE_CMD646=y
 | 
			
		||||
# CONFIG_IDE_MMIO=y
 | 
			
		||||
CONFIG_VIRTIO_VGA=y
 | 
			
		||||
CONFIG_MC146818RTC=y
 | 
			
		||||
CONFIG_DINO=y
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								default-configs/hyperv.mak
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								default-configs/hyperv.mak
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
CONFIG_HYPERV=$(CONFIG_KVM)
 | 
			
		||||
CONFIG_HYPERV_TESTDEV=y
 | 
			
		||||
@@ -1,27 +1,74 @@
 | 
			
		||||
# Default configuration for i386-softmmu
 | 
			
		||||
 | 
			
		||||
# Uncomment the following lines to disable these optional devices:
 | 
			
		||||
#
 | 
			
		||||
#CONFIG_AMD_IOMMU=n
 | 
			
		||||
#CONFIG_APPLESMC=n
 | 
			
		||||
#CONFIG_FDC=n
 | 
			
		||||
#CONFIG_HPET=n
 | 
			
		||||
#CONFIG_HYPERV=n
 | 
			
		||||
#CONFIG_ISA_DEBUG=n
 | 
			
		||||
#CONFIG_ISA_IPMI_BT=n
 | 
			
		||||
#CONFIG_ISA_IPMI_KCS=n
 | 
			
		||||
#CONFIG_PCI_DEVICES=n
 | 
			
		||||
#CONFIG_PVPANIC=n
 | 
			
		||||
#CONFIG_QXL=n
 | 
			
		||||
#CONFIG_SEV=n
 | 
			
		||||
#CONFIG_SGA=n
 | 
			
		||||
#CONFIG_TEST_DEVICES=n
 | 
			
		||||
#CONFIG_TPM_CRB=n
 | 
			
		||||
#CONFIG_TPM_TIS=n
 | 
			
		||||
#CONFIG_VTD=n
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
CONFIG_ISAPC=y
 | 
			
		||||
include pci.mak
 | 
			
		||||
include sound.mak
 | 
			
		||||
include usb.mak
 | 
			
		||||
include hyperv.mak
 | 
			
		||||
CONFIG_QXL=$(CONFIG_SPICE)
 | 
			
		||||
CONFIG_VGA_ISA=y
 | 
			
		||||
CONFIG_VGA_CIRRUS=y
 | 
			
		||||
CONFIG_VMWARE_VGA=y
 | 
			
		||||
CONFIG_VMXNET3_PCI=y
 | 
			
		||||
CONFIG_VIRTIO_VGA=y
 | 
			
		||||
CONFIG_VMMOUSE=y
 | 
			
		||||
CONFIG_IPMI=y
 | 
			
		||||
CONFIG_IPMI_LOCAL=y
 | 
			
		||||
CONFIG_IPMI_EXTERN=y
 | 
			
		||||
CONFIG_ISA_IPMI_KCS=y
 | 
			
		||||
CONFIG_ISA_IPMI_BT=y
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_SERIAL_ISA=y
 | 
			
		||||
CONFIG_PARALLEL=y
 | 
			
		||||
CONFIG_I8254=y
 | 
			
		||||
CONFIG_PCSPK=y
 | 
			
		||||
CONFIG_PCKBD=y
 | 
			
		||||
CONFIG_FDC=y
 | 
			
		||||
CONFIG_ACPI=y
 | 
			
		||||
CONFIG_ACPI_X86=y
 | 
			
		||||
CONFIG_ACPI_X86_ICH=y
 | 
			
		||||
CONFIG_ACPI_MEMORY_HOTPLUG=y
 | 
			
		||||
CONFIG_ACPI_CPU_HOTPLUG=y
 | 
			
		||||
CONFIG_APM=y
 | 
			
		||||
CONFIG_I8257=y
 | 
			
		||||
CONFIG_IDE_ISA=y
 | 
			
		||||
CONFIG_IDE_PIIX=y
 | 
			
		||||
CONFIG_NE2000_ISA=y
 | 
			
		||||
CONFIG_HPET=y
 | 
			
		||||
CONFIG_APPLESMC=y
 | 
			
		||||
CONFIG_I8259=y
 | 
			
		||||
CONFIG_PFLASH_CFI01=y
 | 
			
		||||
CONFIG_TPM_TIS=$(CONFIG_TPM)
 | 
			
		||||
CONFIG_TPM_CRB=$(CONFIG_TPM)
 | 
			
		||||
CONFIG_MC146818RTC=y
 | 
			
		||||
CONFIG_PCI_PIIX=y
 | 
			
		||||
CONFIG_WDT_IB700=y
 | 
			
		||||
CONFIG_ISA_DEBUG=y
 | 
			
		||||
CONFIG_ISA_TESTDEV=y
 | 
			
		||||
CONFIG_VMPORT=y
 | 
			
		||||
CONFIG_SGA=y
 | 
			
		||||
CONFIG_LPC_ICH9=y
 | 
			
		||||
CONFIG_PCI_EXPRESS_Q35=y
 | 
			
		||||
CONFIG_APIC=y
 | 
			
		||||
CONFIG_IOAPIC=y
 | 
			
		||||
CONFIG_PVPANIC=y
 | 
			
		||||
CONFIG_MEM_DEVICE=y
 | 
			
		||||
CONFIG_DIMM=y
 | 
			
		||||
CONFIG_NVDIMM=y
 | 
			
		||||
CONFIG_ACPI_NVDIMM=y
 | 
			
		||||
CONFIG_PCIE_PORT=y
 | 
			
		||||
CONFIG_XIO3130=y
 | 
			
		||||
CONFIG_IOH3420=y
 | 
			
		||||
CONFIG_I82801B11=y
 | 
			
		||||
CONFIG_SMBIOS=y
 | 
			
		||||
CONFIG_PXB=y
 | 
			
		||||
CONFIG_ACPI_VMGENID=y
 | 
			
		||||
CONFIG_ACPI_SMBUS=y
 | 
			
		||||
CONFIG_SMBUS_EEPROM=y
 | 
			
		||||
CONFIG_FW_CFG_DMA=y
 | 
			
		||||
CONFIG_I2C=y
 | 
			
		||||
CONFIG_SEV=$(CONFIG_KVM)
 | 
			
		||||
CONFIG_VTD=y
 | 
			
		||||
CONFIG_AMD_IOMMU=y
 | 
			
		||||
CONFIG_PAM=y
 | 
			
		||||
CONFIG_I440FX=y
 | 
			
		||||
CONFIG_Q35=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
# Default configuration for lm32-softmmu
 | 
			
		||||
 | 
			
		||||
# Uncomment the following lines to disable these optional devices:
 | 
			
		||||
#
 | 
			
		||||
#CONFIG_MILKYMIST_TMU2=n        # disabling it actually causes compile-time failures
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
CONFIG_LM32=y
 | 
			
		||||
CONFIG_MILKYMIST=y
 | 
			
		||||
CONFIG_MILKYMIST_TMU2=$(call land,$(CONFIG_X11),$(CONFIG_OPENGL))
 | 
			
		||||
CONFIG_FRAMEBUFFER=y
 | 
			
		||||
CONFIG_PTIMER=y
 | 
			
		||||
CONFIG_PFLASH_CFI01=y
 | 
			
		||||
CONFIG_PFLASH_CFI02=y
 | 
			
		||||
CONFIG_SD=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
# Default configuration for m68k-softmmu
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
CONFIG_COLDFIRE=y
 | 
			
		||||
CONFIG_PTIMER=y
 | 
			
		||||
CONFIG_AN5206=y
 | 
			
		||||
CONFIG_MCF5208=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,15 @@
 | 
			
		||||
# Default configuration for microblaze-softmmu
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
CONFIG_PTIMER=y
 | 
			
		||||
CONFIG_PFLASH_CFI01=y
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_XILINX=y
 | 
			
		||||
CONFIG_XILINX_AXI=y
 | 
			
		||||
CONFIG_XILINX_SPI=y
 | 
			
		||||
CONFIG_XILINX_ETHLITE=y
 | 
			
		||||
CONFIG_SSI=y
 | 
			
		||||
CONFIG_SSI_M25P80=y
 | 
			
		||||
CONFIG_XLNX_ZYNQMP=y
 | 
			
		||||
CONFIG_PETALOGIX_S3ADSP1800=y
 | 
			
		||||
CONFIG_PETALOGIX_ML605=y
 | 
			
		||||
CONFIG_XLNX_ZYNQMP_PMU=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
# Common mips*-softmmu CONFIG defines
 | 
			
		||||
 | 
			
		||||
CONFIG_ISA_BUS=y
 | 
			
		||||
CONFIG_PCI=y
 | 
			
		||||
CONFIG_PCI_DEVICES=y
 | 
			
		||||
include pci.mak
 | 
			
		||||
include sound.mak
 | 
			
		||||
include usb.mak
 | 
			
		||||
CONFIG_ESP=y
 | 
			
		||||
CONFIG_SCSI=y
 | 
			
		||||
CONFIG_VGA_ISA=y
 | 
			
		||||
CONFIG_VGA_ISA_MM=y
 | 
			
		||||
CONFIG_VGA_CIRRUS=y
 | 
			
		||||
@@ -30,13 +31,13 @@ CONFIG_MIPSNET=y
 | 
			
		||||
CONFIG_PFLASH_CFI01=y
 | 
			
		||||
CONFIG_I8259=y
 | 
			
		||||
CONFIG_MC146818RTC=y
 | 
			
		||||
CONFIG_ISA_TESTDEV=y
 | 
			
		||||
CONFIG_EMPTY_SLOT=y
 | 
			
		||||
CONFIG_MIPS_CPS=y
 | 
			
		||||
CONFIG_MIPS_ITU=y
 | 
			
		||||
CONFIG_I2C=y
 | 
			
		||||
CONFIG_R4K=y
 | 
			
		||||
CONFIG_MALTA=y
 | 
			
		||||
CONFIG_PCNET_PCI=y
 | 
			
		||||
CONFIG_MIPSSIM=y
 | 
			
		||||
CONFIG_ACPI_SMBUS=y
 | 
			
		||||
CONFIG_SMBUS_EEPROM=y
 | 
			
		||||
CONFIG_TEST_DEVICES=y
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,10 @@ CONFIG_RC4030=y
 | 
			
		||||
CONFIG_DP8393X=y
 | 
			
		||||
CONFIG_DS1225Y=y
 | 
			
		||||
CONFIG_FULONG=y
 | 
			
		||||
CONFIG_ATI_VGA=y
 | 
			
		||||
CONFIG_RTL8139_PCI=y
 | 
			
		||||
CONFIG_JAZZ=y
 | 
			
		||||
CONFIG_G364FB=y
 | 
			
		||||
CONFIG_JAZZ_LED=y
 | 
			
		||||
CONFIG_VT82C686=y
 | 
			
		||||
CONFIG_AHCI=y
 | 
			
		||||
CONFIG_MIPS_BOSTON=y
 | 
			
		||||
CONFIG_FITLOADER=y
 | 
			
		||||
CONFIG_PCI_EXPRESS=y
 | 
			
		||||
CONFIG_PCI_EXPRESS_XILINX=y
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
# Default configuration for moxie-softmmu
 | 
			
		||||
 | 
			
		||||
# Boards:
 | 
			
		||||
#
 | 
			
		||||
CONFIG_ISA_BUS=y
 | 
			
		||||
CONFIG_MC146818RTC=y
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_SERIAL_ISA=y
 | 
			
		||||
CONFIG_VGA=y
 | 
			
		||||
CONFIG_MOXIESIM=y
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user