Compare commits
5 Commits
qemu-sparc
...
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)
|
||||
|
||||
4
.gitignore
vendored
4
.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
|
||||
@@ -97,7 +95,6 @@
|
||||
*.gcno
|
||||
*.gcov
|
||||
/pc-bios/bios-pq/status
|
||||
/pc-bios/edk2-*.fd
|
||||
/pc-bios/vgabios-pq/status
|
||||
/pc-bios/optionrom/linuxboot.asm
|
||||
/pc-bios/optionrom/linuxboot.bin
|
||||
@@ -121,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*
|
||||
|
||||
@@ -71,18 +71,3 @@ build-clang:
|
||||
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
build-tci:
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
|
||||
- ./configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
||||
- make -j2
|
||||
- make tests/boot-serial-test tests/cdrom-test tests/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
|
||||
./tests/boot-serial-test || exit 1 ;
|
||||
./tests/cdrom-test || exit 1 ;
|
||||
done
|
||||
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test
|
||||
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow
|
||||
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -39,19 +39,13 @@
|
||||
url = https://git.qemu.org/git/capstone.git
|
||||
[submodule "roms/seabios-hppa"]
|
||||
path = roms/seabios-hppa
|
||||
url = https://git.qemu.org/git/seabios-hppa.git
|
||||
url = https://github.com/hdeller/seabios-hppa.git
|
||||
[submodule "roms/u-boot-sam460ex"]
|
||||
path = roms/u-boot-sam460ex
|
||||
url = https://git.qemu.org/git/u-boot-sam460ex.git
|
||||
[submodule "tests/fp/berkeley-testfloat-3"]
|
||||
path = tests/fp/berkeley-testfloat-3
|
||||
url = https://git.qemu.org/git/berkeley-testfloat-3.git
|
||||
url = https://github.com/cota/berkeley-testfloat-3
|
||||
[submodule "tests/fp/berkeley-softfloat-3"]
|
||||
path = tests/fp/berkeley-softfloat-3
|
||||
url = https://git.qemu.org/git/berkeley-softfloat-3.git
|
||||
[submodule "roms/edk2"]
|
||||
path = roms/edk2
|
||||
url = https://git.qemu.org/git/edk2.git
|
||||
[submodule "slirp"]
|
||||
path = slirp
|
||||
url = https://git.qemu.org/git/libslirp.git
|
||||
url = https://github.com/cota/berkeley-softfloat-3
|
||||
|
||||
63
.travis.yml
63
.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"
|
||||
@@ -223,8 +196,8 @@ matrix:
|
||||
|
||||
# Acceptance (Functional) tests
|
||||
- env:
|
||||
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make check-acceptance"
|
||||
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
|
||||
- TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -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"
|
||||
|
||||
47
CODING_STYLE
47
CODING_STYLE
@@ -29,45 +29,6 @@ Spaces of course are superior to tabs because:
|
||||
|
||||
Do not leave whitespace dangling off the ends of lines.
|
||||
|
||||
1.1 Multiline Indent
|
||||
|
||||
There are several places where indent is necessary:
|
||||
|
||||
- if/else
|
||||
- while/for
|
||||
- function definition & call
|
||||
|
||||
When breaking up a long line to fit within line width, we need a proper indent
|
||||
for the following lines.
|
||||
|
||||
In case of if/else, while/for, align the secondary lines just after the
|
||||
opening parenthesis of the first.
|
||||
|
||||
For example:
|
||||
|
||||
if (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
while (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
In case of function, there are several variants:
|
||||
|
||||
* 4 spaces indent from the beginning
|
||||
* align the secondary lines just after the opening parenthesis of the
|
||||
first
|
||||
|
||||
For example:
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, do_another(y,
|
||||
z));
|
||||
|
||||
2. Line width
|
||||
|
||||
Lines should be 80 characters; try not to make them longer.
|
||||
@@ -147,10 +108,10 @@ block to a separate function altogether.
|
||||
When comparing a variable for (in)equality with a constant, list the
|
||||
constant on the right, as in:
|
||||
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
|
||||
Besides, good compilers already warn users when '==' is mis-typed as '=',
|
||||
|
||||
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
|
||||
140
MAINTAINERS
140
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>
|
||||
@@ -663,14 +653,10 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/arm/nseries.c
|
||||
F: hw/display/blizzard.c
|
||||
F: hw/input/lm832x.c
|
||||
F: hw/input/tsc2005.c
|
||||
F: hw/misc/cbus.c
|
||||
F: hw/timer/twl92230.c
|
||||
F: include/hw/display/blizzard.h
|
||||
F: include/hw/input/tsc2xxx.h
|
||||
F: include/hw/misc/cbus.h
|
||||
|
||||
Palm
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
@@ -679,7 +665,6 @@ L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/arm/palm.c
|
||||
F: hw/input/tsc210x.c
|
||||
F: include/hw/input/tsc2xxx.h
|
||||
|
||||
Raspberry Pi
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -719,7 +704,6 @@ F: hw/misc/mst_fpga.c
|
||||
F: hw/misc/max111x.c
|
||||
F: include/hw/arm/pxa.h
|
||||
F: include/hw/arm/sharpsl.h
|
||||
F: include/hw/display/tc6393xb.h
|
||||
|
||||
SABRELITE / i.MX6
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -746,7 +730,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/stellaris*
|
||||
F: include/hw/input/gamepad.h
|
||||
|
||||
Versatile Express
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -868,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
|
||||
@@ -993,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
|
||||
@@ -1068,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*
|
||||
@@ -1075,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
|
||||
@@ -1127,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>
|
||||
@@ -1158,11 +1113,10 @@ F: include/hw/timer/sun4v-rtc.h
|
||||
|
||||
Leon3
|
||||
M: Fabien Chouteau <chouteau@adacore.com>
|
||||
M: KONRAD Frederic <frederic.konrad@adacore.com>
|
||||
S: Maintained
|
||||
F: hw/sparc/leon3.c
|
||||
F: hw/*/grlib*
|
||||
F: include/hw/*/grlib*
|
||||
F: include/hw/sparc/grlib.h
|
||||
|
||||
S390 Machines
|
||||
-------------
|
||||
@@ -1189,7 +1143,6 @@ S: Supported
|
||||
F: hw/s390x/ipl.*
|
||||
F: pc-bios/s390-ccw/
|
||||
F: pc-bios/s390-ccw.img
|
||||
F: docs/devel/s390-dasd-ipl.txt
|
||||
T: git https://github.com/borntraeger/qemu.git s390-next
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
@@ -1228,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>
|
||||
@@ -1392,13 +1341,6 @@ F: include/hw/net/
|
||||
F: tests/virtio-net-test.c
|
||||
T: git https://github.com/jasowang/qemu.git net
|
||||
|
||||
Parallel NOR Flash devices
|
||||
M: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
T: git https://gitlab.com/philmd/qemu.git pflash-next
|
||||
S: Maintained
|
||||
F: hw/block/pflash_cfi*.c
|
||||
F: include/hw/block/flash.h
|
||||
|
||||
SCSI
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Fam Zheng <fam@euphon.net>
|
||||
@@ -1461,7 +1403,6 @@ S: Supported
|
||||
F: hw/vfio/ccw.c
|
||||
F: hw/s390x/s390-ccw.c
|
||||
F: include/hw/s390x/s390-ccw.h
|
||||
F: include/hw/s390x/vfio-ccw.h
|
||||
T: git https://github.com/cohuck/qemu.git s390-next
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
@@ -1483,11 +1424,8 @@ 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-*/
|
||||
F: backends/vhost-user.c
|
||||
F: include/sysemu/vhost-user-backend.h
|
||||
|
||||
virtio
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
@@ -1529,7 +1467,6 @@ L: qemu-s390x@nongnu.org
|
||||
virtio-input
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/input/vhost-user-input.c
|
||||
F: hw/input/virtio-input*.c
|
||||
F: include/hw/virtio/virtio-input.h
|
||||
|
||||
@@ -1833,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
|
||||
@@ -1859,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/
|
||||
|
||||
@@ -1915,8 +1852,6 @@ F: hmp.[ch]
|
||||
F: hmp-commands*.hx
|
||||
F: include/monitor/hmp-target.h
|
||||
F: tests/test-hmp.c
|
||||
F: include/qemu/qemu-print.h
|
||||
F: util/qemu-print.c
|
||||
|
||||
Network device backends
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
@@ -1997,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
|
||||
@@ -2046,7 +1977,6 @@ M: Laurent Vivier <lvivier@redhat.com>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: qtest.c
|
||||
F: accel/qtest.c
|
||||
F: tests/libqtest.*
|
||||
F: tests/libqos/
|
||||
F: tests/*-test.c
|
||||
@@ -2127,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
|
||||
@@ -2202,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
|
||||
@@ -2233,30 +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
|
||||
|
||||
EDK2 Firmware
|
||||
M: Laszlo Ersek <lersek@redhat.com>
|
||||
M: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
S: Supported
|
||||
F: pc-bios/descriptors/??-edk2-*.json
|
||||
F: pc-bios/edk2-*
|
||||
F: roms/Makefile.edk2
|
||||
F: roms/edk2
|
||||
F: roms/edk2-*
|
||||
F: tests/data/uefi-boot-images/
|
||||
F: tests/uefi-test-tools/
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
@@ -2302,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
|
||||
@@ -2321,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
|
||||
@@ -2415,13 +2319,12 @@ F: block/ssh.c
|
||||
|
||||
CURL
|
||||
L: qemu-block@nongnu.org
|
||||
S: Odd Fixes
|
||||
S: Supported
|
||||
F: block/curl.c
|
||||
|
||||
GLUSTER
|
||||
L: qemu-block@nongnu.org
|
||||
L: integration@gluster.org
|
||||
S: Odd Fixes
|
||||
S: Supported
|
||||
F: block/gluster.c
|
||||
|
||||
Null Block Driver
|
||||
@@ -2594,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
|
||||
@@ -2622,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
|
||||
|
||||
181
Makefile
181
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
|
||||
@@ -296,10 +282,6 @@ ui/input-keymap-%.c: $(KEYCODEMAP_GEN) $(KEYCODEMAP_CSV) $(SRC_PATH)/ui/Makefile
|
||||
$(KEYCODEMAP_GEN): .git-submodule-status
|
||||
$(KEYCODEMAP_CSV): .git-submodule-status
|
||||
|
||||
edk2-decompressed = $(basename $(wildcard pc-bios/edk2-*.fd.bz2))
|
||||
pc-bios/edk2-%.fd: pc-bios/edk2-%.fd.bz2
|
||||
$(call quiet-command,bzip2 -d -c $< > $@,"BUNZIP2",$<)
|
||||
|
||||
# Don't try to regenerate Makefile or configure
|
||||
# We don't generate any of them
|
||||
Makefile: ;
|
||||
@@ -331,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 > $@, \
|
||||
@@ -347,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 $@; \
|
||||
@@ -419,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 $@; \
|
||||
@@ -449,7 +429,6 @@ $(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||
$(SOFTMMU_SUBDIR_RULES): $(edk2-decompressed)
|
||||
|
||||
subdir-%:
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
|
||||
@@ -479,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) $(CFLAGS)" LDFLAGS="$(LDFLAGS)")
|
||||
|
||||
$(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))
|
||||
@@ -503,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)
|
||||
|
||||
######################################################################
|
||||
|
||||
@@ -633,13 +609,8 @@ 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 {} +
|
||||
rm -f $(edk2-decompressed)
|
||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga$(EXESUF) TAGS cscope.* *.pod *~ */*~
|
||||
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
|
||||
rm -f ui/shader/*-vert.h ui/shader/*-frag.h
|
||||
@@ -662,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
|
||||
@@ -698,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
|
||||
@@ -729,30 +681,12 @@ spapr-rtas.bin slof.bin skiboot.lid \
|
||||
palcode-clipper \
|
||||
u-boot.e500 u-boot-sam460-20100605.bin \
|
||||
qemu_vga.ndrv \
|
||||
edk2-licenses.txt \
|
||||
hppa-firmware.img
|
||||
|
||||
DESCS=50-edk2-i386-secure.json 50-edk2-x86_64-secure.json \
|
||||
60-edk2-aarch64.json 60-edk2-arm.json 60-edk2-i386.json 60-edk2-x86_64.json
|
||||
else
|
||||
BLOBS=
|
||||
DESCS=
|
||||
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)"
|
||||
@@ -797,8 +731,7 @@ endif
|
||||
|
||||
ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir \
|
||||
$(if $(INSTALL_BLOBS),$(edk2-decompressed))
|
||||
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
|
||||
ifneq ($(TOOLS),)
|
||||
$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
|
||||
endif
|
||||
@@ -820,21 +753,6 @@ ifneq ($(BLOBS),)
|
||||
set -e; for x in $(BLOBS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||
done
|
||||
endif
|
||||
ifdef INSTALL_BLOBS
|
||||
set -e; for x in $(edk2-decompressed); do \
|
||||
$(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||
done
|
||||
endif
|
||||
ifneq ($(DESCS),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/firmware"
|
||||
set -e; tmpf=$$(mktemp); trap 'rm -f -- "$$tmpf"' EXIT; \
|
||||
for x in $(DESCS); do \
|
||||
sed -e 's,@DATADIR@,$(DESTDIR)$(qemu_datadir),' \
|
||||
"$(SRC_PATH)/pc-bios/descriptors/$$x" > "$$tmpf"; \
|
||||
$(INSTALL_DATA) "$$tmpf" \
|
||||
"$(DESTDIR)$(qemu_datadir)/firmware/$$x"; \
|
||||
done
|
||||
endif
|
||||
for s in $(ICON_SIZES); do \
|
||||
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \
|
||||
@@ -899,14 +817,11 @@ ui/shader.o: $(SRC_PATH)/ui/shader.c \
|
||||
MAKEINFO=makeinfo
|
||||
MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
|
||||
MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
|
||||
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) -DVERSION="$(VERSION)" -DCONFDIR="$(qemu_confdir)"
|
||||
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)"
|
||||
TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
|
||||
|
||||
docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
|
||||
$(call quiet-command,(\
|
||||
echo "@set VERSION $(VERSION)" && \
|
||||
echo "@set CONFDIR $(qemu_confdir)" \
|
||||
)> $@,"GEN","$@")
|
||||
docs/version.texi: $(SRC_PATH)/VERSION
|
||||
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
|
||||
|
||||
%.html: %.texi docs/version.texi
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||
@@ -922,23 +837,6 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
|
||||
%.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) -W -n -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","$@")
|
||||
|
||||
@@ -967,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
|
||||
@@ -976,7 +874,7 @@ qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
|
||||
docs/qemu-cpu-models.texi docs/security.texi
|
||||
docs/qemu-cpu-models.texi
|
||||
|
||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||
@@ -995,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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
|
||||
obj-$(CONFIG_KVM) += kvm/
|
||||
obj-$(CONFIG_TCG) += tcg/
|
||||
obj-y += stubs/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -107,6 +103,11 @@ void configure_accelerator(MachineState *ms, const char *progname)
|
||||
if (!acc) {
|
||||
continue;
|
||||
}
|
||||
if (acc->available && !acc->available()) {
|
||||
printf("%s not supported for this target\n",
|
||||
acc->name);
|
||||
continue;
|
||||
}
|
||||
ret = accel_init_machine(acc, ms);
|
||||
if (ret < 0) {
|
||||
init_failed = true;
|
||||
|
||||
@@ -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);
|
||||
@@ -1798,7 +1798,7 @@ static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run)
|
||||
if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) {
|
||||
fprintf(stderr, "emulation failure\n");
|
||||
if (!kvm_arch_stop_on_emulation_error(cpu)) {
|
||||
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
|
||||
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);
|
||||
return EXCP_INTERRUPT;
|
||||
}
|
||||
}
|
||||
@@ -2089,7 +2089,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (ret < 0) {
|
||||
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
|
||||
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);
|
||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* QTest accelerator code
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
static int qtest_init_accel(MachineState *ms)
|
||||
{
|
||||
QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0,
|
||||
&error_abort);
|
||||
qemu_opt_set(opts, "shift", "0", &error_abort);
|
||||
configure_icount(opts, &error_abort);
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qtest_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "QTest";
|
||||
ac->init_machine = qtest_init_accel;
|
||||
ac->allowed = &qtest_allowed;
|
||||
}
|
||||
|
||||
#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest")
|
||||
|
||||
static const TypeInfo qtest_accel_type = {
|
||||
.name = TYPE_QTEST_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = qtest_accel_class_init,
|
||||
};
|
||||
|
||||
static void qtest_type_init(void)
|
||||
{
|
||||
type_register_static(&qtest_accel_type);
|
||||
}
|
||||
|
||||
type_init(qtest_type_init);
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -855,28 +855,10 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
|
||||
return ram_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: tlb_fill() can trigger a resize of the TLB. This means that all of the
|
||||
* caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must
|
||||
* be discarded and looked up again (e.g. via tlb_entry()).
|
||||
*/
|
||||
static void tlb_fill(CPUState *cpu, target_ulong addr, int size,
|
||||
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
bool ok;
|
||||
|
||||
/*
|
||||
* This is not a probe, so only valid return is success; failure
|
||||
* should result in exception + longjmp to the cpu loop.
|
||||
*/
|
||||
ok = cc->tlb_fill(cpu, addr, size, access_type, mmu_idx, false, retaddr);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
int mmu_idx, target_ulong addr, uintptr_t retaddr,
|
||||
MMUAccessType access_type, int size)
|
||||
int mmu_idx,
|
||||
target_ulong addr, uintptr_t retaddr,
|
||||
bool recheck, MMUAccessType access_type, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr mr_offset;
|
||||
@@ -886,6 +868,29 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
bool locked = false;
|
||||
MemTxResult r;
|
||||
|
||||
if (recheck) {
|
||||
/*
|
||||
* This is a TLB_RECHECK access, where the MMU protection
|
||||
* covers a smaller range than a target page, and we must
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
CPUTLBEntry *entry;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
|
||||
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
tlb_addr = entry->addr_read;
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access */
|
||||
uintptr_t haddr = addr + entry->addend;
|
||||
|
||||
return ldn_p((void *)haddr, size);
|
||||
}
|
||||
/* Fall through for handling IO accesses */
|
||||
}
|
||||
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
@@ -919,8 +924,9 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
}
|
||||
|
||||
static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
int mmu_idx, uint64_t val, target_ulong addr,
|
||||
uintptr_t retaddr, int size)
|
||||
int mmu_idx,
|
||||
uint64_t val, target_ulong addr,
|
||||
uintptr_t retaddr, bool recheck, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr mr_offset;
|
||||
@@ -929,6 +935,30 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
bool locked = false;
|
||||
MemTxResult r;
|
||||
|
||||
if (recheck) {
|
||||
/*
|
||||
* This is a TLB_RECHECK access, where the MMU protection
|
||||
* covers a smaller range than a target page, and we must
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
CPUTLBEntry *entry;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
tlb_addr = tlb_addr_write(entry);
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access */
|
||||
uintptr_t haddr = addr + entry->addend;
|
||||
|
||||
stn_p((void *)haddr, size, val);
|
||||
return;
|
||||
}
|
||||
/* Fall through for handling IO accesses */
|
||||
}
|
||||
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
@@ -957,16 +987,6 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
}
|
||||
}
|
||||
|
||||
static inline target_ulong tlb_read_ofs(CPUTLBEntry *entry, size_t ofs)
|
||||
{
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
return *(target_ulong *)((uintptr_t)entry + ofs);
|
||||
#else
|
||||
/* ofs might correspond to .addr_write, so use atomic_read */
|
||||
return atomic_read((target_ulong *)((uintptr_t)entry + ofs));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return true if ADDR is present in the victim tlb, and has been copied
|
||||
back to the main tlb. */
|
||||
static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
@@ -977,7 +997,14 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
assert_cpu_is_self(ENV_GET_CPU(env));
|
||||
for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) {
|
||||
CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx];
|
||||
target_ulong cmp = tlb_read_ofs(vtlb, elt_ofs);
|
||||
target_ulong cmp;
|
||||
|
||||
/* elt_ofs might correspond to .addr_write, so use atomic_read */
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
|
||||
#else
|
||||
cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs));
|
||||
#endif
|
||||
|
||||
if (cmp == page) {
|
||||
/* Found entry in victim tlb, swap tlb and iotlb. */
|
||||
@@ -1061,56 +1088,6 @@ void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
|
||||
}
|
||||
}
|
||||
|
||||
void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
||||
MMUAccessType access_type, int mmu_idx)
|
||||
{
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
uintptr_t tlb_addr, page;
|
||||
size_t elt_ofs;
|
||||
|
||||
switch (access_type) {
|
||||
case MMU_DATA_LOAD:
|
||||
elt_ofs = offsetof(CPUTLBEntry, addr_read);
|
||||
break;
|
||||
case MMU_DATA_STORE:
|
||||
elt_ofs = offsetof(CPUTLBEntry, addr_write);
|
||||
break;
|
||||
case MMU_INST_FETCH:
|
||||
elt_ofs = offsetof(CPUTLBEntry, addr_code);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
page = addr & TARGET_PAGE_MASK;
|
||||
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
||||
|
||||
if (!tlb_hit_page(tlb_addr, page)) {
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
|
||||
if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page)) {
|
||||
CPUState *cs = ENV_GET_CPU(env);
|
||||
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||
|
||||
if (!cc->tlb_fill(cs, addr, 0, access_type, mmu_idx, true, 0)) {
|
||||
/* Non-faulting page table read failed. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TLB resize via tlb_fill may have moved the entry. */
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
||||
}
|
||||
|
||||
if (tlb_addr & ~TARGET_PAGE_MASK) {
|
||||
/* IO access */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (void *)((uintptr_t)addr + entry->addend);
|
||||
}
|
||||
|
||||
/* Probe for a read-modify-write atomic operation. Do not allow unaligned
|
||||
* operations, or io operations to proceed. Return the host address. */
|
||||
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
@@ -1190,481 +1167,26 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
}
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
#define NEED_BE_BSWAP 0
|
||||
#define NEED_LE_BSWAP 1
|
||||
# define TGT_BE(X) (X)
|
||||
# define TGT_LE(X) BSWAP(X)
|
||||
#else
|
||||
#define NEED_BE_BSWAP 1
|
||||
#define NEED_LE_BSWAP 0
|
||||
# define TGT_BE(X) BSWAP(X)
|
||||
# define TGT_LE(X) (X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Byte Swap Helper
|
||||
*
|
||||
* This should all dead code away depending on the build host and
|
||||
* access type.
|
||||
*/
|
||||
#define MMUSUFFIX _mmu
|
||||
|
||||
static inline uint64_t handle_bswap(uint64_t val, int size, bool big_endian)
|
||||
{
|
||||
if ((big_endian && NEED_BE_BSWAP) || (!big_endian && NEED_LE_BSWAP)) {
|
||||
switch (size) {
|
||||
case 1: return val;
|
||||
case 2: return bswap16(val);
|
||||
case 4: return bswap32(val);
|
||||
case 8: return bswap64(val);
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
#define DATA_SIZE 1
|
||||
#include "softmmu_template.h"
|
||||
|
||||
/*
|
||||
* Load Helpers
|
||||
*
|
||||
* We support two different access types. SOFTMMU_CODE_ACCESS is
|
||||
* specifically for reading instructions from system memory. It is
|
||||
* called by the translation loop and in some helpers where the code
|
||||
* is disassembled. It shouldn't be called directly by guest code.
|
||||
*/
|
||||
#define DATA_SIZE 2
|
||||
#include "softmmu_template.h"
|
||||
|
||||
typedef uint64_t FullLoadHelper(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr);
|
||||
#define DATA_SIZE 4
|
||||
#include "softmmu_template.h"
|
||||
|
||||
static inline uint64_t __attribute__((always_inline))
|
||||
load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi,
|
||||
uintptr_t retaddr, size_t size, bool big_endian, bool code_read,
|
||||
FullLoadHelper *full_load)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = code_read ? entry->addr_code : entry->addr_read;
|
||||
const size_t tlb_off = code_read ?
|
||||
offsetof(CPUTLBEntry, addr_code) : offsetof(CPUTLBEntry, addr_read);
|
||||
const MMUAccessType access_type =
|
||||
code_read ? MMU_INST_FETCH : MMU_DATA_LOAD;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
void *haddr;
|
||||
uint64_t res;
|
||||
|
||||
/* Handle CPU specific unaligned behaviour */
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, access_type,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!victim_tlb_hit(env, mmu_idx, index, tlb_off,
|
||||
addr & TARGET_PAGE_MASK)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size,
|
||||
access_type, mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = code_read ? entry->addr_code : entry->addr_read;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (size - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
if (tlb_addr & TLB_RECHECK) {
|
||||
/*
|
||||
* This is a TLB_RECHECK access, where the MMU protection
|
||||
* covers a smaller range than a target page, and we must
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size,
|
||||
access_type, mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
|
||||
tlb_addr = code_read ? entry->addr_code : entry->addr_read;
|
||||
tlb_addr &= ~TLB_RECHECK;
|
||||
if (!(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
/* RAM access */
|
||||
goto do_aligned_access;
|
||||
}
|
||||
}
|
||||
|
||||
res = io_readx(env, &env->iotlb[mmu_idx][index], mmu_idx, addr,
|
||||
retaddr, access_type, size);
|
||||
return handle_bswap(res, size, big_endian);
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (size > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + size - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
target_ulong addr1, addr2;
|
||||
tcg_target_ulong r1, r2;
|
||||
unsigned shift;
|
||||
do_unaligned_access:
|
||||
addr1 = addr & ~(size - 1);
|
||||
addr2 = addr1 + size;
|
||||
r1 = full_load(env, addr1, oi, retaddr);
|
||||
r2 = full_load(env, addr2, oi, retaddr);
|
||||
shift = (addr & (size - 1)) * 8;
|
||||
|
||||
if (big_endian) {
|
||||
/* Big-endian combine. */
|
||||
res = (r1 << shift) | (r2 >> ((size * 8) - shift));
|
||||
} else {
|
||||
/* Little-endian combine. */
|
||||
res = (r1 >> shift) | (r2 << ((size * 8) - shift));
|
||||
}
|
||||
return res & MAKE_64BIT_MASK(0, size * 8);
|
||||
}
|
||||
|
||||
do_aligned_access:
|
||||
haddr = (void *)((uintptr_t)addr + entry->addend);
|
||||
switch (size) {
|
||||
case 1:
|
||||
res = ldub_p(haddr);
|
||||
break;
|
||||
case 2:
|
||||
if (big_endian) {
|
||||
res = lduw_be_p(haddr);
|
||||
} else {
|
||||
res = lduw_le_p(haddr);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (big_endian) {
|
||||
res = (uint32_t)ldl_be_p(haddr);
|
||||
} else {
|
||||
res = (uint32_t)ldl_le_p(haddr);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (big_endian) {
|
||||
res = ldq_be_p(haddr);
|
||||
} else {
|
||||
res = ldq_le_p(haddr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the benefit of TCG generated code, we want to avoid the
|
||||
* complication of ABI-specific return type promotion and always
|
||||
* return a value extended to the register size of the host. This is
|
||||
* tcg_target_long, except in the case of a 32-bit host and 64-bit
|
||||
* data, and for that we always have uint64_t.
|
||||
*
|
||||
* We don't bother with this widened value for SOFTMMU_CODE_ACCESS.
|
||||
*/
|
||||
|
||||
static uint64_t full_ldub_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 1, false, false,
|
||||
full_ldub_mmu);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_ldub_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_le_lduw_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 2, false, false,
|
||||
full_le_lduw_mmu);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_le_lduw_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_be_lduw_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 2, true, false,
|
||||
full_be_lduw_mmu);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_be_lduw_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_le_ldul_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 4, false, false,
|
||||
full_le_ldul_mmu);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_le_ldul_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_be_ldul_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 4, true, false,
|
||||
full_be_ldul_mmu);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_be_ldul_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 8, false, false,
|
||||
helper_le_ldq_mmu);
|
||||
}
|
||||
|
||||
uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 8, true, false,
|
||||
helper_be_ldq_mmu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide signed versions of the load routines as well. We can of course
|
||||
* avoid this for 64-bit data, or for 32-bit data on 32-bit host.
|
||||
*/
|
||||
|
||||
|
||||
tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int8_t)helper_ret_ldub_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int16_t)helper_le_lduw_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int16_t)helper_be_lduw_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int32_t)helper_le_ldul_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int32_t)helper_be_ldul_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store Helpers
|
||||
*/
|
||||
|
||||
static inline void __attribute__((always_inline))
|
||||
store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr, size_t size, bool big_endian)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
const size_t tlb_off = offsetof(CPUTLBEntry, addr_write);
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
void *haddr;
|
||||
|
||||
/* Handle CPU specific unaligned behaviour */
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!victim_tlb_hit(env, mmu_idx, index, tlb_off,
|
||||
addr & TARGET_PAGE_MASK)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (size - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
if (tlb_addr & TLB_RECHECK) {
|
||||
/*
|
||||
* This is a TLB_RECHECK access, where the MMU protection
|
||||
* covers a smaller range than a target page, and we must
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
|
||||
tlb_addr = tlb_addr_write(entry);
|
||||
tlb_addr &= ~TLB_RECHECK;
|
||||
if (!(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
/* RAM access */
|
||||
goto do_aligned_access;
|
||||
}
|
||||
}
|
||||
|
||||
io_writex(env, &env->iotlb[mmu_idx][index], mmu_idx,
|
||||
handle_bswap(val, size, big_endian),
|
||||
addr, retaddr, size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (size > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + size - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i;
|
||||
uintptr_t index2;
|
||||
CPUTLBEntry *entry2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
do_unaligned_access:
|
||||
/*
|
||||
* Ensure the second page is in the TLB. Note that the first page
|
||||
* is already guaranteed to be filled, and that the second page
|
||||
* cannot evict the first.
|
||||
*/
|
||||
page2 = (addr + size) & TARGET_PAGE_MASK;
|
||||
index2 = tlb_index(env, mmu_idx, page2);
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
tlb_addr2 = tlb_addr_write(entry2);
|
||||
if (!tlb_hit_page(tlb_addr2, page2)
|
||||
&& !victim_tlb_hit(env, mmu_idx, index2, tlb_off,
|
||||
page2 & TARGET_PAGE_MASK)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, size, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: not efficient, but simple.
|
||||
* This loop must go in the forward direction to avoid issues
|
||||
* with self-modifying code in Windows 64-bit.
|
||||
*/
|
||||
for (i = 0; i < size; ++i) {
|
||||
uint8_t val8;
|
||||
if (big_endian) {
|
||||
/* Big-endian extract. */
|
||||
val8 = val >> (((size - 1) * 8) - (i * 8));
|
||||
} else {
|
||||
/* Little-endian extract. */
|
||||
val8 = val >> (i * 8);
|
||||
}
|
||||
helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
do_aligned_access:
|
||||
haddr = (void *)((uintptr_t)addr + entry->addend);
|
||||
switch (size) {
|
||||
case 1:
|
||||
stb_p(haddr, val);
|
||||
break;
|
||||
case 2:
|
||||
if (big_endian) {
|
||||
stw_be_p(haddr, val);
|
||||
} else {
|
||||
stw_le_p(haddr, val);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (big_endian) {
|
||||
stl_be_p(haddr, val);
|
||||
} else {
|
||||
stl_le_p(haddr, val);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (big_endian) {
|
||||
stq_be_p(haddr, val);
|
||||
} else {
|
||||
stq_le_p(haddr, val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 1, false);
|
||||
}
|
||||
|
||||
void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 2, false);
|
||||
}
|
||||
|
||||
void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 2, true);
|
||||
}
|
||||
|
||||
void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 4, false);
|
||||
}
|
||||
|
||||
void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 4, true);
|
||||
}
|
||||
|
||||
void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 8, false);
|
||||
}
|
||||
|
||||
void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
store_helper(env, addr, val, oi, retaddr, 8, true);
|
||||
}
|
||||
#define DATA_SIZE 8
|
||||
#include "softmmu_template.h"
|
||||
|
||||
/* First set of helpers allows passing in of OI and RETADDR. This makes
|
||||
them callable from other helpers. */
|
||||
@@ -1725,81 +1247,20 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
|
||||
|
||||
/* Code access functions. */
|
||||
|
||||
static uint64_t full_ldub_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 1, false, true,
|
||||
full_ldub_cmmu);
|
||||
}
|
||||
#undef MMUSUFFIX
|
||||
#define MMUSUFFIX _cmmu
|
||||
#undef GETPC
|
||||
#define GETPC() ((uintptr_t)0)
|
||||
#define SOFTMMU_CODE_ACCESS
|
||||
|
||||
uint8_t helper_ret_ldb_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_ldub_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
#define DATA_SIZE 1
|
||||
#include "softmmu_template.h"
|
||||
|
||||
static uint64_t full_le_lduw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 2, false, true,
|
||||
full_le_lduw_cmmu);
|
||||
}
|
||||
#define DATA_SIZE 2
|
||||
#include "softmmu_template.h"
|
||||
|
||||
uint16_t helper_le_ldw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_le_lduw_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
#define DATA_SIZE 4
|
||||
#include "softmmu_template.h"
|
||||
|
||||
static uint64_t full_be_lduw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 2, true, true,
|
||||
full_be_lduw_cmmu);
|
||||
}
|
||||
|
||||
uint16_t helper_be_ldw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_be_lduw_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_le_ldul_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 4, false, true,
|
||||
full_le_ldul_cmmu);
|
||||
}
|
||||
|
||||
uint32_t helper_le_ldl_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_le_ldul_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_be_ldul_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 4, true, true,
|
||||
full_be_ldul_cmmu);
|
||||
}
|
||||
|
||||
uint32_t helper_be_ldl_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_be_ldul_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
uint64_t helper_le_ldq_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 8, false, true,
|
||||
helper_le_ldq_cmmu);
|
||||
}
|
||||
|
||||
uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, 8, true, true,
|
||||
helper_be_ldq_cmmu);
|
||||
}
|
||||
#define DATA_SIZE 8
|
||||
#include "softmmu_template.h"
|
||||
|
||||
454
accel/tcg/softmmu_template.h
Normal file
454
accel/tcg/softmmu_template.h
Normal file
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Software MMU support
|
||||
*
|
||||
* Generate helpers used by TCG for qemu_ld/st ops and code load
|
||||
* functions.
|
||||
*
|
||||
* Included from target op helpers and exec.c.
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if DATA_SIZE == 8
|
||||
#define SUFFIX q
|
||||
#define LSUFFIX q
|
||||
#define SDATA_TYPE int64_t
|
||||
#define DATA_TYPE uint64_t
|
||||
#elif DATA_SIZE == 4
|
||||
#define SUFFIX l
|
||||
#define LSUFFIX l
|
||||
#define SDATA_TYPE int32_t
|
||||
#define DATA_TYPE uint32_t
|
||||
#elif DATA_SIZE == 2
|
||||
#define SUFFIX w
|
||||
#define LSUFFIX uw
|
||||
#define SDATA_TYPE int16_t
|
||||
#define DATA_TYPE uint16_t
|
||||
#elif DATA_SIZE == 1
|
||||
#define SUFFIX b
|
||||
#define LSUFFIX ub
|
||||
#define SDATA_TYPE int8_t
|
||||
#define DATA_TYPE uint8_t
|
||||
#else
|
||||
#error unsupported data size
|
||||
#endif
|
||||
|
||||
|
||||
/* For the benefit of TCG generated code, we want to avoid the complication
|
||||
of ABI-specific return type promotion and always return a value extended
|
||||
to the register size of the host. This is tcg_target_long, except in the
|
||||
case of a 32-bit host and 64-bit data, and for that we always have
|
||||
uint64_t. Don't bother with this widened value for SOFTMMU_CODE_ACCESS. */
|
||||
#if defined(SOFTMMU_CODE_ACCESS) || DATA_SIZE == 8
|
||||
# define WORD_TYPE DATA_TYPE
|
||||
# define USUFFIX SUFFIX
|
||||
#else
|
||||
# define WORD_TYPE tcg_target_ulong
|
||||
# define USUFFIX glue(u, SUFFIX)
|
||||
# define SSUFFIX glue(s, SUFFIX)
|
||||
#endif
|
||||
|
||||
#ifdef SOFTMMU_CODE_ACCESS
|
||||
#define READ_ACCESS_TYPE MMU_INST_FETCH
|
||||
#define ADDR_READ addr_code
|
||||
#else
|
||||
#define READ_ACCESS_TYPE MMU_DATA_LOAD
|
||||
#define ADDR_READ addr_read
|
||||
#endif
|
||||
|
||||
#if DATA_SIZE == 8
|
||||
# define BSWAP(X) bswap64(X)
|
||||
#elif DATA_SIZE == 4
|
||||
# define BSWAP(X) bswap32(X)
|
||||
#elif DATA_SIZE == 2
|
||||
# define BSWAP(X) bswap16(X)
|
||||
#else
|
||||
# define BSWAP(X) (X)
|
||||
#endif
|
||||
|
||||
#if DATA_SIZE == 1
|
||||
# define helper_le_ld_name glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_be_ld_name helper_le_ld_name
|
||||
# define helper_le_lds_name glue(glue(helper_ret_ld, SSUFFIX), MMUSUFFIX)
|
||||
# define helper_be_lds_name helper_le_lds_name
|
||||
# define helper_le_st_name glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)
|
||||
# define helper_be_st_name helper_le_st_name
|
||||
#else
|
||||
# define helper_le_ld_name glue(glue(helper_le_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_be_ld_name glue(glue(helper_be_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_le_lds_name glue(glue(helper_le_ld, SSUFFIX), MMUSUFFIX)
|
||||
# define helper_be_lds_name glue(glue(helper_be_ld, SSUFFIX), MMUSUFFIX)
|
||||
# define helper_le_st_name glue(glue(helper_le_st, SUFFIX), MMUSUFFIX)
|
||||
# define helper_be_st_name glue(glue(helper_be_st, SUFFIX), MMUSUFFIX)
|
||||
#endif
|
||||
|
||||
#ifndef SOFTMMU_CODE_ACCESS
|
||||
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||
size_t mmu_idx, size_t index,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr,
|
||||
bool recheck,
|
||||
MMUAccessType access_type)
|
||||
{
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
|
||||
access_type, DATA_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = entry->ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = entry->ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
||||
tlb_addr & TLB_RECHECK,
|
||||
READ_ACCESS_TYPE);
|
||||
res = TGT_LE(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
target_ulong addr1, addr2;
|
||||
DATA_TYPE res1, res2;
|
||||
unsigned shift;
|
||||
do_unaligned_access:
|
||||
addr1 = addr & ~(DATA_SIZE - 1);
|
||||
addr2 = addr1 + DATA_SIZE;
|
||||
res1 = helper_le_ld_name(env, addr1, oi, retaddr);
|
||||
res2 = helper_le_ld_name(env, addr2, oi, retaddr);
|
||||
shift = (addr & (DATA_SIZE - 1)) * 8;
|
||||
|
||||
/* Little-endian combine. */
|
||||
res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
#if DATA_SIZE == 1
|
||||
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
||||
#else
|
||||
res = glue(glue(ld, LSUFFIX), _le_p)((uint8_t *)haddr);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = entry->ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = entry->ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
||||
tlb_addr & TLB_RECHECK,
|
||||
READ_ACCESS_TYPE);
|
||||
res = TGT_BE(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
target_ulong addr1, addr2;
|
||||
DATA_TYPE res1, res2;
|
||||
unsigned shift;
|
||||
do_unaligned_access:
|
||||
addr1 = addr & ~(DATA_SIZE - 1);
|
||||
addr2 = addr1 + DATA_SIZE;
|
||||
res1 = helper_be_ld_name(env, addr1, oi, retaddr);
|
||||
res2 = helper_be_ld_name(env, addr2, oi, retaddr);
|
||||
shift = (addr & (DATA_SIZE - 1)) * 8;
|
||||
|
||||
/* Big-endian combine. */
|
||||
res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
||||
return res;
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#ifndef SOFTMMU_CODE_ACCESS
|
||||
|
||||
/* Provide signed versions of the load routines as well. We can of course
|
||||
avoid this for 64-bit data, or for 32-bit data on 32-bit host. */
|
||||
#if DATA_SIZE * 8 < TCG_TARGET_REG_BITS
|
||||
WORD_TYPE helper_le_lds_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (SDATA_TYPE)helper_le_ld_name(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
# if DATA_SIZE > 1
|
||||
WORD_TYPE helper_be_lds_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (SDATA_TYPE)helper_be_ld_name(env, addr, oi, retaddr);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||
size_t mmu_idx, size_t index,
|
||||
DATA_TYPE val,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr,
|
||||
bool recheck)
|
||||
{
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
|
||||
recheck, DATA_SIZE);
|
||||
}
|
||||
|
||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
val = TGT_LE(val);
|
||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
|
||||
retaddr, tlb_addr & TLB_RECHECK);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i;
|
||||
target_ulong page2;
|
||||
CPUTLBEntry *entry2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* XXX: not efficient, but simple. */
|
||||
/* This loop must go in the forward direction to avoid issues
|
||||
with self-modifying code in Windows 64-bit. */
|
||||
for (i = 0; i < DATA_SIZE; ++i) {
|
||||
/* Little-endian extract. */
|
||||
uint8_t val8 = val >> (i * 8);
|
||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||
oi, retaddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
#if DATA_SIZE == 1
|
||||
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
||||
#else
|
||||
glue(glue(st, SUFFIX), _le_p)((uint8_t *)haddr, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
val = TGT_BE(val);
|
||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
|
||||
tlb_addr & TLB_RECHECK);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i;
|
||||
target_ulong page2;
|
||||
CPUTLBEntry *entry2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* XXX: not efficient, but simple */
|
||||
/* This loop must go in the forward direction to avoid issues
|
||||
with self-modifying code. */
|
||||
for (i = 0; i < DATA_SIZE; ++i) {
|
||||
/* Big-endian extract. */
|
||||
uint8_t val8 = val >> (((DATA_SIZE - 1) * 8) - (i * 8));
|
||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||
oi, retaddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
|
||||
|
||||
#undef READ_ACCESS_TYPE
|
||||
#undef DATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef LSUFFIX
|
||||
#undef DATA_SIZE
|
||||
#undef ADDR_READ
|
||||
#undef WORD_TYPE
|
||||
#undef SDATA_TYPE
|
||||
#undef USUFFIX
|
||||
#undef SSUFFIX
|
||||
#undef BSWAP
|
||||
#undef helper_le_ld_name
|
||||
#undef helper_be_ld_name
|
||||
#undef helper_le_lds_name
|
||||
#undef helper_be_lds_name
|
||||
#undef helper_le_st_name
|
||||
#undef helper_be_st_name
|
||||
@@ -398,54 +398,6 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs8)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
|
||||
int8_t aa = *(int8_t *)(a + i);
|
||||
*(int8_t *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs16)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
|
||||
int16_t aa = *(int16_t *)(a + i);
|
||||
*(int16_t *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs32)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||
int32_t aa = *(int32_t *)(a + i);
|
||||
*(int32_t *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs64)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||
int64_t aa = *(int64_t *)(a + i);
|
||||
*(int64_t *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_mov)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
@@ -773,150 +725,6 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl8v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
uint8_t sh = *(uint8_t *)(b + i) & 7;
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl16v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl32v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl64v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr8v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
uint8_t sh = *(uint8_t *)(b + i) & 7;
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr16v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr32v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr64v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar8v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
uint8_t sh = *(uint8_t *)(b + i) & 7;
|
||||
*(int8_t *)(d + i) = *(int8_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar16v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(int16_t *)(d + i) = *(int16_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar32v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(int32_t *)(d + i) = *(int32_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(int64_t *)(d + i) = *(int64_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
/* If vectors are enabled, the compiler fills in -1 for true.
|
||||
Otherwise, we must take care of this by hand. */
|
||||
#ifdef CONFIG_VECTOR16
|
||||
|
||||
@@ -225,11 +225,6 @@ DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_abs8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_abs16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_abs32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_abs64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
@@ -259,21 +254,6 @@ DEF_HELPER_FLAGS_3(gvec_sar16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_shl8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_shr8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shr16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shr32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shr64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_sar8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include "translate-all.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/log.h"
|
||||
@@ -1674,7 +1673,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
tb_page_addr_t phys_pc, phys_page2;
|
||||
target_ulong virt_page2;
|
||||
tcg_insn_unit *gen_code_buf;
|
||||
int gen_code_size, search_size, max_insns;
|
||||
int gen_code_size, search_size;
|
||||
#ifdef CONFIG_PROFILER
|
||||
TCGProfile *prof = &tcg_ctx->prof;
|
||||
int64_t ti;
|
||||
@@ -1692,17 +1691,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
cflags &= ~CF_CLUSTER_MASK;
|
||||
cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||
|
||||
max_insns = cflags & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = CF_COUNT_MASK;
|
||||
}
|
||||
if (max_insns > TCG_MAX_INSNS) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
if (cpu->singlestep_enabled || singlestep) {
|
||||
max_insns = 1;
|
||||
}
|
||||
|
||||
buffer_overflow:
|
||||
tb = tb_alloc(pc);
|
||||
if (unlikely(!tb)) {
|
||||
@@ -1722,7 +1710,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
tb->cflags = cflags;
|
||||
tb->trace_vcpu_dstate = *cpu->trace_dstate;
|
||||
tcg_ctx->tb_cflags = cflags;
|
||||
tb_overflow:
|
||||
|
||||
#ifdef CONFIG_PROFILER
|
||||
/* includes aborted translations because of exceptions */
|
||||
@@ -1733,7 +1720,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
tcg_func_start(tcg_ctx);
|
||||
|
||||
tcg_ctx->cpu = ENV_GET_CPU(env);
|
||||
gen_intermediate_code(cpu, tb, max_insns);
|
||||
gen_intermediate_code(cpu, tb);
|
||||
tcg_ctx->cpu = NULL;
|
||||
|
||||
trace_translate_block(tb, tb->pc, tb->tc.ptr);
|
||||
@@ -1756,39 +1743,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
ti = profile_getclock();
|
||||
#endif
|
||||
|
||||
/* ??? Overflow could be handled better here. In particular, we
|
||||
don't need to re-do gen_intermediate_code, nor should we re-do
|
||||
the tcg optimization currently hidden inside tcg_gen_code. All
|
||||
that should be required is to flush the TBs, allocate a new TB,
|
||||
re-initialize it per above, and re-do the actual code generation. */
|
||||
gen_code_size = tcg_gen_code(tcg_ctx, tb);
|
||||
if (unlikely(gen_code_size < 0)) {
|
||||
switch (gen_code_size) {
|
||||
case -1:
|
||||
/*
|
||||
* Overflow of code_gen_buffer, or the current slice of it.
|
||||
*
|
||||
* TODO: We don't need to re-do gen_intermediate_code, nor
|
||||
* should we re-do the tcg optimization currently hidden
|
||||
* inside tcg_gen_code. All that should be required is to
|
||||
* flush the TBs, allocate a new TB, re-initialize it per
|
||||
* above, and re-do the actual code generation.
|
||||
*/
|
||||
goto buffer_overflow;
|
||||
|
||||
case -2:
|
||||
/*
|
||||
* The code generated for the TranslationBlock is too large.
|
||||
* The maximum size allowed by the unwind info is 64k.
|
||||
* There may be stricter constraints from relocations
|
||||
* in the tcg backend.
|
||||
*
|
||||
* Try again with half as many insns as we attempted this time.
|
||||
* If a single insn overflows, there's a bug somewhere...
|
||||
*/
|
||||
max_insns = tb->icount;
|
||||
assert(max_insns > 1);
|
||||
max_insns /= 2;
|
||||
goto tb_overflow;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
goto buffer_overflow;
|
||||
}
|
||||
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
|
||||
if (unlikely(search_size < 0)) {
|
||||
@@ -2252,7 +2214,8 @@ void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr)
|
||||
tb_jmp_cache_clear_page(cpu, addr);
|
||||
}
|
||||
|
||||
static void print_qht_statistics(struct qht_stats hst)
|
||||
static void print_qht_statistics(FILE *f, fprintf_function cpu_fprintf,
|
||||
struct qht_stats hst)
|
||||
{
|
||||
uint32_t hgram_opts;
|
||||
size_t hgram_bins;
|
||||
@@ -2261,7 +2224,7 @@ static void print_qht_statistics(struct qht_stats hst)
|
||||
if (!hst.head_buckets) {
|
||||
return;
|
||||
}
|
||||
qemu_printf("TB hash buckets %zu/%zu (%0.2f%% head buckets used)\n",
|
||||
cpu_fprintf(f, "TB hash buckets %zu/%zu (%0.2f%% head buckets used)\n",
|
||||
hst.used_head_buckets, hst.head_buckets,
|
||||
(double)hst.used_head_buckets / hst.head_buckets * 100);
|
||||
|
||||
@@ -2271,7 +2234,7 @@ static void print_qht_statistics(struct qht_stats hst)
|
||||
hgram_opts |= QDIST_PR_NODECIMAL;
|
||||
}
|
||||
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
|
||||
qemu_printf("TB hash occupancy %0.2f%% avg chain occ. Histogram: %s\n",
|
||||
cpu_fprintf(f, "TB hash occupancy %0.2f%% avg chain occ. Histogram: %s\n",
|
||||
qdist_avg(&hst.occupancy) * 100, hgram);
|
||||
g_free(hgram);
|
||||
|
||||
@@ -2284,7 +2247,7 @@ static void print_qht_statistics(struct qht_stats hst)
|
||||
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
|
||||
}
|
||||
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
|
||||
qemu_printf("TB hash avg chain %0.3f buckets. Histogram: %s\n",
|
||||
cpu_fprintf(f, "TB hash avg chain %0.3f buckets. Histogram: %s\n",
|
||||
qdist_avg(&hst.chain), hgram);
|
||||
g_free(hgram);
|
||||
}
|
||||
@@ -2322,7 +2285,7 @@ static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
|
||||
return false;
|
||||
}
|
||||
|
||||
void dump_exec_info(void)
|
||||
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
||||
{
|
||||
struct tb_tree_stats tst = {};
|
||||
struct qht_stats hst;
|
||||
@@ -2331,49 +2294,48 @@ void dump_exec_info(void)
|
||||
tcg_tb_foreach(tb_tree_stats_iter, &tst);
|
||||
nb_tbs = tst.nb_tbs;
|
||||
/* XXX: avoid using doubles ? */
|
||||
qemu_printf("Translation buffer state:\n");
|
||||
cpu_fprintf(f, "Translation buffer state:\n");
|
||||
/*
|
||||
* Report total code size including the padding and TB structs;
|
||||
* otherwise users might think "-tb-size" is not honoured.
|
||||
* For avg host size we use the precise numbers from tb_tree_stats though.
|
||||
*/
|
||||
qemu_printf("gen code size %zu/%zu\n",
|
||||
cpu_fprintf(f, "gen code size %zu/%zu\n",
|
||||
tcg_code_size(), tcg_code_capacity());
|
||||
qemu_printf("TB count %zu\n", nb_tbs);
|
||||
qemu_printf("TB avg target size %zu max=%zu bytes\n",
|
||||
cpu_fprintf(f, "TB count %zu\n", nb_tbs);
|
||||
cpu_fprintf(f, "TB avg target size %zu max=%zu bytes\n",
|
||||
nb_tbs ? tst.target_size / nb_tbs : 0,
|
||||
tst.max_target_size);
|
||||
qemu_printf("TB avg host size %zu bytes (expansion ratio: %0.1f)\n",
|
||||
cpu_fprintf(f, "TB avg host size %zu bytes (expansion ratio: %0.1f)\n",
|
||||
nb_tbs ? tst.host_size / nb_tbs : 0,
|
||||
tst.target_size ? (double)tst.host_size / tst.target_size : 0);
|
||||
qemu_printf("cross page TB count %zu (%zu%%)\n", tst.cross_page,
|
||||
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
|
||||
qemu_printf("direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n",
|
||||
cpu_fprintf(f, "cross page TB count %zu (%zu%%)\n", tst.cross_page,
|
||||
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
|
||||
cpu_fprintf(f, "direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n",
|
||||
tst.direct_jmp_count,
|
||||
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
|
||||
tst.direct_jmp2_count,
|
||||
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
|
||||
|
||||
qht_statistics_init(&tb_ctx.htable, &hst);
|
||||
print_qht_statistics(hst);
|
||||
print_qht_statistics(f, cpu_fprintf, hst);
|
||||
qht_statistics_destroy(&hst);
|
||||
|
||||
qemu_printf("\nStatistics:\n");
|
||||
qemu_printf("TB flush count %u\n",
|
||||
cpu_fprintf(f, "\nStatistics:\n");
|
||||
cpu_fprintf(f, "TB flush count %u\n",
|
||||
atomic_read(&tb_ctx.tb_flush_count));
|
||||
qemu_printf("TB invalidate count %zu\n",
|
||||
tcg_tb_phys_invalidate_count());
|
||||
cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count());
|
||||
|
||||
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
|
||||
qemu_printf("TLB full flushes %zu\n", flush_full);
|
||||
qemu_printf("TLB partial flushes %zu\n", flush_part);
|
||||
qemu_printf("TLB elided flushes %zu\n", flush_elide);
|
||||
tcg_dump_info();
|
||||
cpu_fprintf(f, "TLB full flushes %zu\n", flush_full);
|
||||
cpu_fprintf(f, "TLB partial flushes %zu\n", flush_part);
|
||||
cpu_fprintf(f, "TLB elided flushes %zu\n", flush_elide);
|
||||
tcg_dump_info(f, cpu_fprintf);
|
||||
}
|
||||
|
||||
void dump_opcount_info(void)
|
||||
void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
|
||||
{
|
||||
tcg_dump_op_count();
|
||||
tcg_dump_op_count(f, cpu_fprintf);
|
||||
}
|
||||
|
||||
#else /* CONFIG_USER_ONLY */
|
||||
|
||||
@@ -32,7 +32,7 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
}
|
||||
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb, int max_insns)
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
|
||||
@@ -42,9 +42,20 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
db->pc_next = db->pc_first;
|
||||
db->is_jmp = DISAS_NEXT;
|
||||
db->num_insns = 0;
|
||||
db->max_insns = max_insns;
|
||||
db->singlestep_enabled = cpu->singlestep_enabled;
|
||||
|
||||
/* Instruction counting */
|
||||
db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
||||
if (db->max_insns == 0) {
|
||||
db->max_insns = CF_COUNT_MASK;
|
||||
}
|
||||
if (db->max_insns > TCG_MAX_INSNS) {
|
||||
db->max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
if (db->singlestep_enabled || singlestep) {
|
||||
db->max_insns = 1;
|
||||
}
|
||||
|
||||
ops->init_disas_context(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
{
|
||||
CPUState *cpu = current_cpu;
|
||||
CPUClass *cc;
|
||||
int ret;
|
||||
unsigned long address = (unsigned long)info->si_addr;
|
||||
MMUAccessType access_type;
|
||||
|
||||
/* We must handle PC addresses from two different sources:
|
||||
* a call return address and a signal frame address.
|
||||
@@ -147,17 +147,35 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
are still valid segv ones */
|
||||
address = h2g_nocheck(address);
|
||||
|
||||
/*
|
||||
* There is no way the target can handle this other than raising
|
||||
* an exception. Undo signal and retaddr state prior to longjmp.
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
/* see if it is an MMU fault */
|
||||
g_assert(cc->handle_mmu_fault);
|
||||
ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX);
|
||||
|
||||
if (ret == 0) {
|
||||
/* The MMU fault was handled without causing real CPU fault.
|
||||
* Retain helper_retaddr for a possible second fault.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* All other paths lead to cpu_exit; clear helper_retaddr
|
||||
* for next execution.
|
||||
*/
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
helper_retaddr = 0;
|
||||
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
||||
cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc);
|
||||
g_assert_not_reached();
|
||||
if (ret < 0) {
|
||||
return 0; /* not an MMU fault */
|
||||
}
|
||||
|
||||
/* Now we have a real cpu fault. */
|
||||
cpu_restore_state(cpu, pc, true);
|
||||
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
cpu_loop_exit(cpu);
|
||||
|
||||
/* never comes here */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/base.h"
|
||||
#include "trace.h"
|
||||
#include "authz/trace.h"
|
||||
|
||||
bool qauthz_is_allowed(QAuthZ *authz,
|
||||
const char *identity,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/list.h"
|
||||
#include "trace.h"
|
||||
#include "authz/trace.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/qapi-visit-authz.h"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/listfile.h"
|
||||
#include "trace.h"
|
||||
#include "authz/trace.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/sockets.h"
|
||||
@@ -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)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/pamacct.h"
|
||||
#include "trace.h"
|
||||
#include "authz/trace.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/simple.h"
|
||||
#include "trace.h"
|
||||
#include "authz/trace.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
static bool qauthz_simple_is_allowed(QAuthZ *authz,
|
||||
|
||||
@@ -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,11 +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-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* QEMU vhost-user backend
|
||||
*
|
||||
* Copyright (C) 2018 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "sysemu/vhost-user-backend.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "io/channel-command.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
|
||||
static bool
|
||||
ioeventfd_enabled(void)
|
||||
{
|
||||
return kvm_enabled() && kvm_eventfds_enabled();
|
||||
}
|
||||
|
||||
int
|
||||
vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
|
||||
unsigned nvqs, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(!b->vdev && vdev);
|
||||
|
||||
if (!ioeventfd_enabled()) {
|
||||
error_setg(errp, "vhost initialization failed: requires kvm");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->vdev = vdev;
|
||||
b->dev.nvqs = nvqs;
|
||||
b->dev.vqs = g_new(struct vhost_virtqueue, nvqs);
|
||||
|
||||
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "vhost initialization failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vhost_user_backend_start(VhostUserBackend *b)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
int ret, i ;
|
||||
|
||||
if (b->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!k->set_guest_notifiers) {
|
||||
error_report("binding does not support guest notifiers");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = vhost_dev_enable_notifiers(&b->dev, b->vdev);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
|
||||
if (ret < 0) {
|
||||
error_report("Error binding guest notifier");
|
||||
goto err_host_notifiers;
|
||||
}
|
||||
|
||||
b->dev.acked_features = b->vdev->guest_features;
|
||||
ret = vhost_dev_start(&b->dev, b->vdev);
|
||||
if (ret < 0) {
|
||||
error_report("Error start vhost dev");
|
||||
goto err_guest_notifiers;
|
||||
}
|
||||
|
||||
/* guest_notifier_mask/pending not used yet, so just unmask
|
||||
* everything here. virtio-pci will do the right thing by
|
||||
* enabling/disabling irqfd.
|
||||
*/
|
||||
for (i = 0; i < b->dev.nvqs; i++) {
|
||||
vhost_virtqueue_mask(&b->dev, b->vdev,
|
||||
b->dev.vq_index + i, false);
|
||||
}
|
||||
|
||||
b->started = true;
|
||||
return;
|
||||
|
||||
err_guest_notifiers:
|
||||
k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false);
|
||||
err_host_notifiers:
|
||||
vhost_dev_disable_notifiers(&b->dev, b->vdev);
|
||||
}
|
||||
|
||||
void
|
||||
vhost_user_backend_stop(VhostUserBackend *b)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
int ret = 0;
|
||||
|
||||
if (!b->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
vhost_dev_stop(&b->dev, b->vdev);
|
||||
|
||||
if (k->set_guest_notifiers) {
|
||||
ret = k->set_guest_notifiers(qbus->parent,
|
||||
b->dev.nvqs, false);
|
||||
if (ret < 0) {
|
||||
error_report("vhost guest notifier cleanup failed: %d", ret);
|
||||
}
|
||||
}
|
||||
assert(ret >= 0);
|
||||
|
||||
vhost_dev_disable_notifiers(&b->dev, b->vdev);
|
||||
b->started = false;
|
||||
}
|
||||
|
||||
static void set_chardev(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
|
||||
Chardev *chr;
|
||||
|
||||
if (b->completed) {
|
||||
error_setg(errp, QERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(b->chr_name);
|
||||
b->chr_name = g_strdup(value);
|
||||
|
||||
chr = qemu_chr_find(b->chr_name);
|
||||
if (chr == NULL) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
"Chardev '%s' not found", b->chr_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
b->completed = true;
|
||||
/* could call vhost_dev_init() so early message can be exchanged */
|
||||
}
|
||||
|
||||
static char *get_chardev(Object *obj, Error **errp)
|
||||
{
|
||||
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
|
||||
Chardev *chr = qemu_chr_fe_get_driver(&b->chr);
|
||||
|
||||
if (chr && chr->label) {
|
||||
return g_strdup(chr->label);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vhost_user_backend_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL);
|
||||
}
|
||||
|
||||
static void vhost_user_backend_finalize(Object *obj)
|
||||
{
|
||||
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
|
||||
|
||||
g_free(b->dev.vqs);
|
||||
g_free(b->chr_name);
|
||||
|
||||
vhost_user_cleanup(&b->vhost_user);
|
||||
qemu_chr_fe_deinit(&b->chr, true);
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_user_backend_info = {
|
||||
.name = TYPE_VHOST_USER_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(VhostUserBackend),
|
||||
.instance_init = vhost_user_backend_init,
|
||||
.instance_finalize = vhost_user_backend_finalize,
|
||||
.class_size = sizeof(VhostUserBackendClass),
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&vhost_user_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
507
block.c
507
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;
|
||||
@@ -950,9 +945,8 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options,
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||
|
||||
/* Copy the read-only and discard options from the parent */
|
||||
/* Copy the read-only option from the parent */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_DISCARD);
|
||||
|
||||
/* aio=native doesn't work for cache.direct=off, so disable it for the
|
||||
* temporary snapshot */
|
||||
@@ -1164,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;
|
||||
}
|
||||
|
||||
@@ -1692,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;
|
||||
@@ -1743,10 +1743,11 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||
uint64_t parent_perm, uint64_t parent_shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
assert(bs->drv && bs->drv->bdrv_child_perm);
|
||||
bs->drv->bdrv_child_perm(bs, c, role, reopen_queue,
|
||||
parent_perm, parent_shared,
|
||||
nperm, nshared);
|
||||
if (bs->drv && bs->drv->bdrv_child_perm) {
|
||||
bs->drv->bdrv_child_perm(bs, c, role, reopen_queue,
|
||||
parent_perm, parent_shared,
|
||||
nperm, nshared);
|
||||
}
|
||||
/* TODO Take force_share from reopen_queue */
|
||||
if (child_bs && child_bs->force_share) {
|
||||
*nshared = BLK_PERM_ALL;
|
||||
@@ -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;
|
||||
@@ -4082,14 +3790,14 @@ static void bdrv_delete(BlockDriverState *bs)
|
||||
assert(bdrv_op_blocker_is_empty(bs));
|
||||
assert(!bs->refcnt);
|
||||
|
||||
bdrv_close(bs);
|
||||
|
||||
/* remove from list, if necessary */
|
||||
if (bs->node_name[0] != '\0') {
|
||||
QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list);
|
||||
}
|
||||
QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list);
|
||||
|
||||
bdrv_close(bs);
|
||||
|
||||
g_free(bs);
|
||||
}
|
||||
|
||||
@@ -4121,7 +3829,7 @@ typedef struct CheckCo {
|
||||
int ret;
|
||||
} CheckCo;
|
||||
|
||||
static void coroutine_fn bdrv_check_co_entry(void *opaque)
|
||||
static void bdrv_check_co_entry(void *opaque)
|
||||
{
|
||||
CheckCo *cco = opaque;
|
||||
cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -5671,6 +5298,10 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
BdrvAioNotifier *baf, *baf_tmp;
|
||||
BdrvChild *child;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!bs->walking_aio_notifiers);
|
||||
bs->walking_aio_notifiers = true;
|
||||
QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) {
|
||||
@@ -5685,7 +5316,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
*/
|
||||
bs->walking_aio_notifiers = false;
|
||||
|
||||
if (bs->drv && bs->drv->bdrv_detach_aio_context) {
|
||||
if (bs->drv->bdrv_detach_aio_context) {
|
||||
bs->drv->bdrv_detach_aio_context(bs);
|
||||
}
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
@@ -5704,6 +5335,10 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
BdrvAioNotifier *ban, *ban_tmp;
|
||||
BdrvChild *child;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bs->quiesce_counter) {
|
||||
aio_disable_external(new_context);
|
||||
}
|
||||
@@ -5713,7 +5348,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_attach_aio_context(child->bs, new_context);
|
||||
}
|
||||
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
|
||||
if (bs->drv->bdrv_attach_aio_context) {
|
||||
bs->drv->bdrv_attach_aio_context(bs, new_context);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||
void **bounce_buffer)
|
||||
{
|
||||
int ret;
|
||||
QEMUIOVector qiov;
|
||||
BlockBackend *blk = job->common.blk;
|
||||
int nbytes;
|
||||
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
|
||||
@@ -117,8 +118,9 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||
if (!*bounce_buffer) {
|
||||
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||
}
|
||||
qemu_iovec_init_buf(&qiov, *bounce_buffer, nbytes);
|
||||
|
||||
ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
|
||||
ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_read_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
@@ -127,13 +129,13 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (buffer_is_zero(*bounce_buffer, nbytes)) {
|
||||
if (qemu_iovec_is_zero(&qiov)) {
|
||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||
nbytes, write_flags | BDRV_REQ_MAY_UNMAP);
|
||||
qiov.size, write_flags | BDRV_REQ_MAY_UNMAP);
|
||||
} else {
|
||||
ret = blk_co_pwrite(job->target, start,
|
||||
nbytes, *bounce_buffer, write_flags |
|
||||
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
|
||||
ret = blk_co_pwritev(job->target, start,
|
||||
qiov.size, &qiov, write_flags |
|
||||
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
|
||||
}
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_write_fail(job, start, ret);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -48,15 +47,16 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||
void *buf)
|
||||
{
|
||||
int ret = 0;
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||
|
||||
assert(bytes < SIZE_MAX);
|
||||
|
||||
ret = blk_co_pread(bs, offset, bytes, buf, 0);
|
||||
ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blk_co_pwrite(base, offset, bytes, buf, 0);
|
||||
ret = blk_co_pwritev(base, offset, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -68,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);
|
||||
@@ -87,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);
|
||||
@@ -303,14 +296,23 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
commit_top_bs->total_sectors = top->total_sectors;
|
||||
bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top));
|
||||
|
||||
bdrv_append(commit_top_bs, top, &local_err);
|
||||
bdrv_set_backing_hd(commit_top_bs, top, &local_err);
|
||||
if (local_err) {
|
||||
bdrv_unref(commit_top_bs);
|
||||
commit_top_bs = NULL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
bdrv_replace_node(top, commit_top_bs, &local_err);
|
||||
if (local_err) {
|
||||
bdrv_unref(commit_top_bs);
|
||||
commit_top_bs = NULL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->commit_top_bs = commit_top_bs;
|
||||
bdrv_unref(commit_top_bs);
|
||||
|
||||
/* Block all nodes between top and base, because they will
|
||||
* disappear from the chain after this operation. */
|
||||
@@ -328,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;
|
||||
@@ -365,18 +362,12 @@ 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);
|
||||
}
|
||||
if (s->top) {
|
||||
blk_unref(s->top);
|
||||
}
|
||||
if (s->base_read_only) {
|
||||
bdrv_reopen_set_read_only(base, true, NULL);
|
||||
}
|
||||
job_early_fail(&s->common.job);
|
||||
/* commit_top_bs has to be replaced after deleting the block job,
|
||||
* otherwise this would fail because of lack of permissions. */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BLOCK_CRYPTO_H
|
||||
#define BLOCK_CRYPTO_H
|
||||
#ifndef BLOCK_CRYPTO_H__
|
||||
#define BLOCK_CRYPTO_H__
|
||||
|
||||
#define BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, helpstr) \
|
||||
{ \
|
||||
@@ -94,4 +94,4 @@ block_crypto_create_opts_init(QDict *opts, Error **errp);
|
||||
QCryptoBlockOpenOptions *
|
||||
block_crypto_open_opts_init(QDict *opts, Error **errp);
|
||||
|
||||
#endif /* BLOCK_CRYPTO_H */
|
||||
#endif /* BLOCK_CRYPTO_H__ */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
59
block/io.c
59
block/io.c
@@ -837,6 +837,42 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
|
||||
return rwco.ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a synchronous request using coroutines
|
||||
*/
|
||||
static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf,
|
||||
int nb_sectors, bool is_write, BdrvRequestFlags flags)
|
||||
{
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf,
|
||||
nb_sectors * BDRV_SECTOR_SIZE);
|
||||
|
||||
if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS,
|
||||
&qiov, is_write, flags);
|
||||
}
|
||||
|
||||
/* return < 0 if error. See bdrv_write() for the return codes */
|
||||
int bdrv_read(BdrvChild *child, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
return bdrv_rw_co(child, sector_num, buf, nb_sectors, false, 0);
|
||||
}
|
||||
|
||||
/* Return < 0 if error. Important errors are:
|
||||
-EIO generic I/O error (may happen for all errors)
|
||||
-ENOMEDIUM No media inserted.
|
||||
-EINVAL Invalid sector number or nb_sectors
|
||||
-EACCES Trying to write a read-only device
|
||||
*/
|
||||
int bdrv_write(BdrvChild *child, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
return bdrv_rw_co(child, sector_num, (uint8_t *)buf, nb_sectors, true, 0);
|
||||
}
|
||||
|
||||
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
|
||||
int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
@@ -873,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) {
|
||||
@@ -881,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;
|
||||
@@ -899,7 +939,6 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
|
||||
return qiov->size;
|
||||
}
|
||||
|
||||
/* See bdrv_pwrite() for the return codes */
|
||||
int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes)
|
||||
{
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||
@@ -923,12 +962,6 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
|
||||
return qiov->size;
|
||||
}
|
||||
|
||||
/* Return no. of bytes on success or < 0 on error. Important errors are:
|
||||
-EIO generic I/O error (may happen for all errors)
|
||||
-ENOMEDIUM No media inserted.
|
||||
-EINVAL Invalid offset or number of bytes
|
||||
-EACCES Trying to write a read-only device
|
||||
*/
|
||||
int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes)
|
||||
{
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||
@@ -986,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;
|
||||
@@ -1033,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;
|
||||
@@ -1440,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;
|
||||
@@ -1487,7 +1514,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||
assert(!bs->supported_zero_flags);
|
||||
}
|
||||
|
||||
if (ret < 0 && !(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;
|
||||
|
||||
@@ -2926,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;
|
||||
|
||||
@@ -220,18 +220,20 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
||||
if (bs->backing) {
|
||||
int64_t nb_cow_sectors = to_allocate * s->tracks;
|
||||
int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS;
|
||||
void *buf = qemu_blockalign(bs, nb_cow_bytes);
|
||||
QEMUIOVector qiov =
|
||||
QEMU_IOVEC_INIT_BUF(qiov, qemu_blockalign(bs, nb_cow_bytes),
|
||||
nb_cow_bytes);
|
||||
|
||||
ret = bdrv_co_pread(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
||||
nb_cow_bytes, buf, 0);
|
||||
ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
||||
nb_cow_bytes, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
qemu_vfree(buf);
|
||||
qemu_vfree(qemu_iovec_buf(&qiov));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
|
||||
nb_cow_bytes, buf, 0);
|
||||
qemu_vfree(buf);
|
||||
nb_cow_bytes, &qiov, 0);
|
||||
qemu_vfree(qemu_iovec_buf(&qiov));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
177
block/qapi.c
177
block/qapi.c
@@ -36,7 +36,6 @@
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qmp/qnum.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
@@ -494,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,
|
||||
@@ -631,17 +630,48 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
return head;
|
||||
}
|
||||
|
||||
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
#define NB_SUFFIXES 4
|
||||
|
||||
static char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
||||
{
|
||||
char date_buf[128], clock_buf[128];
|
||||
static const char suffixes[NB_SUFFIXES] = {'K', 'M', 'G', 'T'};
|
||||
int64_t base;
|
||||
int i;
|
||||
|
||||
if (size <= 999) {
|
||||
snprintf(buf, buf_size, "%" PRId64, size);
|
||||
} else {
|
||||
base = 1024;
|
||||
for (i = 0; i < NB_SUFFIXES; i++) {
|
||||
if (size < (10 * base)) {
|
||||
snprintf(buf, buf_size, "%0.1f%c",
|
||||
(double)size / base,
|
||||
suffixes[i]);
|
||||
break;
|
||||
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
|
||||
snprintf(buf, buf_size, "%" PRId64 "%c",
|
||||
((size + (base >> 1)) / base),
|
||||
suffixes[i]);
|
||||
break;
|
||||
}
|
||||
base = base * 1024;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
||||
QEMUSnapshotInfo *sn)
|
||||
{
|
||||
char buf1[128], date_buf[128], clock_buf[128];
|
||||
struct tm tm;
|
||||
time_t ti;
|
||||
int64_t secs;
|
||||
char *sizing = NULL;
|
||||
|
||||
if (!sn) {
|
||||
qemu_printf("%-10s%-20s%7s%20s%15s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||
func_fprintf(f,
|
||||
"%-10s%-20s%7s%20s%15s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||
} else {
|
||||
ti = sn->date_sec;
|
||||
localtime_r(&ti, &tm);
|
||||
@@ -654,47 +684,50 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
(int)((secs / 60) % 60),
|
||||
(int)(secs % 60),
|
||||
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
||||
sizing = size_to_str(sn->vm_state_size);
|
||||
qemu_printf("%-10s%-20s%7s%20s%15s",
|
||||
sn->id_str, sn->name,
|
||||
sizing,
|
||||
date_buf,
|
||||
clock_buf);
|
||||
func_fprintf(f,
|
||||
"%-10s%-20s%7s%20s%15s",
|
||||
sn->id_str, sn->name,
|
||||
get_human_readable_size(buf1, sizeof(buf1),
|
||||
sn->vm_state_size),
|
||||
date_buf,
|
||||
clock_buf);
|
||||
}
|
||||
g_free(sizing);
|
||||
}
|
||||
|
||||
static void dump_qdict(int indentation, QDict *dict);
|
||||
static void dump_qlist(int indentation, QList *list);
|
||||
static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QDict *dict);
|
||||
static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QList *list);
|
||||
|
||||
static void dump_qobject(int comp_indent, QObject *obj)
|
||||
static void dump_qobject(fprintf_function func_fprintf, void *f,
|
||||
int comp_indent, QObject *obj)
|
||||
{
|
||||
switch (qobject_type(obj)) {
|
||||
case QTYPE_QNUM: {
|
||||
QNum *value = qobject_to(QNum, obj);
|
||||
char *tmp = qnum_to_string(value);
|
||||
qemu_printf("%s", tmp);
|
||||
func_fprintf(f, "%s", tmp);
|
||||
g_free(tmp);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QSTRING: {
|
||||
QString *value = qobject_to(QString, obj);
|
||||
qemu_printf("%s", qstring_get_str(value));
|
||||
func_fprintf(f, "%s", qstring_get_str(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_QDICT: {
|
||||
QDict *value = qobject_to(QDict, obj);
|
||||
dump_qdict(comp_indent, value);
|
||||
dump_qdict(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QLIST: {
|
||||
QList *value = qobject_to(QList, obj);
|
||||
dump_qlist(comp_indent, value);
|
||||
dump_qlist(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QBOOL: {
|
||||
QBool *value = qobject_to(QBool, obj);
|
||||
qemu_printf("%s", qbool_get_bool(value) ? "true" : "false");
|
||||
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -702,7 +735,8 @@ static void dump_qobject(int comp_indent, QObject *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_qlist(int indentation, QList *list)
|
||||
static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QList *list)
|
||||
{
|
||||
const QListEntry *entry;
|
||||
int i = 0;
|
||||
@@ -710,16 +744,17 @@ static void dump_qlist(int indentation, QList *list)
|
||||
for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
|
||||
QType type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
qemu_printf("%*s[%i]:%c", indentation * 4, "", i,
|
||||
composite ? '\n' : ' ');
|
||||
dump_qobject(indentation + 1, entry->value);
|
||||
func_fprintf(f, "%*s[%i]:%c", indentation * 4, "", i,
|
||||
composite ? '\n' : ' ');
|
||||
dump_qobject(func_fprintf, f, indentation + 1, entry->value);
|
||||
if (!composite) {
|
||||
qemu_printf("\n");
|
||||
func_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_qdict(int indentation, QDict *dict)
|
||||
static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QDict *dict)
|
||||
{
|
||||
const QDictEntry *entry;
|
||||
|
||||
@@ -734,17 +769,18 @@ static void dump_qdict(int indentation, QDict *dict)
|
||||
key[i] = entry->key[i] == '-' ? ' ' : entry->key[i];
|
||||
}
|
||||
key[i] = 0;
|
||||
qemu_printf("%*s%s:%c", indentation * 4, "", key,
|
||||
composite ? '\n' : ' ');
|
||||
dump_qobject(indentation + 1, entry->value);
|
||||
func_fprintf(f, "%*s%s:%c", indentation * 4, "", key,
|
||||
composite ? '\n' : ' ');
|
||||
dump_qobject(func_fprintf, f, indentation + 1, entry->value);
|
||||
if (!composite) {
|
||||
qemu_printf("\n");
|
||||
func_fprintf(f, "\n");
|
||||
}
|
||||
g_free(key);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec)
|
||||
void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfoSpecific *info_spec)
|
||||
{
|
||||
QObject *obj, *data;
|
||||
Visitor *v = qobject_output_visitor_new(&obj);
|
||||
@@ -752,64 +788,65 @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec)
|
||||
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
|
||||
visit_complete(v, &obj);
|
||||
data = qdict_get(qobject_to(QDict, obj), "data");
|
||||
dump_qobject(1, data);
|
||||
dump_qobject(func_fprintf, f, 1, data);
|
||||
qobject_unref(obj);
|
||||
visit_free(v);
|
||||
}
|
||||
|
||||
void bdrv_image_info_dump(ImageInfo *info)
|
||||
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfo *info)
|
||||
{
|
||||
char *size_buf, *dsize_buf;
|
||||
char size_buf[128], dsize_buf[128];
|
||||
if (!info->has_actual_size) {
|
||||
dsize_buf = g_strdup("unavailable");
|
||||
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
|
||||
} else {
|
||||
dsize_buf = size_to_str(info->actual_size);
|
||||
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
|
||||
info->actual_size);
|
||||
}
|
||||
size_buf = size_to_str(info->virtual_size);
|
||||
qemu_printf("image: %s\n"
|
||||
"file format: %s\n"
|
||||
"virtual size: %s (%" PRId64 " bytes)\n"
|
||||
"disk size: %s\n",
|
||||
info->filename, info->format, size_buf,
|
||||
info->virtual_size,
|
||||
dsize_buf);
|
||||
g_free(size_buf);
|
||||
g_free(dsize_buf);
|
||||
get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size);
|
||||
func_fprintf(f,
|
||||
"image: %s\n"
|
||||
"file format: %s\n"
|
||||
"virtual size: %s (%" PRId64 " bytes)\n"
|
||||
"disk size: %s\n",
|
||||
info->filename, info->format, size_buf,
|
||||
info->virtual_size,
|
||||
dsize_buf);
|
||||
|
||||
if (info->has_encrypted && info->encrypted) {
|
||||
qemu_printf("encrypted: yes\n");
|
||||
func_fprintf(f, "encrypted: yes\n");
|
||||
}
|
||||
|
||||
if (info->has_cluster_size) {
|
||||
qemu_printf("cluster_size: %" PRId64 "\n",
|
||||
info->cluster_size);
|
||||
func_fprintf(f, "cluster_size: %" PRId64 "\n",
|
||||
info->cluster_size);
|
||||
}
|
||||
|
||||
if (info->has_dirty_flag && info->dirty_flag) {
|
||||
qemu_printf("cleanly shut down: no\n");
|
||||
func_fprintf(f, "cleanly shut down: no\n");
|
||||
}
|
||||
|
||||
if (info->has_backing_filename) {
|
||||
qemu_printf("backing file: %s", info->backing_filename);
|
||||
func_fprintf(f, "backing file: %s", info->backing_filename);
|
||||
if (!info->has_full_backing_filename) {
|
||||
qemu_printf(" (cannot determine actual path)");
|
||||
func_fprintf(f, " (cannot determine actual path)");
|
||||
} else if (strcmp(info->backing_filename,
|
||||
info->full_backing_filename) != 0) {
|
||||
qemu_printf(" (actual path: %s)", info->full_backing_filename);
|
||||
func_fprintf(f, " (actual path: %s)", info->full_backing_filename);
|
||||
}
|
||||
qemu_printf("\n");
|
||||
func_fprintf(f, "\n");
|
||||
if (info->has_backing_filename_format) {
|
||||
qemu_printf("backing file format: %s\n",
|
||||
info->backing_filename_format);
|
||||
func_fprintf(f, "backing file format: %s\n",
|
||||
info->backing_filename_format);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->has_snapshots) {
|
||||
SnapshotInfoList *elem;
|
||||
|
||||
qemu_printf("Snapshot list:\n");
|
||||
bdrv_snapshot_dump(NULL);
|
||||
qemu_printf("\n");
|
||||
func_fprintf(f, "Snapshot list:\n");
|
||||
bdrv_snapshot_dump(func_fprintf, f, NULL);
|
||||
func_fprintf(f, "\n");
|
||||
|
||||
/* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but
|
||||
* we convert to the block layer's native QEMUSnapshotInfo for now.
|
||||
@@ -825,13 +862,13 @@ void bdrv_image_info_dump(ImageInfo *info)
|
||||
|
||||
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
|
||||
pstrcpy(sn.name, sizeof(sn.name), elem->value->name);
|
||||
bdrv_snapshot_dump(&sn);
|
||||
qemu_printf("\n");
|
||||
bdrv_snapshot_dump(func_fprintf, f, &sn);
|
||||
func_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (info->has_format_specific) {
|
||||
qemu_printf("Format specific information:\n");
|
||||
bdrv_image_info_specific_dump(info->format_specific);
|
||||
func_fprintf(f, "Format specific information:\n");
|
||||
bdrv_image_info_specific_dump(func_fprintf, f, info->format_specific);
|
||||
}
|
||||
}
|
||||
|
||||
19
block/qcow.c
19
block/qcow.c
@@ -631,6 +631,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
int offset_in_cluster;
|
||||
int ret = 0, n;
|
||||
uint64_t cluster_offset;
|
||||
QEMUIOVector hd_qiov;
|
||||
uint8_t *buf;
|
||||
void *orig_buf;
|
||||
|
||||
@@ -662,10 +663,11 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
if (!cluster_offset) {
|
||||
if (bs->backing) {
|
||||
/* read from the base image */
|
||||
qemu_iovec_init_buf(&hd_qiov, buf, n);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
/* qcow2 emits this on bs->file instead of bs->backing */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
ret = bdrv_co_pread(bs->backing, offset, n, buf, 0);
|
||||
ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
@@ -686,10 +688,11 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
qemu_iovec_init_buf(&hd_qiov, buf, n);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
ret = bdrv_co_pread(bs->file, cluster_offset + offset_in_cluster,
|
||||
n, buf, 0);
|
||||
ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
|
||||
n, &hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
@@ -728,6 +731,7 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
int offset_in_cluster;
|
||||
uint64_t cluster_offset;
|
||||
int ret = 0, n;
|
||||
QEMUIOVector hd_qiov;
|
||||
uint8_t *buf;
|
||||
void *orig_buf;
|
||||
|
||||
@@ -772,10 +776,11 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
}
|
||||
|
||||
qemu_iovec_init_buf(&hd_qiov, buf, n);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
ret = bdrv_co_pwrite(bs->file, cluster_offset + offset_in_cluster,
|
||||
n, buf, 0);
|
||||
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
|
||||
n, &hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
@@ -1051,6 +1056,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QEMUIOVector hd_qiov;
|
||||
z_stream strm;
|
||||
int ret, out_len;
|
||||
uint8_t *buf, *out_buf;
|
||||
@@ -1116,8 +1122,9 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
|
||||
qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
|
||||
ret = bdrv_co_pwrite(bs->file, cluster_offset, out_len, out_buf, 0);
|
||||
ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table,
|
||||
continue;
|
||||
}
|
||||
|
||||
qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_ALWAYS);
|
||||
qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_OTHER);
|
||||
bitmap_table[i] = 0;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1520,31 +1508,12 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t start, last, cluster_offset, k, refcount;
|
||||
int64_t file_len;
|
||||
int ret;
|
||||
|
||||
if (size <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
file_len = bdrv_getlength(bs->file->bs);
|
||||
if (file_len < 0) {
|
||||
return file_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Last cluster of qcow2 image may be semi-allocated, so it may be OK to
|
||||
* reference some space after file end but it should be less than one
|
||||
* cluster.
|
||||
*/
|
||||
if (offset + size - file_len >= s->cluster_size) {
|
||||
fprintf(stderr, "ERROR: counting reference for region exceeding the "
|
||||
"end of the file by one cluster or more: offset 0x%" PRIx64
|
||||
" size 0x%" PRIx64 "\n", offset, size);
|
||||
res->corruptions++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
start = start_of_cluster(s, offset);
|
||||
last = start_of_cluster(s, offset + size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
@@ -1591,7 +1560,7 @@ enum {
|
||||
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
void **refcount_table,
|
||||
int64_t *refcount_table_size, int64_t l2_offset,
|
||||
int flags, BdrvCheckMode fix, bool active)
|
||||
int flags, BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t *l2_table, l2_entry;
|
||||
@@ -1613,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) {
|
||||
@@ -1624,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;
|
||||
@@ -1660,11 +1622,18 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
{
|
||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
|
||||
if (flags & CHECK_FRAG_INFO) {
|
||||
res->bfi.allocated_clusters++;
|
||||
if (next_contiguous_offset &&
|
||||
offset != next_contiguous_offset) {
|
||||
res->bfi.fragmented_clusters++;
|
||||
}
|
||||
next_contiguous_offset = offset + s->cluster_size;
|
||||
}
|
||||
|
||||
/* Correct offsets are cluster aligned */
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
res->corruptions++;
|
||||
|
||||
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 "
|
||||
@@ -1675,13 +1644,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
if (fix & BDRV_FIX_ERRORS) {
|
||||
uint64_t l2e_offset =
|
||||
l2_offset + (uint64_t)i * sizeof(uint64_t);
|
||||
int ign = active ? QCOW2_OL_ACTIVE_L2 :
|
||||
QCOW2_OL_INACTIVE_L2;
|
||||
|
||||
l2_entry = QCOW_OFLAG_ZERO;
|
||||
l2_table[i] = cpu_to_be64(l2_entry);
|
||||
ret = qcow2_pre_write_overlap_check(bs, ign,
|
||||
l2e_offset, sizeof(uint64_t), false);
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2,
|
||||
l2e_offset, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: Overlap check failed\n");
|
||||
res->check_errors++;
|
||||
@@ -1699,36 +1667,27 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
/* Do not abort, continue checking the rest of this
|
||||
* L2 table's entries */
|
||||
} else {
|
||||
res->corruptions--;
|
||||
res->corruptions_fixed++;
|
||||
/* Skip marking the cluster as used
|
||||
* (it is unused now) */
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
res->corruptions++;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "ERROR offset=%" PRIx64 ": Data cluster is "
|
||||
"not properly aligned; L2 entry corrupted.\n", offset);
|
||||
res->corruptions++;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & CHECK_FRAG_INFO) {
|
||||
res->bfi.allocated_clusters++;
|
||||
if (next_contiguous_offset &&
|
||||
offset != next_contiguous_offset) {
|
||||
res->bfi.fragmented_clusters++;
|
||||
}
|
||||
next_contiguous_offset = offset + s->cluster_size;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@@ -1763,7 +1722,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
void **refcount_table,
|
||||
int64_t *refcount_table_size,
|
||||
int64_t l1_table_offset, int l1_size,
|
||||
int flags, BdrvCheckMode fix, bool active)
|
||||
int flags, BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL, l2_offset, l1_size2;
|
||||
@@ -1819,7 +1778,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
/* Process and check L2 entries */
|
||||
ret = check_refcounts_l2(bs, res, refcount_table,
|
||||
refcount_table_size, l2_offset, flags,
|
||||
fix, active);
|
||||
fix);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1866,7 +1825,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
for (i = 0; i < s->l1_size; i++) {
|
||||
uint64_t l1_entry = s->l1_table[i];
|
||||
uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK;
|
||||
int l2_dirty = 0;
|
||||
bool l2_dirty = false;
|
||||
|
||||
if (!l2_offset) {
|
||||
continue;
|
||||
@@ -1879,7 +1838,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
continue;
|
||||
}
|
||||
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||
res->corruptions++;
|
||||
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
|
||||
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||
repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
|
||||
@@ -1892,8 +1850,9 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
res->check_errors++;
|
||||
goto fail;
|
||||
}
|
||||
res->corruptions--;
|
||||
res->corruptions_fixed++;
|
||||
} else {
|
||||
res->corruptions++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1909,23 +1868,18 @@ 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)) {
|
||||
res->corruptions++;
|
||||
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
|
||||
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||
repair ? "Repairing" : "ERROR", l2_entry, refcount);
|
||||
@@ -1933,16 +1887,18 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
l2_table[j] = cpu_to_be64(refcount == 1
|
||||
? l2_entry | QCOW_OFLAG_COPIED
|
||||
: l2_entry & ~QCOW_OFLAG_COPIED);
|
||||
l2_dirty++;
|
||||
l2_dirty = true;
|
||||
res->corruptions_fixed++;
|
||||
} else {
|
||||
res->corruptions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (l2_dirty > 0) {
|
||||
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));
|
||||
@@ -1958,8 +1914,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
res->check_errors++;
|
||||
goto fail;
|
||||
}
|
||||
res->corruptions -= l2_dirty;
|
||||
res->corruptions_fixed += l2_dirty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1997,7 +1951,6 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
}
|
||||
|
||||
if (cluster >= *nb_clusters) {
|
||||
res->corruptions++;
|
||||
fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n",
|
||||
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
|
||||
|
||||
@@ -2037,7 +1990,6 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
goto resize_fail;
|
||||
}
|
||||
|
||||
res->corruptions--;
|
||||
res->corruptions_fixed++;
|
||||
ret = qcow2_inc_refcounts_imrt(bs, res,
|
||||
refcount_table, nb_clusters,
|
||||
@@ -2051,9 +2003,12 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
continue;
|
||||
|
||||
resize_fail:
|
||||
res->corruptions++;
|
||||
*rebuild = true;
|
||||
fprintf(stderr, "ERROR could not resize image: %s\n",
|
||||
strerror(-ret));
|
||||
} else {
|
||||
res->corruptions++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -2109,18 +2064,12 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
/* current L1 table */
|
||||
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
|
||||
s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO,
|
||||
fix, true);
|
||||
fix);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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)) {
|
||||
@@ -2138,8 +2087,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
continue;
|
||||
}
|
||||
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
|
||||
sn->l1_table_offset, sn->l1_size, 0, fix,
|
||||
false);
|
||||
sn->l1_table_offset, sn->l1_size, 0, fix);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -2418,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;
|
||||
@@ -2429,8 +2377,8 @@ write_refblocks:
|
||||
on_disk_refblock = (void *)((char *) *refcount_table +
|
||||
refblock_index * s->cluster_size);
|
||||
|
||||
ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock,
|
||||
s->cluster_size);
|
||||
ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE,
|
||||
on_disk_refblock, s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
|
||||
goto fail;
|
||||
@@ -2469,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;
|
||||
@@ -2804,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) {
|
||||
@@ -2913,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;
|
||||
@@ -3180,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;
|
||||
|
||||
411
block/qcow2.c
411
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() */
|
||||
|
||||
@@ -1259,6 +1207,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
s->cluster_bits = header.cluster_bits;
|
||||
s->cluster_size = 1 << s->cluster_bits;
|
||||
s->cluster_sectors = 1 << (s->cluster_bits - BDRV_SECTOR_BITS);
|
||||
|
||||
/* Initialise version 3 header fields */
|
||||
if (header.version == 2) {
|
||||
@@ -1491,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
|
||||
@@ -1703,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);
|
||||
@@ -1909,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) {
|
||||
@@ -2045,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) {
|
||||
@@ -2061,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);
|
||||
@@ -2220,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;
|
||||
}
|
||||
@@ -2234,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);
|
||||
@@ -2323,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);
|
||||
}
|
||||
@@ -2500,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);
|
||||
@@ -2542,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,
|
||||
@@ -2635,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;
|
||||
}
|
||||
@@ -2720,13 +2595,10 @@ static int qcow2_set_up_encryption(BlockDriverState *bs,
|
||||
* Returns: 0 on success, -errno on failure.
|
||||
*/
|
||||
static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t new_length, PreallocMode mode,
|
||||
Error **errp)
|
||||
uint64_t new_length)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t bytes;
|
||||
uint64_t host_offset = 0;
|
||||
int64_t file_length;
|
||||
unsigned int cur_bytes;
|
||||
int ret;
|
||||
QCowL2Meta *meta;
|
||||
@@ -2735,11 +2607,10 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
|
||||
bytes = new_length - offset;
|
||||
|
||||
while (bytes) {
|
||||
cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size));
|
||||
cur_bytes = MIN(bytes, INT_MAX);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
|
||||
&host_offset, &meta);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Allocating clusters failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2748,7 +2619,6 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Mapping clusters failed");
|
||||
qcow2_free_any_clusters(bs, meta->alloc_offset,
|
||||
meta->nb_clusters, QCOW2_DISCARD_NEVER);
|
||||
return ret;
|
||||
@@ -2773,18 +2643,10 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
|
||||
* all of the allocated clusters (otherwise we get failing reads after
|
||||
* EOF). Extend the image to the last allocated sector.
|
||||
*/
|
||||
file_length = bdrv_getlength(s->data_file->bs);
|
||||
if (file_length < 0) {
|
||||
error_setg_errno(errp, -file_length, "Could not get file size");
|
||||
return file_length;
|
||||
}
|
||||
|
||||
if (host_offset + cur_bytes > file_length) {
|
||||
if (mode == PREALLOC_MODE_METADATA) {
|
||||
mode = PREALLOC_MODE_OFF;
|
||||
}
|
||||
ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, mode,
|
||||
errp);
|
||||
if (host_offset != 0) {
|
||||
uint8_t data = 0;
|
||||
ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1,
|
||||
&data, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -2967,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;
|
||||
@@ -3064,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);
|
||||
@@ -3105,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);
|
||||
@@ -3128,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);
|
||||
@@ -3166,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);
|
||||
@@ -3189,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) {
|
||||
@@ -3203,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;
|
||||
@@ -3235,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;
|
||||
|
||||
@@ -3247,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);
|
||||
@@ -3263,7 +3104,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
||||
out:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
bdrv_unref(data_bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3274,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;
|
||||
@@ -3317,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 },
|
||||
};
|
||||
|
||||
@@ -3339,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);
|
||||
@@ -3393,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;
|
||||
}
|
||||
@@ -3544,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;
|
||||
@@ -3614,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);
|
||||
@@ -3682,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;
|
||||
}
|
||||
@@ -3756,17 +3575,12 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
|
||||
switch (prealloc) {
|
||||
case PREALLOC_MODE_OFF:
|
||||
if (has_data_file(bs)) {
|
||||
ret = bdrv_co_truncate(s->data_file, offset, prealloc, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PREALLOC_MODE_METADATA:
|
||||
ret = preallocate_co(bs, old_length, offset, prealloc, errp);
|
||||
ret = preallocate_co(bs, old_length, offset);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Preallocation failed");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
@@ -3779,16 +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, prealloc, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
old_file_size = bdrv_getlength(bs->file->bs);
|
||||
if (old_file_size < 0) {
|
||||
error_setg_errno(errp, -old_file_size,
|
||||
@@ -3928,8 +3732,8 @@ fail:
|
||||
* @src - source buffer, @src_size bytes
|
||||
*
|
||||
* Returns: compressed size on success
|
||||
* -ENOMEM destination buffer is not enough to store compressed data
|
||||
* -EIO on any other error
|
||||
* -1 destination buffer is not enough to store compressed data
|
||||
* -2 on any other error
|
||||
*/
|
||||
static ssize_t qcow2_compress(void *dest, size_t dest_size,
|
||||
const void *src, size_t src_size)
|
||||
@@ -3942,7 +3746,7 @@ static ssize_t qcow2_compress(void *dest, size_t dest_size,
|
||||
ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
-12, 9, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK) {
|
||||
return -EIO;
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* strm.next_in is not const in old zlib versions, such as those used on
|
||||
@@ -3956,7 +3760,7 @@ static ssize_t qcow2_compress(void *dest, size_t dest_size,
|
||||
if (ret == Z_STREAM_END) {
|
||||
ret = dest_size - strm.avail_out;
|
||||
} else {
|
||||
ret = (ret == Z_OK ? -ENOMEM : -EIO);
|
||||
ret = (ret == Z_OK ? -1 : -2);
|
||||
}
|
||||
|
||||
deflateEnd(&strm);
|
||||
@@ -4093,23 +3897,21 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
QEMUIOVector hd_qiov;
|
||||
int ret;
|
||||
ssize_t out_len;
|
||||
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)) {
|
||||
@@ -4133,34 +3935,38 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
out_len = qcow2_co_compress(bs, out_buf, s->cluster_size - 1,
|
||||
buf, s->cluster_size);
|
||||
if (out_len == -ENOMEM) {
|
||||
if (out_len == -2) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
} else if (out_len == -1) {
|
||||
/* could not compress: write normal cluster */
|
||||
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
goto success;
|
||||
} else if (out_len < 0) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED);
|
||||
ret = bdrv_co_pwrite(s->data_file, cluster_offset, out_len, out_buf, 0);
|
||||
qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -4183,6 +3989,7 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
|
||||
int ret = 0, csize, nb_csectors;
|
||||
uint64_t coffset;
|
||||
uint8_t *buf, *out_buf;
|
||||
QEMUIOVector local_qiov;
|
||||
int offset_in_cluster = offset_into_cluster(s, offset);
|
||||
|
||||
coffset = file_cluster_offset & s->cluster_offset_mask;
|
||||
@@ -4193,11 +4000,12 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
qemu_iovec_init_buf(&local_qiov, buf, csize);
|
||||
|
||||
out_buf = qemu_blockalign(bs, s->cluster_size);
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
|
||||
ret = bdrv_co_pread(bs->file, coffset, csize, buf, 0);
|
||||
ret = bdrv_co_preadv(bs->file, coffset, csize, &local_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -4378,17 +4186,14 @@ static int qcow2_make_empty(BlockDriverState *bs)
|
||||
|
||||
if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps &&
|
||||
3 + l1_clusters <= s->refcount_block_size &&
|
||||
s->crypt_method_header != QCOW_CRYPT_LUKS &&
|
||||
!has_data_file(bs)) {
|
||||
s->crypt_method_header != QCOW_CRYPT_LUKS) {
|
||||
/* The following function only works for qcow2 v3 images (it
|
||||
* requires the dirty flag) and only as long as there are no
|
||||
* features that reserve extra clusters (such as snapshots,
|
||||
* LUKS header, or persistent bitmaps), because it completely
|
||||
* empties the image. Furthermore, the L1 table and three
|
||||
* additional clusters (image header, refcount table, one
|
||||
* refcount block) have to fit inside one refcount block. It
|
||||
* only resets the image file, i.e. does not work with an
|
||||
* external data file. */
|
||||
* refcount block) have to fit inside one refcount block. */
|
||||
return make_completely_empty(bs);
|
||||
}
|
||||
|
||||
@@ -4674,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
|
||||
@@ -4754,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);
|
||||
@@ -4880,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;
|
||||
@@ -4963,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 */
|
||||
@@ -5024,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,
|
||||
@@ -5189,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,
|
||||
@@ -5301,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 {
|
||||
@@ -266,6 +259,7 @@ typedef struct Qcow2BitmapHeaderExt {
|
||||
typedef struct BDRVQcow2State {
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
int l2_slice_size;
|
||||
int l2_bits;
|
||||
int l2_size;
|
||||
@@ -343,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 {
|
||||
@@ -466,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);
|
||||
@@ -521,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;
|
||||
@@ -532,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;
|
||||
}
|
||||
@@ -631,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,
|
||||
@@ -656,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);
|
||||
@@ -722,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,
|
||||
|
||||
@@ -106,7 +106,7 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
|
||||
/**
|
||||
* Descend tables and check each cluster is referenced once only
|
||||
*/
|
||||
static int coroutine_fn qed_check_l1_table(QEDCheck *check, QEDTable *table)
|
||||
static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
|
||||
{
|
||||
BDRVQEDState *s = check->s;
|
||||
unsigned int i, num_invalid_l1 = 0;
|
||||
@@ -218,7 +218,7 @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
|
||||
}
|
||||
|
||||
/* Called with table_lock held. */
|
||||
int coroutine_fn qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
{
|
||||
QEDCheck check = {
|
||||
.s = s,
|
||||
|
||||
@@ -19,25 +19,24 @@
|
||||
#include "qemu/bswap.h"
|
||||
|
||||
/* Called with table_lock held. */
|
||||
static int coroutine_fn qed_read_table(BDRVQEDState *s, uint64_t offset,
|
||||
QEDTable *table)
|
||||
static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
|
||||
{
|
||||
unsigned int bytes = s->header.cluster_size * s->header.table_size;
|
||||
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(
|
||||
qiov, table->offsets, s->header.cluster_size * s->header.table_size);
|
||||
int noffsets;
|
||||
int i, ret;
|
||||
|
||||
trace_qed_read_table(s, offset, table);
|
||||
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
ret = bdrv_co_pread(s->bs->file, offset, bytes, table->offsets, 0);
|
||||
ret = bdrv_preadv(s->bs->file, offset, &qiov);
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Byteswap offsets */
|
||||
noffsets = bytes / sizeof(uint64_t);
|
||||
noffsets = qiov.size / sizeof(uint64_t);
|
||||
for (i = 0; i < noffsets; i++) {
|
||||
table->offsets[i] = le64_to_cpu(table->offsets[i]);
|
||||
}
|
||||
@@ -61,13 +60,13 @@ out:
|
||||
*
|
||||
* Called with table_lock held.
|
||||
*/
|
||||
static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset,
|
||||
QEDTable *table, unsigned int index,
|
||||
unsigned int n, bool flush)
|
||||
static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
unsigned int index, unsigned int n, bool flush)
|
||||
{
|
||||
unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
|
||||
unsigned int start, end, i;
|
||||
QEDTable *new_table;
|
||||
QEMUIOVector qiov;
|
||||
size_t len_bytes;
|
||||
int ret;
|
||||
|
||||
@@ -80,6 +79,7 @@ static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset,
|
||||
len_bytes = (end - start) * sizeof(uint64_t);
|
||||
|
||||
new_table = qemu_blockalign(s->bs, len_bytes);
|
||||
qemu_iovec_init_buf(&qiov, new_table->offsets, len_bytes);
|
||||
|
||||
/* Byteswap table */
|
||||
for (i = start; i < end; i++) {
|
||||
@@ -91,7 +91,7 @@ static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset,
|
||||
offset += start * sizeof(uint64_t);
|
||||
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
ret = bdrv_co_pwrite(s->bs->file, offset, len_bytes, new_table->offsets, 0);
|
||||
ret = bdrv_pwritev(s->bs->file, offset, &qiov);
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
trace_qed_write_table_cb(s, table, flush, ret);
|
||||
if (ret < 0) {
|
||||
@@ -111,29 +111,27 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn qed_read_l1_table_sync(BDRVQEDState *s)
|
||||
int qed_read_l1_table_sync(BDRVQEDState *s)
|
||||
{
|
||||
return qed_read_table(s, s->header.l1_table_offset, s->l1_table);
|
||||
}
|
||||
|
||||
/* Called with table_lock held. */
|
||||
int coroutine_fn qed_write_l1_table(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n)
|
||||
int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
|
||||
{
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
|
||||
return qed_write_table(s, s->header.l1_table_offset,
|
||||
s->l1_table, index, n, false);
|
||||
}
|
||||
|
||||
int coroutine_fn qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n)
|
||||
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n)
|
||||
{
|
||||
return qed_write_l1_table(s, index, n);
|
||||
}
|
||||
|
||||
/* Called with table_lock held. */
|
||||
int coroutine_fn qed_read_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
uint64_t offset)
|
||||
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -170,25 +168,22 @@ int coroutine_fn qed_read_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
uint64_t offset)
|
||||
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
|
||||
{
|
||||
return qed_read_l2_table(s, request, offset);
|
||||
}
|
||||
|
||||
/* Called with table_lock held. */
|
||||
int coroutine_fn qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n,
|
||||
bool flush)
|
||||
int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush)
|
||||
{
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
|
||||
return qed_write_table(s, request->l2_table->offset,
|
||||
request->l2_table->table, index, n, flush);
|
||||
}
|
||||
|
||||
int coroutine_fn qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n,
|
||||
bool flush)
|
||||
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush)
|
||||
{
|
||||
return qed_write_l2_table(s, request, index, n, flush);
|
||||
}
|
||||
|
||||
11
block/qed.c
11
block/qed.c
@@ -113,13 +113,15 @@ static int coroutine_fn qed_write_header(BDRVQEDState *s)
|
||||
int nsectors = DIV_ROUND_UP(sizeof(QEDHeader), BDRV_SECTOR_SIZE);
|
||||
size_t len = nsectors * BDRV_SECTOR_SIZE;
|
||||
uint8_t *buf;
|
||||
QEMUIOVector qiov;
|
||||
int ret;
|
||||
|
||||
assert(s->allocating_acb || s->allocating_write_reqs_plugged);
|
||||
|
||||
buf = qemu_blockalign(s->bs, len);
|
||||
qemu_iovec_init_buf(&qiov, buf, len);
|
||||
|
||||
ret = bdrv_co_pread(s->bs->file, 0, len, buf, 0);
|
||||
ret = bdrv_co_preadv(s->bs->file, 0, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -127,7 +129,7 @@ static int coroutine_fn qed_write_header(BDRVQEDState *s)
|
||||
/* Update header */
|
||||
qed_header_cpu_to_le(&s->header, (QEDHeader *) buf);
|
||||
|
||||
ret = bdrv_co_pwrite(s->bs->file, 0, len, buf, 0);
|
||||
ret = bdrv_co_pwritev(s->bs->file, 0, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -1604,9 +1606,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
static int bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
28
block/qed.h
28
block/qed.h
@@ -201,21 +201,17 @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table);
|
||||
/**
|
||||
* Table I/O functions
|
||||
*/
|
||||
int coroutine_fn qed_read_l1_table_sync(BDRVQEDState *s);
|
||||
int coroutine_fn qed_write_l1_table(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n);
|
||||
int coroutine_fn qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n);
|
||||
int coroutine_fn qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
uint64_t offset);
|
||||
int coroutine_fn qed_read_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
uint64_t offset);
|
||||
int coroutine_fn qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n,
|
||||
bool flush);
|
||||
int coroutine_fn qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n,
|
||||
bool flush);
|
||||
int qed_read_l1_table_sync(BDRVQEDState *s);
|
||||
int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n);
|
||||
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n);
|
||||
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
uint64_t offset);
|
||||
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset);
|
||||
int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush);
|
||||
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush);
|
||||
|
||||
/**
|
||||
* Cluster functions
|
||||
@@ -227,7 +223,7 @@ int coroutine_fn qed_find_cluster(BDRVQEDState *s, QEDRequest *request,
|
||||
/**
|
||||
* Consistency check
|
||||
*/
|
||||
int coroutine_fn qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix);
|
||||
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix);
|
||||
|
||||
QEDTable *qed_alloc_table(BDRVQEDState *s);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
111
block/ssh.c
111
block/ssh.c
@@ -75,14 +75,6 @@ typedef struct BDRVSSHState {
|
||||
|
||||
/* Used to warn if 'flush' is not supported. */
|
||||
bool unsafe_flush_warning;
|
||||
|
||||
/*
|
||||
* Store the user name for ssh_refresh_filename() because the
|
||||
* default depends on the system you are on -- therefore, when we
|
||||
* generate a filename, it should always contain the user name we
|
||||
* are actually using.
|
||||
*/
|
||||
char *user;
|
||||
} BDRVSSHState;
|
||||
|
||||
static void ssh_state_init(BDRVSSHState *s)
|
||||
@@ -95,8 +87,6 @@ static void ssh_state_init(BDRVSSHState *s)
|
||||
|
||||
static void ssh_state_free(BDRVSSHState *s)
|
||||
{
|
||||
g_free(s->user);
|
||||
|
||||
if (s->sftp_handle) {
|
||||
libssh2_sftp_close(s->sftp_handle);
|
||||
}
|
||||
@@ -169,19 +159,31 @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
|
||||
g_free(msg);
|
||||
}
|
||||
|
||||
static void sftp_error_trace(BDRVSSHState *s, const char *op)
|
||||
static void GCC_FMT_ATTR(2, 3)
|
||||
sftp_error_report(BDRVSSHState *s, const char *fs, ...)
|
||||
{
|
||||
char *ssh_err;
|
||||
int ssh_err_code;
|
||||
unsigned long sftp_err_code;
|
||||
va_list args;
|
||||
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
/* See <libssh2_sftp.h>. */
|
||||
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
|
||||
va_start(args, fs);
|
||||
error_vprintf(fs, args);
|
||||
|
||||
trace_sftp_error(op, ssh_err, ssh_err_code, sftp_err_code);
|
||||
if ((s)->sftp) {
|
||||
char *ssh_err;
|
||||
int ssh_err_code;
|
||||
unsigned long sftp_err_code;
|
||||
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
/* See <libssh2_sftp.h>. */
|
||||
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
|
||||
|
||||
error_printf(": %s (libssh2 error code: %d, sftp error code: %lu)",
|
||||
ssh_err, ssh_err_code, sftp_err_code);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
error_printf("\n");
|
||||
}
|
||||
|
||||
static int parse_uri(const char *filename, QDict *options, Error **errp)
|
||||
@@ -638,13 +640,14 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
|
||||
int ssh_flags, int creat_mode, Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
const char *user;
|
||||
long port = 0;
|
||||
|
||||
if (opts->has_user) {
|
||||
s->user = g_strdup(opts->user);
|
||||
user = opts->user;
|
||||
} else {
|
||||
s->user = g_strdup(g_get_user_name());
|
||||
if (!s->user) {
|
||||
user = g_get_user_name();
|
||||
if (!user) {
|
||||
error_setg_errno(errp, errno, "Can't get user name");
|
||||
ret = -errno;
|
||||
goto err;
|
||||
@@ -694,7 +697,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
|
||||
}
|
||||
|
||||
/* Authenticate. */
|
||||
ret = authenticate(s, s->user, errp);
|
||||
ret = authenticate(s, user, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -1032,7 +1035,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
|
||||
goto again;
|
||||
}
|
||||
if (r < 0) {
|
||||
sftp_error_trace(s, "read");
|
||||
sftp_error_report(s, "read failed");
|
||||
s->offset = -1;
|
||||
return -EIO;
|
||||
}
|
||||
@@ -1102,7 +1105,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
||||
goto again;
|
||||
}
|
||||
if (r < 0) {
|
||||
sftp_error_trace(s, "write");
|
||||
sftp_error_report(s, "write failed");
|
||||
s->offset = -1;
|
||||
return -EIO;
|
||||
}
|
||||
@@ -1185,7 +1188,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
if (r < 0) {
|
||||
sftp_error_trace(s, "fsync");
|
||||
sftp_error_report(s, "fsync failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -1251,58 +1254,6 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
return ssh_grow_file(s, offset, errp);
|
||||
}
|
||||
|
||||
static void ssh_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
const char *path, *host_key_check;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* None of these options can be represented in a plain "host:port"
|
||||
* format, so if any was given, we have to abort.
|
||||
*/
|
||||
if (s->inet->has_ipv4 || s->inet->has_ipv6 || s->inet->has_to ||
|
||||
s->inet->has_numeric)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
path = qdict_get_try_str(bs->full_open_options, "path");
|
||||
assert(path); /* mandatory option */
|
||||
|
||||
host_key_check = qdict_get_try_str(bs->full_open_options, "host_key_check");
|
||||
|
||||
ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"ssh://%s@%s:%s%s%s%s",
|
||||
s->user, s->inet->host, s->inet->port, path,
|
||||
host_key_check ? "?host_key_check=" : "",
|
||||
host_key_check ?: "");
|
||||
if (ret >= sizeof(bs->exact_filename)) {
|
||||
/* An overflow makes the filename unusable, so do not report any */
|
||||
bs->exact_filename[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static char *ssh_bdrv_dirname(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
if (qdict_haskey(bs->full_open_options, "host_key_check")) {
|
||||
/*
|
||||
* We cannot generate a simple prefix if we would have to
|
||||
* append a query string.
|
||||
*/
|
||||
error_setg(errp,
|
||||
"Cannot generate a base directory with host_key_check set");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bs->exact_filename[0] == '\0') {
|
||||
error_setg(errp, "Cannot generate a base directory for this ssh node");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path_combine(bs->exact_filename, "");
|
||||
}
|
||||
|
||||
static const char *const ssh_strong_runtime_opts[] = {
|
||||
"host",
|
||||
"port",
|
||||
@@ -1329,8 +1280,6 @@ static BlockDriver bdrv_ssh = {
|
||||
.bdrv_getlength = ssh_getlength,
|
||||
.bdrv_co_truncate = ssh_co_truncate,
|
||||
.bdrv_co_flush_to_disk = ssh_co_flush,
|
||||
.bdrv_refresh_filename = ssh_refresh_filename,
|
||||
.bdrv_dirname = ssh_bdrv_dirname,
|
||||
.create_opts = &ssh_create_opts,
|
||||
.strong_runtime_opts = ssh_strong_runtime_opts,
|
||||
};
|
||||
|
||||
@@ -35,27 +35,18 @@ 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,
|
||||
int64_t offset, uint64_t bytes,
|
||||
void *buf)
|
||||
{
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||
|
||||
assert(bytes < SIZE_MAX);
|
||||
|
||||
/* Copy-on-read the unallocated clusters */
|
||||
return blk_co_pread(blk, offset, bytes, buf, 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);
|
||||
}
|
||||
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
|
||||
}
|
||||
|
||||
static int stream_prepare(Job *job)
|
||||
@@ -67,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) {
|
||||
@@ -220,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,
|
||||
@@ -236,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,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);
|
||||
@@ -286,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"
|
||||
@@ -208,6 +207,3 @@ sheepdog_co_rw_vector_new(uint64_t oid) "new oid 0x%" PRIx64
|
||||
sheepdog_snapshot_create_info(const char *sn_name, const char *id, const char *name, int64_t size, int is_snapshot) "sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " "is_snapshot %d"
|
||||
sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s"
|
||||
sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32
|
||||
|
||||
# ssh.c
|
||||
sftp_error(const char *op, const char *ssh_err, int ssh_err_code, unsigned long sftp_err_code) "%s failed: %s (libssh2 error code: %d, sftp error code: %lu)"
|
||||
|
||||
15
block/vdi.c
15
block/vdi.c
@@ -171,8 +171,6 @@ typedef struct {
|
||||
uint64_t unused2[7];
|
||||
} QEMU_PACKED VdiHeader;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(VdiHeader) != 512);
|
||||
|
||||
typedef struct {
|
||||
/* The block map entries are little endian (even in memory). */
|
||||
uint32_t *bmap;
|
||||
@@ -386,7 +384,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
logout("\n");
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -486,8 +484,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file, header.offset_bmap, s->bmap,
|
||||
bmap_size * SECTOR_SIZE);
|
||||
ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap,
|
||||
bmap_size);
|
||||
if (ret < 0) {
|
||||
goto fail_free_bmap;
|
||||
}
|
||||
@@ -706,7 +704,7 @@ nonallocating_write:
|
||||
assert(VDI_IS_ALLOCATED(bmap_first));
|
||||
*header = s->header;
|
||||
vdi_header_to_le(header);
|
||||
ret = bdrv_pwrite(bs->file, 0, block, sizeof(VdiHeader));
|
||||
ret = bdrv_write(bs->file, 0, block, 1);
|
||||
g_free(block);
|
||||
block = NULL;
|
||||
|
||||
@@ -724,11 +722,10 @@ nonallocating_write:
|
||||
base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE;
|
||||
logout("will write %u block map sectors starting from entry %u\n",
|
||||
n_sectors, bmap_first);
|
||||
ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE, base,
|
||||
n_sectors * SECTOR_SIZE);
|
||||
ret = bdrv_write(bs->file, offset, base, n_sectors);
|
||||
}
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
|
||||
@@ -551,7 +551,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
if (file_length < desc_entries->hdr.last_file_offset) {
|
||||
new_file_size = desc_entries->hdr.last_file_offset;
|
||||
if (new_file_size % (1 * MiB)) {
|
||||
if (new_file_size % (1024*1024)) {
|
||||
/* round up to nearest 1MB boundary */
|
||||
new_file_size = QEMU_ALIGN_UP(new_file_size, MiB);
|
||||
if (new_file_size > INT64_MAX) {
|
||||
|
||||
@@ -1175,7 +1175,7 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
*new_offset = current_len;
|
||||
|
||||
/* per the spec, the address for a block is in units of 1MB */
|
||||
*new_offset = ROUND_UP(*new_offset, 1 * MiB);
|
||||
*new_offset = ROUND_UP(*new_offset, 1024 * 1024);
|
||||
if (*new_offset > INT64_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1338,7 +1338,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
case PAYLOAD_BLOCK_FULLY_PRESENT:
|
||||
/* if the file offset address is in the header zone,
|
||||
* there is a problem */
|
||||
if (sinfo.file_offset < (1 * MiB)) {
|
||||
if (sinfo.file_offset < (1024 * 1024)) {
|
||||
ret = -EFAULT;
|
||||
goto error_bat_restore;
|
||||
}
|
||||
@@ -1889,8 +1889,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
return -EINVAL;
|
||||
}
|
||||
if (block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||
error_setg(errp, "Block size must not exceed %" PRId64,
|
||||
VHDX_BLOCK_SIZE_MAX);
|
||||
error_setg(errp, "Block size must not exceed %d", VHDX_BLOCK_SIZE_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
16
block/vhdx.h
16
block/vhdx.h
@@ -17,11 +17,13 @@
|
||||
|
||||
#ifndef BLOCK_VHDX_H
|
||||
#define BLOCK_VHDX_H
|
||||
#include "qemu/units.h"
|
||||
|
||||
#define KiB (1 * 1024)
|
||||
#define MiB (KiB * 1024)
|
||||
#define GiB (MiB * 1024)
|
||||
#define TiB ((uint64_t) GiB * 1024)
|
||||
|
||||
#define DEFAULT_LOG_SIZE 1048576 /* 1MiB */
|
||||
/* Note: can't use 1 * MiB, because it's passed to stringify() */
|
||||
|
||||
/* Structures and fields present in the VHDX file */
|
||||
|
||||
/* The header section has the following blocks,
|
||||
@@ -34,7 +36,7 @@
|
||||
* 0.........64KB...........128KB........192KB..........256KB................1MB
|
||||
*/
|
||||
|
||||
#define VHDX_HEADER_BLOCK_SIZE (64 * KiB)
|
||||
#define VHDX_HEADER_BLOCK_SIZE (64 * 1024)
|
||||
|
||||
#define VHDX_FILE_ID_OFFSET 0
|
||||
#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE * 1)
|
||||
@@ -83,7 +85,7 @@ typedef struct QEMU_PACKED MSGUID {
|
||||
#define guid_eq(a, b) \
|
||||
(memcmp(&(a), &(b), sizeof(MSGUID)) == 0)
|
||||
|
||||
#define VHDX_HEADER_SIZE (4 * KiB) /* although the vhdx_header struct in disk
|
||||
#define VHDX_HEADER_SIZE (4 * 1024) /* although the vhdx_header struct in disk
|
||||
is only 582 bytes, for purposes of crc
|
||||
the header is the first 4KB of the 64KB
|
||||
block */
|
||||
@@ -159,8 +161,8 @@ typedef struct QEMU_PACKED VHDXRegionTableEntry {
|
||||
|
||||
|
||||
/* ---- LOG ENTRY STRUCTURES ---- */
|
||||
#define VHDX_LOG_MIN_SIZE (1 * MiB)
|
||||
#define VHDX_LOG_SECTOR_SIZE (4 * KiB)
|
||||
#define VHDX_LOG_MIN_SIZE (1024 * 1024)
|
||||
#define VHDX_LOG_SECTOR_SIZE 4096
|
||||
#define VHDX_LOG_HDR_SIZE 64
|
||||
#define VHDX_LOG_SIGNATURE 0x65676f6c
|
||||
typedef struct QEMU_PACKED VHDXLogEntryHeader {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -397,8 +395,6 @@ static int vmdk_parent_open(BlockDriverState *bs)
|
||||
pstrcpy(bs->auto_backing_file, end_name - p_name + 1, p_name);
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
bs->auto_backing_file);
|
||||
pstrcpy(bs->backing_format, sizeof(bs->backing_format),
|
||||
"vmdk");
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@@ -639,10 +639,8 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
qemu_iovec_reset(&local_qiov);
|
||||
qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes);
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_preadv(bs->file, image_offset, n_bytes,
|
||||
&local_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -699,10 +697,8 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
qemu_iovec_reset(&local_qiov);
|
||||
qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes);
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_pwritev(bs->file, image_offset, n_bytes,
|
||||
&local_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1494,8 +1494,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
|
||||
DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64
|
||||
" allocated\n", sector_num,
|
||||
n >> BDRV_SECTOR_BITS));
|
||||
if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE,
|
||||
buf + i * 0x200, n) < 0) {
|
||||
if (bdrv_read(s->qcow, sector_num, buf + i * 0x200,
|
||||
n >> BDRV_SECTOR_BITS)) {
|
||||
return -1;
|
||||
}
|
||||
i += (n >> BDRV_SECTOR_BITS) - 1;
|
||||
@@ -1983,9 +1983,8 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
|
||||
if (res) {
|
||||
return -1;
|
||||
}
|
||||
res = bdrv_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE,
|
||||
s->cluster_buffer, BDRV_SECTOR_SIZE);
|
||||
if (res < 0) {
|
||||
res = bdrv_write(s->qcow, offset, s->cluster_buffer, 1);
|
||||
if (res) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
@@ -3051,8 +3050,7 @@ DLOG(checkpoint());
|
||||
* Use qcow backend. Commit later.
|
||||
*/
|
||||
DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
|
||||
ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, buf,
|
||||
nb_sectors * BDRV_SECTOR_SIZE);
|
||||
ret = bdrv_write(s->qcow, sector_num, buf, nb_sectors);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing to qcow backend\n");
|
||||
return ret;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user