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,18 +408,17 @@ 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);
|
||||
dolog("channels | %10d | %10d\n",
|
||||
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("obtained: samples %ld\n", obt->samples);
|
||||
dolog ("parameter | requested value | obtained value\n");
|
||||
dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
|
||||
dolog ("channels | %10d | %10d\n",
|
||||
req->nchannels, obt->nchannels);
|
||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||
dolog ("============================================\n");
|
||||
dolog ("requested: buffer size %d period size %d\n",
|
||||
req->buffer_size, req->period_size);
|
||||
dolog ("obtained: samples %ld\n", obt->samples);
|
||||
}
|
||||
|
||||
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||
@@ -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,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||
Audiodev *dev)
|
||||
static int alsa_open (int in, struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||
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) {
|
||||
int dir = 0;
|
||||
unsigned int btime = apdo->buffer_length;
|
||||
if (req->buffer_size) {
|
||||
unsigned long obt;
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near(
|
||||
handle, hw_params, &btime, &dir);
|
||||
if (size_in_usec) {
|
||||
int dir = 0;
|
||||
unsigned int btime = req->buffer_size;
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near (
|
||||
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) {
|
||||
int dir = 0;
|
||||
unsigned int ptime = apdo->period_length;
|
||||
if (req->period_size) {
|
||||
unsigned long obt;
|
||||
|
||||
err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
|
||||
&dir);
|
||||
if (size_in_usec) {
|
||||
int dir = 0;
|
||||
unsigned int ptime = req->period_size;
|
||||
|
||||
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,
|
||||
|
||||
922
audio/audio.c
922
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,
|
||||
|
||||
236
audio/sdlaudio.c
236
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,36 +258,76 @@ 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;
|
||||
|
||||
to_mix = audio_MIN(samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
#if USE_SEMAPHORE
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip(buf, src, chunk);
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
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) {
|
||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
#if USE_SEMAPHORE
|
||||
again:
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
/* 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,13 +154,15 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
"Huge pages size (ex: 2M, 1G)",
|
||||
&error_abort);
|
||||
}
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "seal",
|
||||
"Seal growing & shrinking",
|
||||
&error_abort);
|
||||
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "seal",
|
||||
"Seal growing & shrinking",
|
||||
&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo memfd_backend_info = {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
477
block.c
477
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);
|
||||
bdrv_join_options(bs, options, old_options);
|
||||
qobject_unref(old_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);
|
||||
}
|
||||
/* 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);
|
||||
qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
|
||||
qdict_extract_subqdict(options, &new_child_options, child_key_dot);
|
||||
g_free(child_key_dot);
|
||||
}
|
||||
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,
|
||||
ignore_children, &local_err);
|
||||
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,11 +34,12 @@ 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) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
((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);
|
||||
ret = -1;
|
||||
goto out;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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,19 +1457,14 @@ 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) {
|
||||
return 0;
|
||||
}
|
||||
} while (errno == EINTR);
|
||||
do {
|
||||
uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes };
|
||||
if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) {
|
||||
return 0;
|
||||
}
|
||||
} while (errno == EINTR);
|
||||
|
||||
ret = translate_err(-errno);
|
||||
}
|
||||
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,39 +962,35 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
|
||||
if (bitmap == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
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. */
|
||||
if (!(bm->flags & BME_FLAG_AUTO)) {
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
bdrv_dirty_bitmap_set_persistance(bitmap, true);
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
needs_update = true;
|
||||
created_dirty_bitmaps =
|
||||
g_slist_append(created_dirty_bitmaps, bitmap);
|
||||
}
|
||||
if (!(bm->flags & BME_FLAG_AUTO)) {
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
created_dirty_bitmaps =
|
||||
g_slist_append(created_dirty_bitmaps, bitmap);
|
||||
}
|
||||
|
||||
if (needs_update && can_write(bs)) {
|
||||
/* in_use flags must be updated */
|
||||
int ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
goto fail;
|
||||
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) {
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
goto fail;
|
||||
}
|
||||
header_updated = true;
|
||||
} else {
|
||||
g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
|
||||
(gpointer)true);
|
||||
}
|
||||
header_updated = true;
|
||||
}
|
||||
|
||||
if (!can_write(bs)) {
|
||||
g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
|
||||
(gpointer)true);
|
||||
}
|
||||
|
||||
g_slist_free(created_dirty_bitmaps);
|
||||
@@ -1133,21 +1112,23 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||
if (bitmap == NULL) {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
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;
|
||||
}
|
||||
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
if (ro_dirty_bitmaps != NULL) {
|
||||
@@ -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 offset,
|
||||
int compressed_size,
|
||||
uint64_t *host_offset)
|
||||
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t 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,8 +1142,8 @@ 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)
|
||||
== offset_into_cluster(s, *host_offset));
|
||||
assert(*host_offset == 0 || offset_into_cluster(s, guest_offset)
|
||||
== offset_into_cluster(s, *host_offset));
|
||||
|
||||
/*
|
||||
* Calculate the number of clusters to look for. We stop at L2 slice
|
||||
@@ -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;
|
||||
}
|
||||
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,13 +1683,11 @@ 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,
|
||||
offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
ret = qcow2_inc_refcounts_imrt(bs, res,
|
||||
refcount_table, refcount_table_size,
|
||||
offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1889,20 +1868,16 @@ 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);
|
||||
if (ret < 0) {
|
||||
/* don't print message nor increment check_errors */
|
||||
continue;
|
||||
}
|
||||
ret = qcow2_get_refcount(bs,
|
||||
data_offset >> s->cluster_bits,
|
||||
&refcount);
|
||||
if (ret < 0) {
|
||||
/* 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: "
|
||||
@@ -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"
|
||||
@@ -198,16 +197,13 @@ enum {
|
||||
|
||||
/* Incompatible feature bits */
|
||||
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_DIRTY_BITNR = 0,
|
||||
QCOW2_INCOMPAT_CORRUPT_BITNR = 1,
|
||||
QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||
QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR,
|
||||
|
||||
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY
|
||||
| QCOW2_INCOMPAT_CORRUPT
|
||||
| QCOW2_INCOMPAT_DATA_FILE,
|
||||
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY
|
||||
| QCOW2_INCOMPAT_CORRUPT,
|
||||
};
|
||||
|
||||
/* Compatible feature bits */
|
||||
@@ -220,13 +216,10 @@ 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_BITMAPS_BITNR = 0,
|
||||
QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_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;
|
||||
}
|
||||
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 offset,
|
||||
int compressed_size,
|
||||
uint64_t *host_offset);
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t 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;
|
||||
}
|
||||
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"
|
||||
|
||||
376
configure
vendored
376
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
|
||||
for config in $mak_wilds; do
|
||||
default_target_list="${default_target_list} $(basename "$config" .mak)"
|
||||
done
|
||||
|
||||
# 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
|
||||
else
|
||||
tpm=yes
|
||||
fi
|
||||
elif test "$tpm" = "yes"; then
|
||||
if test "$mingw32" = "yes" ; then
|
||||
error_exit "TPM emulation only available on POSIX systems"
|
||||
fi
|
||||
if test "$targetos" = Linux && { test "$cpu" = i386 || test "$cpu" = x86_64; }; then
|
||||
tpm_passthrough=$tpm
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
dev->vq[index].kick_fd = vmsg->fds[0];
|
||||
DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
|
||||
dev->vq[index].call_fd = vmsg->fds[0];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
dev->vq[index].err_fd = vmsg->fds[0];
|
||||
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