Compare commits
5 Commits
qemu-openb
...
rm-protoco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6321d78dc5 | ||
|
|
a0974f99aa | ||
|
|
42883c01bf | ||
|
|
7966c2b312 | ||
|
|
5888011244 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -36,7 +36,6 @@
|
||||
/qapi/qapi-commands-common.[ch]
|
||||
/qapi/qapi-commands-crypto.[ch]
|
||||
/qapi/qapi-commands-introspect.[ch]
|
||||
/qapi/qapi-commands-job.[ch]
|
||||
/qapi/qapi-commands-migration.[ch]
|
||||
/qapi/qapi-commands-misc.[ch]
|
||||
/qapi/qapi-commands-net.[ch]
|
||||
@@ -54,7 +53,6 @@
|
||||
/qapi/qapi-events-common.[ch]
|
||||
/qapi/qapi-events-crypto.[ch]
|
||||
/qapi/qapi-events-introspect.[ch]
|
||||
/qapi/qapi-events-job.[ch]
|
||||
/qapi/qapi-events-migration.[ch]
|
||||
/qapi/qapi-events-misc.[ch]
|
||||
/qapi/qapi-events-net.[ch]
|
||||
@@ -73,7 +71,6 @@
|
||||
/qapi/qapi-types-common.[ch]
|
||||
/qapi/qapi-types-crypto.[ch]
|
||||
/qapi/qapi-types-introspect.[ch]
|
||||
/qapi/qapi-types-job.[ch]
|
||||
/qapi/qapi-types-migration.[ch]
|
||||
/qapi/qapi-types-misc.[ch]
|
||||
/qapi/qapi-types-net.[ch]
|
||||
@@ -91,7 +88,6 @@
|
||||
/qapi/qapi-visit-common.[ch]
|
||||
/qapi/qapi-visit-crypto.[ch]
|
||||
/qapi/qapi-visit-introspect.[ch]
|
||||
/qapi/qapi-visit-job.[ch]
|
||||
/qapi/qapi-visit-migration.[ch]
|
||||
/qapi/qapi-visit-misc.[ch]
|
||||
/qapi/qapi-visit-net.[ch]
|
||||
@@ -210,4 +206,3 @@ trace-dtrace-root.h
|
||||
trace-dtrace-root.dtrace
|
||||
trace-ust-all.h
|
||||
trace-ust-all.c
|
||||
/target/arm/decode-sve.inc.c
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -18,7 +18,7 @@
|
||||
url = git://git.qemu-project.org/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = git://git.qemu.org/qemu-palcode.git
|
||||
url = git://github.com/rth7680/qemu-palcode.git
|
||||
[submodule "roms/sgabios"]
|
||||
path = roms/sgabios
|
||||
url = git://git.qemu-project.org/sgabios.git
|
||||
@@ -45,4 +45,4 @@
|
||||
url = git://github.com/hdeller/seabios-hppa.git
|
||||
[submodule "roms/u-boot-sam460ex"]
|
||||
path = roms/u-boot-sam460ex
|
||||
url = git://git.qemu.org/u-boot-sam460ex.git
|
||||
url = git://github.com/zbalaton/u-boot-sam460ex
|
||||
|
||||
18
.mailmap
18
.mailmap
@@ -1,7 +1,6 @@
|
||||
# This mailmap fixes up author names/addresses.
|
||||
|
||||
# The first section translates weird addresses from the original git import
|
||||
# into proper addresses so that they are counted properly by git shortlog.
|
||||
# This mailmap just translates the weird addresses from the original import into git
|
||||
# into proper addresses so that they are counted properly in git shortlog output.
|
||||
#
|
||||
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||
@@ -16,19 +15,10 @@ Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
|
||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
|
||||
# There is also a:
|
||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
||||
|
||||
# Next, translate a few commits where mailman rewrote the From: line due
|
||||
# to strict SPF, although we prefer to avoid adding more entries like that.
|
||||
Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
|
||||
|
||||
|
||||
#
|
||||
# Also list preferred name forms where people have changed their
|
||||
# git author config
|
||||
Daniel P. Berrangé <berrange@redhat.com>
|
||||
|
||||
@@ -35,5 +35,13 @@ build:
|
||||
options: "-e HOME=/root"
|
||||
ci:
|
||||
- unset CC
|
||||
# some targets require newer up to date packages, for example TARGET_LIST matching
|
||||
# aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
|
||||
# see the configure script:
|
||||
# error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
|
||||
# " (1) Preferred: Install the DTC (libfdt) devel package"
|
||||
# " (2) Fetch the DTC submodule, using:"
|
||||
# " git submodule update --init dtc"
|
||||
- dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
|
||||
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
|
||||
102
.travis.yml
102
.travis.yml
@@ -1,8 +1,4 @@
|
||||
# The current Travis default is a container based 14.04 Trust on EC2
|
||||
# Additional builds with specific requirements for a full VM need to
|
||||
# be added as additional matrix: entries later on
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c
|
||||
python:
|
||||
- "2.6"
|
||||
@@ -53,10 +49,9 @@ env:
|
||||
- TEST_CMD="make check"
|
||||
- MAKEFLAGS="-j3"
|
||||
matrix:
|
||||
- CONFIG="--disable-system"
|
||||
- CONFIG="--disable-user"
|
||||
- CONFIG="--enable-debug --enable-debug-tcg"
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
|
||||
- CONFIG=""
|
||||
- CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
|
||||
- CONFIG="--enable-modules --disable-linux-user"
|
||||
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
|
||||
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
|
||||
@@ -69,31 +64,28 @@ before_install:
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || { cat config.log && exit 1; }
|
||||
- ./configure ${CONFIG}
|
||||
script:
|
||||
- make ${MAKEFLAGS} && ${TEST_CMD}
|
||||
matrix:
|
||||
include:
|
||||
# Test with Clang for compile portability (Travis uses clang-5.0)
|
||||
- env: CONFIG="--disable-system"
|
||||
compiler: clang
|
||||
- env: CONFIG="--disable-user"
|
||||
# Test with CLang for compile portability
|
||||
- env: CONFIG=""
|
||||
compiler: clang
|
||||
# gprof/gcov are GCC features
|
||||
- env: 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"
|
||||
- env: CONFIG="--enable-gprof --enable-gcov --disable-pie"
|
||||
compiler: gcc
|
||||
# We manually include builds which we disable "make check" for
|
||||
- env: CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
# We don't need to exercise every backend with every front-end
|
||||
- env: CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
|
||||
- env: CONFIG="--enable-trace-backends=simple"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
|
||||
- env: CONFIG="--enable-trace-backends=ftrace"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
|
||||
- env: CONFIG="--enable-trace-backends=ust"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--disable-tcg"
|
||||
@@ -102,24 +94,80 @@ matrix:
|
||||
- env: CONFIG=""
|
||||
os: osx
|
||||
compiler: clang
|
||||
# Python builds
|
||||
- env: CONFIG="--target-list=x86_64-softmmu"
|
||||
# Plain Trusty System Build
|
||||
- env: CONFIG="--disable-linux-user"
|
||||
sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
# Plain Trusty Linux User Build
|
||||
- env: CONFIG="--disable-system"
|
||||
sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
# Trusty System build with latest stable clang & python 3.0
|
||||
- sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
language: generic
|
||||
compiler: none
|
||||
python:
|
||||
- "3.0"
|
||||
- env: CONFIG="--target-list=x86_64-softmmu"
|
||||
env:
|
||||
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||
- CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
|
||||
before_install:
|
||||
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y clang-3.9
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || cat config.log
|
||||
# Trusty Linux User build with latest stable clang & python 3.6
|
||||
- sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
language: generic
|
||||
compiler: none
|
||||
python:
|
||||
- "3.6"
|
||||
env:
|
||||
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||
- CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
|
||||
before_install:
|
||||
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y clang-3.9
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || cat config.log
|
||||
# Using newer GCC with sanitizers
|
||||
- addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
# PPAs for newer toolchains
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
# Extra toolchains
|
||||
- gcc-7
|
||||
- g++-7
|
||||
- gcc-5
|
||||
- g++-5
|
||||
# Build dependencies
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
@@ -148,8 +196,8 @@ matrix:
|
||||
language: generic
|
||||
compiler: none
|
||||
env:
|
||||
- COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7
|
||||
- CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user"
|
||||
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
|
||||
- CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
|
||||
- TEST_CMD=""
|
||||
before_script:
|
||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
|
||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
|
||||
|
||||
17
CODING_STYLE
17
CODING_STYLE
@@ -124,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments.
|
||||
Rationale: The // form is valid in C99, so this is purely a matter of
|
||||
consistency of style. The checkpatch script will warn you about this.
|
||||
|
||||
Multiline comment blocks should have a row of stars on the left,
|
||||
and the initial /* and terminating */ both on their own lines:
|
||||
/*
|
||||
* like
|
||||
* this
|
||||
*/
|
||||
This is the same format required by the Linux kernel coding style.
|
||||
|
||||
(Some of the existing comments in the codebase use the GNU Coding
|
||||
Standards form which does not have stars on the left, or other
|
||||
variations; avoid these when writing new comments, but don't worry
|
||||
about converting to the preferred form unless you're editing that
|
||||
comment anyway.)
|
||||
|
||||
Rationale: Consistency, and ease of visually picking out a multiline
|
||||
comment from the surrounding code.
|
||||
|
||||
8. trace-events style
|
||||
|
||||
8.1 0x prefix
|
||||
|
||||
9
HACKING
9
HACKING
@@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there
|
||||
is no need to test for failure (as you would have to with malloc).
|
||||
Calling g_malloc with a zero size is valid and will return NULL.
|
||||
|
||||
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
|
||||
reasons:
|
||||
|
||||
a. It catches multiplication overflowing size_t;
|
||||
b. It returns T * instead of void *, letting compiler catch more type
|
||||
errors.
|
||||
|
||||
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
|
||||
|
||||
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
|
||||
qemu_vfree, since breaking this will cause problems on Win32.
|
||||
|
||||
|
||||
82
MAINTAINERS
82
MAINTAINERS
@@ -127,6 +127,7 @@ Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: target/alpha/
|
||||
F: hw/alpha/
|
||||
F: tests/tcg/alpha/
|
||||
F: disas/alpha.c
|
||||
|
||||
@@ -289,7 +290,7 @@ T: git git://github.com/ehabkost/qemu.git x86-next
|
||||
|
||||
Xtensa
|
||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||
W: http://wiki.osll.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
|
||||
W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
|
||||
S: Maintained
|
||||
F: target/xtensa/
|
||||
F: hw/xtensa/
|
||||
@@ -412,12 +413,6 @@ F: include/*/*win32*
|
||||
X: qga/*win32*
|
||||
F: qemu.nsi
|
||||
|
||||
Alpha Machines
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: hw/alpha/
|
||||
F: hw/isa/smc37c669-superio.c
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
Allwinner-a10
|
||||
@@ -447,8 +442,6 @@ F: hw/timer/cmsdk-apb-timer.c
|
||||
F: include/hw/timer/cmsdk-apb-timer.h
|
||||
F: hw/char/cmsdk-apb-uart.c
|
||||
F: include/hw/char/cmsdk-apb-uart.h
|
||||
F: hw/misc/tz-ppc.c
|
||||
F: include/hw/misc/tz-ppc.h
|
||||
|
||||
ARM cores
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -517,11 +510,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/mps2.c
|
||||
F: hw/arm/mps2-tz.c
|
||||
F: hw/misc/mps2-*.c
|
||||
F: include/hw/misc/mps2-*.h
|
||||
F: hw/arm/iotkit.c
|
||||
F: include/hw/arm/iotkit.h
|
||||
F: hw/misc/mps2-scc.c
|
||||
F: include/hw/misc/mps2-scc.h
|
||||
|
||||
Musicpal
|
||||
M: Jan Kiszka <jan.kiszka@web.de>
|
||||
@@ -710,8 +700,6 @@ Fulong 2E
|
||||
M: Yongbok Kim <yongbok.kim@mips.com>
|
||||
S: Odd Fixes
|
||||
F: hw/mips/mips_fulong2e.c
|
||||
F: hw/isa/vt82c686.c
|
||||
F: include/hw/isa/vt82c686.h
|
||||
|
||||
Boston
|
||||
M: Paul Burton <paul.burton@mips.com>
|
||||
@@ -766,11 +754,8 @@ F: hw/ppc/mac_newworld.c
|
||||
F: hw/pci-host/uninorth.c
|
||||
F: hw/pci-bridge/dec.[hc]
|
||||
F: hw/misc/macio/
|
||||
F: hw/misc/mos6522.c
|
||||
F: hw/nvram/mac_nvram.c
|
||||
F: include/hw/misc/macio/
|
||||
F: include/hw/misc/mos6522.h
|
||||
F: include/hw/ppc/mac_dbdma.h
|
||||
F: hw/nvram/mac_nvram.c
|
||||
|
||||
Old World
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
@@ -791,10 +776,9 @@ F: hw/ppc/prep_systemio.c
|
||||
F: hw/ppc/rs6000_mc.c
|
||||
F: hw/pci-host/prep.[hc]
|
||||
F: hw/isa/i82378.c
|
||||
F: hw/isa/pc87312.c
|
||||
F: hw/isa/pc87312.[hc]
|
||||
F: hw/dma/i82374.c
|
||||
F: hw/timer/m48t59-isa.c
|
||||
F: include/hw/isa/pc87312.h
|
||||
F: include/hw/timer/m48t59.h
|
||||
F: pc-bios/ppc_rom.bin
|
||||
|
||||
@@ -917,7 +901,7 @@ X86 Machines
|
||||
------------
|
||||
PC
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/i386/
|
||||
F: hw/i386/
|
||||
@@ -940,7 +924,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Supported
|
||||
F: hw/char/debugcon.c
|
||||
F: hw/char/parallel*
|
||||
F: hw/char/parallel.c
|
||||
F: hw/char/serial*
|
||||
F: hw/dma/i8257*
|
||||
F: hw/i2c/pm_smbus.c
|
||||
@@ -948,7 +932,6 @@ F: hw/input/pckbd.c
|
||||
F: hw/intc/apic*
|
||||
F: hw/intc/ioapic*
|
||||
F: hw/intc/i8259*
|
||||
F: hw/isa/isa-superio.c
|
||||
F: hw/misc/debugexit.c
|
||||
F: hw/misc/pc-testdev.c
|
||||
F: hw/timer/hpet*
|
||||
@@ -956,18 +939,15 @@ F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
F: hw/watchdog/wdt_ib700.c
|
||||
F: include/hw/display/vga.h
|
||||
F: include/hw/char/parallel.h
|
||||
F: include/hw/dma/i8257.h
|
||||
F: include/hw/i2c/pm_smbus.h
|
||||
F: include/hw/input/i8042.h
|
||||
F: include/hw/isa/superio.h
|
||||
F: include/hw/isa/i8257.h
|
||||
F: include/hw/timer/hpet.h
|
||||
F: include/hw/timer/i8254*
|
||||
F: include/hw/timer/mc146818rtc*
|
||||
|
||||
Machine core
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Supported
|
||||
F: hw/core/machine.c
|
||||
F: hw/core/null-machine.c
|
||||
@@ -1006,7 +986,6 @@ F: hw/block/cdrom.c
|
||||
F: hw/block/hd-geometry.c
|
||||
F: tests/ide-test.c
|
||||
F: tests/ahci-test.c
|
||||
F: tests/cdrom-test.c
|
||||
F: tests/libqos/ahci*
|
||||
T: git git://github.com/jnsnow/qemu.git ide
|
||||
|
||||
@@ -1042,7 +1021,7 @@ F: hw/ipack/
|
||||
|
||||
PCI
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/pci/*
|
||||
F: hw/misc/pci-testdev.c
|
||||
@@ -1323,27 +1302,6 @@ S: Maintained
|
||||
F: include/hw/misc/unimp.h
|
||||
F: hw/misc/unimp.c
|
||||
|
||||
Standard VGA
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/display/vga*
|
||||
F: hw/display/bochs-display.c
|
||||
F: include/hw/display/vga.h
|
||||
F: include/hw/display/bochs-vbe.h
|
||||
|
||||
virtio-gpu
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/display/virtio-gpu*
|
||||
F: hw/display/virtio-vga.c
|
||||
F: include/hw/virtio/virtio-gpu.h
|
||||
|
||||
Cirrus VGA
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/
|
||||
F: hw/display/cirrus*
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
@@ -1369,8 +1327,6 @@ F: qemu-img*
|
||||
F: qemu-io*
|
||||
F: tests/qemu-iotests/
|
||||
F: util/qemu-progress.c
|
||||
F: qobject/block-qdict.c
|
||||
F: test/check-block-qdict.c
|
||||
T: git git://repo.or.cz/qemu/kevin.git block
|
||||
|
||||
Block I/O path
|
||||
@@ -1383,7 +1339,6 @@ F: util/aio-*.c
|
||||
F: block/io.c
|
||||
F: migration/block*
|
||||
F: include/block/aio.h
|
||||
F: include/block/aio-wait.h
|
||||
F: scripts/qemugdb/aio.py
|
||||
T: git git://github.com/stefanha/qemu.git block
|
||||
|
||||
@@ -1401,14 +1356,10 @@ L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: blockjob.c
|
||||
F: include/block/blockjob.h
|
||||
F: job.c
|
||||
F: job-qmp.c
|
||||
F: include/block/job.h
|
||||
F: block/backup.c
|
||||
F: block/commit.c
|
||||
F: block/stream.c
|
||||
F: block/mirror.c
|
||||
F: qapi/job.json
|
||||
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
|
||||
|
||||
Block QAPI, monitor, command line
|
||||
@@ -1681,7 +1632,6 @@ S: Maintained
|
||||
F: slirp/
|
||||
F: net/slirp.c
|
||||
F: include/net/slirp.h
|
||||
T: git https://people.debian.org/~sthibault/qemu.git slirp
|
||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Stubs
|
||||
@@ -1693,8 +1643,6 @@ Tracing
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Maintained
|
||||
F: trace/
|
||||
F: trace-events
|
||||
F: qemu-option-trace.texi
|
||||
F: scripts/tracetool.py
|
||||
F: scripts/tracetool/
|
||||
F: docs/devel/tracing.txt
|
||||
@@ -1820,12 +1768,6 @@ F: include/sysemu/replay.h
|
||||
F: docs/replay.txt
|
||||
F: stubs/replay.c
|
||||
|
||||
IOVA Tree
|
||||
M: Peter Xu <peterx@redhat.com>
|
||||
S: Maintained
|
||||
F: include/qemu/iova-tree.h
|
||||
F: util/iova-tree.c
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
@@ -2120,7 +2062,7 @@ F: docs/block-replication.txt
|
||||
|
||||
PVRDMA
|
||||
M: Yuval Shaia <yuval.shaia@oracle.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/rdma/*
|
||||
F: hw/rdma/vmw/*
|
||||
|
||||
61
Makefile
61
Makefile
@@ -62,8 +62,8 @@ seems to have been used for an in-tree build. You can fix this by running \
|
||||
endif
|
||||
endif
|
||||
|
||||
CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_LIST)),y)
|
||||
CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_LIST)),y)
|
||||
CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y)
|
||||
CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y)
|
||||
CONFIG_XEN := $(CONFIG_XEN_BACKEND)
|
||||
CONFIG_ALL=y
|
||||
-include config-all-devices.mak
|
||||
@@ -98,7 +98,6 @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
|
||||
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
|
||||
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
|
||||
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
|
||||
GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c
|
||||
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
|
||||
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
|
||||
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
|
||||
@@ -117,7 +116,6 @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
|
||||
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
|
||||
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
|
||||
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
|
||||
GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c
|
||||
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
|
||||
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
|
||||
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
|
||||
@@ -135,7 +133,6 @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
|
||||
GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
|
||||
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
|
||||
GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
|
||||
GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c
|
||||
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
|
||||
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
|
||||
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
|
||||
@@ -153,7 +150,6 @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
|
||||
GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
|
||||
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
|
||||
GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
|
||||
GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c
|
||||
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
|
||||
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
|
||||
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
|
||||
@@ -322,7 +318,6 @@ KEYCODEMAP_FILES = \
|
||||
ui/input-keymap-xorgkbd-to-qcode.c \
|
||||
ui/input-keymap-xorgxquartz-to-qcode.c \
|
||||
ui/input-keymap-xorgxwin-to-qcode.c \
|
||||
ui/input-keymap-osx-to-qcode.c \
|
||||
$(NULL)
|
||||
|
||||
GENERATED_FILES += $(KEYCODEMAP_FILES)
|
||||
@@ -352,7 +347,7 @@ $(call set-vpath, $(SRC_PATH))
|
||||
|
||||
LIBS+=-lz $(LIBS_TOOLS)
|
||||
|
||||
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
|
||||
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
||||
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
@@ -367,8 +362,8 @@ DOCS=
|
||||
endif
|
||||
|
||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
|
||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_LIST))
|
||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_LIST))
|
||||
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:
|
||||
@@ -443,23 +438,21 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||
qemu-version.h: FORCE
|
||||
$(call quiet-command, \
|
||||
(cd $(SRC_PATH); \
|
||||
printf '#define QEMU_PKGVERSION '; \
|
||||
if test -n "$(PKGVERSION)"; then \
|
||||
pkgvers="$(PKGVERSION)"; \
|
||||
printf '"$(PKGVERSION)"\n'; \
|
||||
else \
|
||||
if test -d .git; then \
|
||||
pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
|
||||
printf '" ('; \
|
||||
git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
|
||||
if ! git diff-index --quiet HEAD &>/dev/null; then \
|
||||
pkgvers="$${pkgvers}-dirty"; \
|
||||
printf -- '-dirty'; \
|
||||
fi; \
|
||||
printf ')"\n'; \
|
||||
else \
|
||||
printf '""\n'; \
|
||||
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)
|
||||
fi) > $@.tmp)
|
||||
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
||||
mv $@.tmp $@; \
|
||||
else \
|
||||
@@ -471,7 +464,7 @@ config-host.h-timestamp: config-host.mak
|
||||
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
|
||||
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_LIST))
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||
|
||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||
@@ -490,7 +483,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
|
||||
|
||||
dtc/%: .git-submodule-status
|
||||
@mkdir -p $@
|
||||
mkdir -p $@
|
||||
|
||||
# Overriding CFLAGS causes us to lose defines added in the sub-makefile.
|
||||
# Not overriding CFLAGS leads to mis-matches between compilation modes.
|
||||
@@ -515,7 +508,7 @@ ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
romsubdir-%:
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/" CFLAGS="$(filter -O% -g%,$(CFLAGS))",)
|
||||
|
||||
ALL_SUBDIRS=$(TARGET_LIST) $(patsubst %,pc-bios/%, $(ROMS))
|
||||
ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
|
||||
|
||||
recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
|
||||
|
||||
@@ -568,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \
|
||||
$(SRC_PATH)/scripts/qapi/visit.py \
|
||||
$(SRC_PATH)/scripts/qapi/common.py \
|
||||
$(SRC_PATH)/scripts/qapi/doc.py \
|
||||
$(SRC_PATH)/scripts/ordereddict.py \
|
||||
$(SRC_PATH)/scripts/qapi-gen.py
|
||||
|
||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
|
||||
@@ -586,7 +580,6 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
||||
$(SRC_PATH)/qapi/char.json \
|
||||
$(SRC_PATH)/qapi/crypto.json \
|
||||
$(SRC_PATH)/qapi/introspect.json \
|
||||
$(SRC_PATH)/qapi/job.json \
|
||||
$(SRC_PATH)/qapi/migration.json \
|
||||
$(SRC_PATH)/qapi/misc.json \
|
||||
$(SRC_PATH)/qapi/net.json \
|
||||
@@ -606,7 +599,6 @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \
|
||||
qapi/qapi-types-common.c qapi/qapi-types-common.h \
|
||||
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
|
||||
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
|
||||
qapi/qapi-types-job.c qapi/qapi-types-job.h \
|
||||
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
|
||||
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
|
||||
qapi/qapi-types-net.c qapi/qapi-types-net.h \
|
||||
@@ -625,7 +617,6 @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
|
||||
qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
|
||||
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
|
||||
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
|
||||
qapi/qapi-visit-job.c qapi/qapi-visit-job.h \
|
||||
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
|
||||
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
|
||||
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
|
||||
@@ -643,7 +634,6 @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
|
||||
qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
|
||||
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
|
||||
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
|
||||
qapi/qapi-commands-job.c qapi/qapi-commands-job.h \
|
||||
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
|
||||
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
|
||||
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
|
||||
@@ -661,7 +651,6 @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \
|
||||
qapi/qapi-events-common.c qapi/qapi-events-common.h \
|
||||
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
|
||||
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
|
||||
qapi/qapi-events-job.c qapi/qapi-events-job.h \
|
||||
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
|
||||
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
|
||||
qapi/qapi-events-net.c qapi/qapi-events-net.h \
|
||||
@@ -772,7 +761,7 @@ distclean: clean
|
||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
rm -f docs/qemu-block-drivers.7
|
||||
for d in $(TARGET_LIST); do \
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
rm -Rf .sdk
|
||||
@@ -786,6 +775,7 @@ bepo cz
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
@@ -865,7 +855,7 @@ ifneq ($(BLOBS),)
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||
done
|
||||
endif
|
||||
ifdef CONFIG_GTK
|
||||
ifeq ($(CONFIG_GTK),m)
|
||||
$(MAKE) -C po $@
|
||||
endif
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
||||
@@ -873,7 +863,7 @@ endif
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
|
||||
done
|
||||
$(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all"
|
||||
for d in $(TARGET_LIST); do \
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
@@ -1060,17 +1050,14 @@ include $(SRC_PATH)/tests/vm/Makefile.include
|
||||
help:
|
||||
@echo 'Generic targets:'
|
||||
@echo ' all - Build all'
|
||||
ifdef CONFIG_MODULES
|
||||
@echo ' modules - Build all modules'
|
||||
endif
|
||||
@echo ' dir/file.o - Build specified target only'
|
||||
@echo ' install - Install QEMU, documentation and tools'
|
||||
@echo ' ctags/TAGS - Generate tags file for editors'
|
||||
@echo ' cscope - Generate cscope index'
|
||||
@echo ''
|
||||
@$(if $(TARGET_LIST), \
|
||||
@$(if $(TARGET_DIRS), \
|
||||
echo 'Architecture specific targets:'; \
|
||||
$(foreach t, $(TARGET_LIST), \
|
||||
$(foreach t, $(TARGET_DIRS), \
|
||||
printf " %-30s - Build for %s\\n" $(patsubst %,subdir-%,$(t)) $(t);) \
|
||||
echo '')
|
||||
@echo 'Cleaning targets:'
|
||||
|
||||
@@ -10,7 +10,6 @@ util-obj-y += qapi/qapi-types-char.o
|
||||
util-obj-y += qapi/qapi-types-common.o
|
||||
util-obj-y += qapi/qapi-types-crypto.o
|
||||
util-obj-y += qapi/qapi-types-introspect.o
|
||||
util-obj-y += qapi/qapi-types-job.o
|
||||
util-obj-y += qapi/qapi-types-migration.o
|
||||
util-obj-y += qapi/qapi-types-misc.o
|
||||
util-obj-y += qapi/qapi-types-net.o
|
||||
@@ -29,7 +28,6 @@ util-obj-y += qapi/qapi-visit-char.o
|
||||
util-obj-y += qapi/qapi-visit-common.o
|
||||
util-obj-y += qapi/qapi-visit-crypto.o
|
||||
util-obj-y += qapi/qapi-visit-introspect.o
|
||||
util-obj-y += qapi/qapi-visit-job.o
|
||||
util-obj-y += qapi/qapi-visit-migration.o
|
||||
util-obj-y += qapi/qapi-visit-misc.o
|
||||
util-obj-y += qapi/qapi-visit-net.o
|
||||
@@ -47,7 +45,6 @@ util-obj-y += qapi/qapi-events-char.o
|
||||
util-obj-y += qapi/qapi-events-common.o
|
||||
util-obj-y += qapi/qapi-events-crypto.o
|
||||
util-obj-y += qapi/qapi-events-introspect.o
|
||||
util-obj-y += qapi/qapi-events-job.o
|
||||
util-obj-y += qapi/qapi-events-migration.o
|
||||
util-obj-y += qapi/qapi-events-misc.o
|
||||
util-obj-y += qapi/qapi-events-net.o
|
||||
@@ -66,7 +63,7 @@ chardev-obj-y = chardev/
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y += nbd/
|
||||
block-obj-y += block.o blockjob.o job.o
|
||||
block-obj-y += block.o blockjob.o
|
||||
block-obj-y += block/ scsi/
|
||||
block-obj-y += qemu-io-cmds.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
@@ -97,7 +94,6 @@ io-obj-y = io/
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
common-obj-y = blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += bootdevice.o iothread.o
|
||||
common-obj-y += job-qmp.o
|
||||
common-obj-y += net/
|
||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
@@ -144,7 +140,6 @@ common-obj-y += qapi/qapi-commands-char.o
|
||||
common-obj-y += qapi/qapi-commands-common.o
|
||||
common-obj-y += qapi/qapi-commands-crypto.o
|
||||
common-obj-y += qapi/qapi-commands-introspect.o
|
||||
common-obj-y += qapi/qapi-commands-job.o
|
||||
common-obj-y += qapi/qapi-commands-migration.o
|
||||
common-obj-y += qapi/qapi-commands-misc.o
|
||||
common-obj-y += qapi/qapi-commands-net.o
|
||||
@@ -196,67 +191,66 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/
|
||||
|
||||
######################################################################
|
||||
trace-events-subdirs =
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += util
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += io
|
||||
trace-events-subdirs += migration
|
||||
trace-events-subdirs += block
|
||||
trace-events-subdirs += chardev
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/audio
|
||||
trace-events-subdirs += hw/block
|
||||
trace-events-subdirs += hw/block/dataplane
|
||||
trace-events-subdirs += hw/char
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/i2c
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/intc
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
trace-events-subdirs += hw/misc
|
||||
trace-events-subdirs += hw/misc/macio
|
||||
trace-events-subdirs += hw/net
|
||||
trace-events-subdirs += hw/nvram
|
||||
trace-events-subdirs += hw/pci
|
||||
trace-events-subdirs += hw/pci-host
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/rdma
|
||||
trace-events-subdirs += hw/rdma/vmw
|
||||
trace-events-subdirs += hw/s390x
|
||||
trace-events-subdirs += hw/virtio
|
||||
trace-events-subdirs += hw/audio
|
||||
trace-events-subdirs += hw/misc
|
||||
trace-events-subdirs += hw/misc/macio
|
||||
trace-events-subdirs += hw/usb
|
||||
trace-events-subdirs += hw/scsi
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/nvram
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += hw/usb
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/pci
|
||||
trace-events-subdirs += hw/pci-host
|
||||
trace-events-subdirs += hw/s390x
|
||||
trace-events-subdirs += hw/vfio
|
||||
trace-events-subdirs += hw/virtio
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += io
|
||||
trace-events-subdirs += linux-user
|
||||
trace-events-subdirs += migration
|
||||
trace-events-subdirs += nbd
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += ui
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += net
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += scsi
|
||||
trace-events-subdirs += target/arm
|
||||
trace-events-subdirs += target/i386
|
||||
trace-events-subdirs += target/mips
|
||||
trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/sparc
|
||||
trace-events-subdirs += ui
|
||||
trace-events-subdirs += util
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += linux-user
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += nbd
|
||||
trace-events-subdirs += scsi
|
||||
|
||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ $(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
endif
|
||||
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
|
||||
QEMU_CFLAGS+=-iquote $(SRC_PATH)/include
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/include
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
# user emulator name
|
||||
@@ -97,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
|
||||
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-$(CONFIG_TCG) += fpu/softfloat.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target/$(TARGET_BASE_ARCH)/
|
||||
obj-y += disas.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(CONFIG_KVM) += kvm/
|
||||
obj-y += kvm/
|
||||
obj-$(CONFIG_TCG) += tcg/
|
||||
obj-y += stubs/
|
||||
|
||||
@@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
||||
|
||||
void configure_accelerator(MachineState *ms)
|
||||
{
|
||||
const char *accel;
|
||||
char **accel_list, **tmp;
|
||||
const char *accel, *p;
|
||||
char buf[10];
|
||||
int ret;
|
||||
bool accel_initialised = false;
|
||||
bool init_failed = false;
|
||||
@@ -83,10 +83,13 @@ void configure_accelerator(MachineState *ms)
|
||||
accel = "tcg";
|
||||
}
|
||||
|
||||
accel_list = g_strsplit(accel, ":", 0);
|
||||
|
||||
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
|
||||
acc = accel_find(*tmp);
|
||||
p = accel;
|
||||
while (!accel_initialised && *p != '\0') {
|
||||
if (*p == ':') {
|
||||
p++;
|
||||
}
|
||||
p = get_opt_name(buf, sizeof(buf), p, ':');
|
||||
acc = accel_find(buf);
|
||||
if (!acc) {
|
||||
continue;
|
||||
}
|
||||
@@ -104,7 +107,6 @@ void configure_accelerator(MachineState *ms)
|
||||
accel_initialised = true;
|
||||
}
|
||||
}
|
||||
g_strfreev(accel_list);
|
||||
|
||||
if (!accel_initialised) {
|
||||
if (!init_failed) {
|
||||
@@ -124,15 +126,6 @@ void accel_register_compat_props(AccelState *accel)
|
||||
register_compat_props_array(class->global_props);
|
||||
}
|
||||
|
||||
void accel_setup_post(MachineState *ms)
|
||||
{
|
||||
AccelState *accel = ms->accelerator;
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
if (acc->setup_post) {
|
||||
acc->setup_post(ms, accel);
|
||||
}
|
||||
}
|
||||
|
||||
static void register_accel_types(void)
|
||||
{
|
||||
type_register_static(&accel_type);
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
obj-y += kvm-all.o
|
||||
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sev.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
@@ -104,10 +103,6 @@ struct KVMState
|
||||
#endif
|
||||
KVMMemoryListener memory_listener;
|
||||
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
|
||||
|
||||
/* memory encryption */
|
||||
void *memcrypt_handle;
|
||||
int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
|
||||
};
|
||||
|
||||
KVMState *kvm_state;
|
||||
@@ -143,26 +138,6 @@ int kvm_get_max_memslots(void)
|
||||
return s->nr_slots;
|
||||
}
|
||||
|
||||
bool kvm_memcrypt_enabled(void)
|
||||
{
|
||||
if (kvm_state && kvm_state->memcrypt_handle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
if (kvm_state->memcrypt_handle &&
|
||||
kvm_state->memcrypt_encrypt_data) {
|
||||
return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
|
||||
ptr, len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
@@ -1661,20 +1636,6 @@ static int kvm_init(MachineState *ms)
|
||||
|
||||
kvm_state = s;
|
||||
|
||||
/*
|
||||
* if memory encryption object is specified then initialize the memory
|
||||
* encryption context.
|
||||
*/
|
||||
if (ms->memory_encryption) {
|
||||
kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
|
||||
if (!kvm_state->memcrypt_handle) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
|
||||
}
|
||||
|
||||
ret = kvm_arch_init(ms, s);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* QEMU SEV stub
|
||||
*
|
||||
* Copyright Advanced Micro Devices 2018
|
||||
*
|
||||
* Authors:
|
||||
* Brijesh Singh <brijesh.singh@amd.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 "qemu-common.h"
|
||||
#include "sysemu/sev.h"
|
||||
|
||||
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void *sev_guest_init(const char *id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool kvm_memcrypt_enabled(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
|
||||
{
|
||||
|
||||
@@ -25,22 +25,18 @@
|
||||
#elif DATA_SIZE == 8
|
||||
# define SUFFIX q
|
||||
# define DATA_TYPE uint64_t
|
||||
# define SDATA_TYPE int64_t
|
||||
# define BSWAP bswap64
|
||||
#elif DATA_SIZE == 4
|
||||
# define SUFFIX l
|
||||
# define DATA_TYPE uint32_t
|
||||
# define SDATA_TYPE int32_t
|
||||
# define BSWAP bswap32
|
||||
#elif DATA_SIZE == 2
|
||||
# define SUFFIX w
|
||||
# define DATA_TYPE uint16_t
|
||||
# define SDATA_TYPE int16_t
|
||||
# define BSWAP bswap16
|
||||
#elif DATA_SIZE == 1
|
||||
# define SUFFIX b
|
||||
# define DATA_TYPE uint8_t
|
||||
# define SDATA_TYPE int8_t
|
||||
# define BSWAP
|
||||
#else
|
||||
# error unsupported data size
|
||||
@@ -122,39 +118,6 @@ GEN_ATOMIC_HELPER(or_fetch)
|
||||
GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
|
||||
/* These helpers are, as a whole, full barriers. Within the helper,
|
||||
* the leading barrier is explicit and the trailing barrier is within
|
||||
* cmpxchg primitive.
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
smp_mb(); \
|
||||
cmp = atomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
old = cmp; new = FN(old, val); \
|
||||
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
|
||||
} while (cmp != old); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
return RET; \
|
||||
}
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
#endif /* DATA SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
@@ -229,45 +192,47 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
|
||||
/* These helpers are, as a whole, full barriers. Within the helper,
|
||||
* the leading barrier is explicit and the trailing barrier is within
|
||||
* cmpxchg primitive.
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE ldo, ldn, old, new, val = xval; \
|
||||
smp_mb(); \
|
||||
ldn = atomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
|
||||
} while (ldo != ldn); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
return RET; \
|
||||
}
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
|
||||
/* Note that for addition, we need to use a separate cmpxchg loop instead
|
||||
of bswaps for the reverse-host-endian helpers. */
|
||||
#define ADD(X, Y) (X + Y)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||
#undef ADD
|
||||
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ldo, ldn, ret, sto;
|
||||
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
while (1) {
|
||||
ret = BSWAP(ldo);
|
||||
sto = BSWAP(ret + val);
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
|
||||
if (ldn == ldo) {
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
ldo = ldn;
|
||||
}
|
||||
}
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ldo, ldn, ret, sto;
|
||||
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
while (1) {
|
||||
ret = BSWAP(ldo) + val;
|
||||
sto = BSWAP(ret);
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
|
||||
if (ldn == ldo) {
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
ldo = ldn;
|
||||
}
|
||||
}
|
||||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
@@ -276,6 +241,5 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||
#undef BSWAP
|
||||
#undef ABI_TYPE
|
||||
#undef DATA_TYPE
|
||||
#undef SDATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef DATA_SIZE
|
||||
|
||||
@@ -27,8 +27,10 @@ bool tcg_allowed;
|
||||
/* exit the current TB, but without causing any exception to be raised */
|
||||
void cpu_loop_exit_noexc(CPUState *cpu)
|
||||
{
|
||||
/* XXX: restore cpu registers saved in host registers */
|
||||
|
||||
cpu->exception_index = -1;
|
||||
cpu_loop_exit(cpu);
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
@@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void)
|
||||
|
||||
void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
/* Undo the setting in cpu_tb_exec. */
|
||||
cpu->can_do_io = 1;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
||||
{
|
||||
if (pc) {
|
||||
cpu_restore_state(cpu, pc, true);
|
||||
cpu_restore_state(cpu, pc);
|
||||
}
|
||||
cpu_loop_exit(cpu);
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "exec/tb-lookup.h"
|
||||
@@ -155,14 +156,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
|
||||
&& qemu_log_in_addr_range(itb->pc)) {
|
||||
qemu_log_lock();
|
||||
int flags = 0;
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
||||
flags |= CPU_DUMP_FPU;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
flags |= CPU_DUMP_CCOP;
|
||||
log_cpu_state(cpu, CPU_DUMP_CCOP);
|
||||
#else
|
||||
log_cpu_state(cpu, 0);
|
||||
#endif
|
||||
log_cpu_state(cpu, flags);
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
@@ -587,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
else {
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
replay_interrupt();
|
||||
cpu->exception_index = -1;
|
||||
*last_tb = NULL;
|
||||
}
|
||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
||||
@@ -609,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
if (unlikely(atomic_read(&cpu->exit_request)
|
||||
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
|
||||
atomic_set(&cpu->exit_request, 0);
|
||||
if (cpu->exception_index == -1) {
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
}
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -706,6 +701,7 @@ int cpu_exec(CPUState *cpu)
|
||||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
#endif /* buggy compiler */
|
||||
cpu->can_do_io = 1;
|
||||
tb_lock_reset();
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
@@ -632,8 +632,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
}
|
||||
|
||||
sz = size;
|
||||
section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz,
|
||||
attrs, &prot);
|
||||
section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz);
|
||||
assert(sz >= TARGET_PAGE_SIZE);
|
||||
|
||||
tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
|
||||
@@ -665,18 +664,6 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index];
|
||||
|
||||
/* refill the tlb */
|
||||
/*
|
||||
* At this point iotlb contains a physical section number in the lower
|
||||
* TARGET_PAGE_BITS, and either
|
||||
* + the ram_addr_t of the page base of the target RAM (if NOTDIRTY or ROM)
|
||||
* + the offset within section->mr of the page base (otherwise)
|
||||
* We subtract the vaddr (which is page aligned and thus won't
|
||||
* disturb the low bits) to give an offset which can be added to the
|
||||
* (non-page-aligned) vaddr of the eventual memory access to get
|
||||
* the MemoryRegion offset for the access. Note that the vaddr we
|
||||
* subtract here is that of the page base, and not the same as the
|
||||
* vaddr we add back in io_readx()/io_writex()/get_page_addr_code().
|
||||
*/
|
||||
env->iotlb[mmu_idx][index].addr = iotlb - vaddr;
|
||||
env->iotlb[mmu_idx][index].attrs = attrs;
|
||||
|
||||
@@ -778,16 +765,13 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
target_ulong addr, uintptr_t retaddr, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr mr_offset;
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
uint64_t val;
|
||||
bool locked = false;
|
||||
MemTxResult r;
|
||||
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||
cpu->mem_io_pc = retaddr;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
@@ -799,13 +783,9 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
qemu_mutex_lock_iothread();
|
||||
locked = true;
|
||||
}
|
||||
r = memory_region_dispatch_read(mr, mr_offset,
|
||||
r = memory_region_dispatch_read(mr, physaddr,
|
||||
&val, size, iotlbentry->attrs);
|
||||
if (r != MEMTX_OK) {
|
||||
hwaddr physaddr = mr_offset +
|
||||
section->offset_within_address_space -
|
||||
section->offset_within_region;
|
||||
|
||||
cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD,
|
||||
mmu_idx, iotlbentry->attrs, r, retaddr);
|
||||
}
|
||||
@@ -822,15 +802,12 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
uintptr_t retaddr, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr mr_offset;
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
bool locked = false;
|
||||
MemTxResult r;
|
||||
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
@@ -841,13 +818,9 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
qemu_mutex_lock_iothread();
|
||||
locked = true;
|
||||
}
|
||||
r = memory_region_dispatch_write(mr, mr_offset,
|
||||
r = memory_region_dispatch_write(mr, physaddr,
|
||||
val, size, iotlbentry->attrs);
|
||||
if (r != MEMTX_OK) {
|
||||
hwaddr physaddr = mr_offset +
|
||||
section->offset_within_address_space -
|
||||
section->offset_within_region;
|
||||
|
||||
cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_STORE,
|
||||
mmu_idx, iotlbentry->attrs, r, retaddr);
|
||||
}
|
||||
@@ -895,13 +868,12 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
*/
|
||||
tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
{
|
||||
int mmu_idx, index;
|
||||
int mmu_idx, index, pd;
|
||||
void *p;
|
||||
MemoryRegion *mr;
|
||||
MemoryRegionSection *section;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
hwaddr physaddr, mr_offset;
|
||||
hwaddr physaddr;
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = cpu_mmu_index(env, true);
|
||||
@@ -912,8 +884,8 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
}
|
||||
}
|
||||
iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
pd = iotlbentry->addr & ~TARGET_PAGE_MASK;
|
||||
mr = iotlb_to_region(cpu, pd, iotlbentry->attrs);
|
||||
if (memory_region_is_unassigned(mr)) {
|
||||
qemu_mutex_lock_iothread();
|
||||
if (memory_region_request_mmio_ptr(mr, addr)) {
|
||||
@@ -934,10 +906,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
* and use the MemTXResult it produced). However it is the
|
||||
* simplest place we have currently available for the check.
|
||||
*/
|
||||
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
physaddr = mr_offset +
|
||||
section->offset_within_address_space -
|
||||
section->offset_within_region;
|
||||
physaddr = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx,
|
||||
iotlbentry->attrs, MEMTX_DECODE_ERROR, 0);
|
||||
|
||||
|
||||
@@ -705,7 +705,7 @@ void HELPER(NAME)(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(TYPE)) { \
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) { \
|
||||
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||
} \
|
||||
clear_high(d, oprsz, desc); \
|
||||
|
||||
@@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add)
|
||||
GEN_ATOMIC_HELPERS(fetch_and)
|
||||
GEN_ATOMIC_HELPERS(fetch_or)
|
||||
GEN_ATOMIC_HELPERS(fetch_xor)
|
||||
GEN_ATOMIC_HELPERS(fetch_smin)
|
||||
GEN_ATOMIC_HELPERS(fetch_umin)
|
||||
GEN_ATOMIC_HELPERS(fetch_smax)
|
||||
GEN_ATOMIC_HELPERS(fetch_umax)
|
||||
|
||||
GEN_ATOMIC_HELPERS(add_fetch)
|
||||
GEN_ATOMIC_HELPERS(and_fetch)
|
||||
GEN_ATOMIC_HELPERS(or_fetch)
|
||||
GEN_ATOMIC_HELPERS(xor_fetch)
|
||||
GEN_ATOMIC_HELPERS(smin_fetch)
|
||||
GEN_ATOMIC_HELPERS(umin_fetch)
|
||||
GEN_ATOMIC_HELPERS(smax_fetch)
|
||||
GEN_ATOMIC_HELPERS(umax_fetch)
|
||||
|
||||
GEN_ATOMIC_HELPERS(xchg)
|
||||
|
||||
|
||||
@@ -299,11 +299,9 @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
|
||||
|
||||
/* The cpu state corresponding to 'searched_pc' is restored.
|
||||
* Called with tb_lock held.
|
||||
* When reset_icount is true, current TB will be interrupted and
|
||||
* icount should be recalculated.
|
||||
*/
|
||||
static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
uintptr_t searched_pc, bool reset_icount)
|
||||
uintptr_t searched_pc)
|
||||
{
|
||||
target_ulong data[TARGET_INSN_START_WORDS] = { tb->pc };
|
||||
uintptr_t host_pc = (uintptr_t)tb->tc.ptr;
|
||||
@@ -335,12 +333,14 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
return -1;
|
||||
|
||||
found:
|
||||
if (reset_icount && (tb->cflags & CF_USE_ICOUNT)) {
|
||||
if (tb->cflags & CF_USE_ICOUNT) {
|
||||
assert(use_icount);
|
||||
/* Reset the cycle counter to the start of the block
|
||||
and shift if to the number of actually executed instructions */
|
||||
cpu->icount_decr.u16.low += num_insns - i;
|
||||
/* Reset the cycle counter to the start of the block. */
|
||||
cpu->icount_decr.u16.low += num_insns;
|
||||
/* Clear the IO flag. */
|
||||
cpu->can_do_io = 0;
|
||||
}
|
||||
cpu->icount_decr.u16.low -= i;
|
||||
restore_state_to_opc(env, tb, data);
|
||||
|
||||
#ifdef CONFIG_PROFILER
|
||||
@@ -351,7 +351,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
bool r = false;
|
||||
@@ -377,7 +377,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
tb_lock();
|
||||
tb = tb_find_pc(host_pc);
|
||||
if (tb) {
|
||||
cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit);
|
||||
cpu_restore_state_from_tb(cpu, tb, host_pc);
|
||||
if (tb->cflags & CF_NOCACHE) {
|
||||
/* one-shot translation, invalidate it immediately */
|
||||
tb_phys_invalidate(tb, -1);
|
||||
@@ -644,8 +644,11 @@ static inline void *alloc_code_gen_buffer(void)
|
||||
static inline void *alloc_code_gen_buffer(void)
|
||||
{
|
||||
size_t size = tcg_ctx->code_gen_buffer_size;
|
||||
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT,
|
||||
void *buf;
|
||||
|
||||
buf = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
return buf;
|
||||
}
|
||||
#else
|
||||
static inline void *alloc_code_gen_buffer(void)
|
||||
@@ -1508,8 +1511,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
|
||||
restore the CPU state */
|
||||
|
||||
current_tb_modified = 1;
|
||||
cpu_restore_state_from_tb(cpu, current_tb,
|
||||
cpu->mem_io_pc, true);
|
||||
cpu_restore_state_from_tb(cpu, current_tb, cpu->mem_io_pc);
|
||||
cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base,
|
||||
¤t_flags);
|
||||
}
|
||||
@@ -1632,7 +1634,7 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc)
|
||||
restore the CPU state */
|
||||
|
||||
current_tb_modified = 1;
|
||||
cpu_restore_state_from_tb(cpu, current_tb, pc, true);
|
||||
cpu_restore_state_from_tb(cpu, current_tb, pc);
|
||||
cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base,
|
||||
¤t_flags);
|
||||
}
|
||||
@@ -1669,14 +1671,14 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr)
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs)
|
||||
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr)
|
||||
{
|
||||
ram_addr_t ram_addr;
|
||||
MemoryRegion *mr;
|
||||
hwaddr l = 1;
|
||||
|
||||
rcu_read_lock();
|
||||
mr = address_space_translate(as, addr, &addr, &l, false, attrs);
|
||||
mr = address_space_translate(as, addr, &addr, &l, false);
|
||||
if (!(memory_region_is_ram(mr)
|
||||
|| memory_region_is_romd(mr))) {
|
||||
rcu_read_unlock();
|
||||
@@ -1698,7 +1700,7 @@ void tb_check_watchpoint(CPUState *cpu)
|
||||
tb = tb_find_pc(cpu->mem_io_pc);
|
||||
if (tb) {
|
||||
/* We can use retranslation to find the PC. */
|
||||
cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc, true);
|
||||
cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc);
|
||||
tb_phys_invalidate(tb, -1);
|
||||
} else {
|
||||
/* The exception probably happened in a helper. The CPU state should
|
||||
@@ -1734,32 +1736,37 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
cpu_abort(cpu, "cpu_io_recompile: could not find TB for pc=%p",
|
||||
(void *)retaddr);
|
||||
}
|
||||
cpu_restore_state_from_tb(cpu, tb, retaddr, true);
|
||||
|
||||
n = cpu->icount_decr.u16.low + tb->icount;
|
||||
cpu_restore_state_from_tb(cpu, tb, retaddr);
|
||||
/* Calculate how many instructions had been executed before the fault
|
||||
occurred. */
|
||||
n = n - cpu->icount_decr.u16.low;
|
||||
/* Generate a new TB ending on the I/O insn. */
|
||||
n++;
|
||||
/* On MIPS and SH, delay slot instructions can only be restarted if
|
||||
they were already the first instruction in the TB. If this is not
|
||||
the first instruction in a TB then re-execute the preceding
|
||||
branch. */
|
||||
n = 1;
|
||||
#if defined(TARGET_MIPS)
|
||||
if ((env->hflags & MIPS_HFLAG_BMASK) != 0
|
||||
&& env->active_tc.PC != tb->pc) {
|
||||
if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) {
|
||||
env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
|
||||
cpu->icount_decr.u16.low++;
|
||||
env->hflags &= ~MIPS_HFLAG_BMASK;
|
||||
n = 2;
|
||||
}
|
||||
#elif defined(TARGET_SH4)
|
||||
if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
|
||||
&& env->pc != tb->pc) {
|
||||
&& n > 1) {
|
||||
env->pc -= 2;
|
||||
cpu->icount_decr.u16.low++;
|
||||
env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
|
||||
n = 2;
|
||||
}
|
||||
#endif
|
||||
/* This should never happen. */
|
||||
if (n > CF_COUNT_MASK) {
|
||||
cpu_abort(cpu, "TB too big during recompile");
|
||||
}
|
||||
|
||||
/* Generate a new TB executing the I/O insn. */
|
||||
/* Adjust the execution state of the next TB. */
|
||||
cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n;
|
||||
|
||||
if (tb->cflags & CF_NOCACHE) {
|
||||
|
||||
@@ -34,6 +34,8 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int max_insns;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
db->pc_first = tb->pc;
|
||||
@@ -43,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
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;
|
||||
max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = CF_COUNT_MASK;
|
||||
}
|
||||
if (db->max_insns > TCG_MAX_INSNS) {
|
||||
db->max_insns = TCG_MAX_INSNS;
|
||||
if (max_insns > TCG_MAX_INSNS) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
if (db->singlestep_enabled || singlestep) {
|
||||
db->max_insns = 1;
|
||||
max_insns = 1;
|
||||
}
|
||||
|
||||
ops->init_disas_context(db, cpu);
|
||||
max_insns = ops->init_disas_context(db, cpu, max_insns);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Reset the temp count so that we can identify leaks */
|
||||
@@ -93,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
update db->pc_next and db->is_jmp to indicate what should be
|
||||
done next -- either exiting this loop or locate the start of
|
||||
the next instruction. */
|
||||
if (db->num_insns == db->max_insns
|
||||
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
|
||||
if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) {
|
||||
/* Accept I/O on the last instruction. */
|
||||
gen_io_start();
|
||||
ops->translate_insn(db, cpu);
|
||||
@@ -110,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Stop translation if the output buffer is full,
|
||||
or we have executed all of the allowed instructions. */
|
||||
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
|
||||
if (tcg_op_buf_full() || db->num_insns >= max_insns) {
|
||||
db->is_jmp = DISAS_TOO_MANY;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
}
|
||||
|
||||
/* Now we have a real cpu fault. */
|
||||
cpu_restore_state(cpu, pc, true);
|
||||
cpu_restore_state(cpu, pc);
|
||||
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
cpu_loop_exit(cpu);
|
||||
|
||||
20
arch_init.c
20
arch_init.c
@@ -29,7 +29,6 @@
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/soundhw.h"
|
||||
#include "qapi/qapi-commands-misc.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
@@ -52,14 +51,14 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_ARM
|
||||
#elif defined(TARGET_CRIS)
|
||||
#define QEMU_ARCH QEMU_ARCH_CRIS
|
||||
#elif defined(TARGET_HPPA)
|
||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||
#elif defined(TARGET_I386)
|
||||
#define QEMU_ARCH QEMU_ARCH_I386
|
||||
#elif defined(TARGET_LM32)
|
||||
#define QEMU_ARCH QEMU_ARCH_LM32
|
||||
#elif defined(TARGET_HPPA)
|
||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||
#elif defined(TARGET_M68K)
|
||||
#define QEMU_ARCH QEMU_ARCH_M68K
|
||||
#elif defined(TARGET_LM32)
|
||||
#define QEMU_ARCH QEMU_ARCH_LM32
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
|
||||
#elif defined(TARGET_MIPS)
|
||||
@@ -80,12 +79,12 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_SH4
|
||||
#elif defined(TARGET_SPARC)
|
||||
#define QEMU_ARCH QEMU_ARCH_SPARC
|
||||
#elif defined(TARGET_TRICORE)
|
||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#elif defined(TARGET_XTENSA)
|
||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#elif defined(TARGET_TRICORE)
|
||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
|
||||
#endif
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
@@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp)
|
||||
{
|
||||
TargetInfo *info = g_malloc0(sizeof(*info));
|
||||
|
||||
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
|
||||
&error_abort);
|
||||
info->arch = g_strdup(TARGET_NAME);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/virtio/vhost-user.h"
|
||||
#include "standard-headers/linux/virtio_crypto.h"
|
||||
#include "sysemu/cryptodev-vhost.h"
|
||||
#include "chardev/char-fe.h"
|
||||
@@ -47,7 +46,6 @@
|
||||
typedef struct CryptoDevBackendVhostUser {
|
||||
CryptoDevBackend parent_obj;
|
||||
|
||||
VhostUserState *vhost_user;
|
||||
CharBackend chr;
|
||||
char *chr_name;
|
||||
bool opened;
|
||||
@@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues,
|
||||
continue;
|
||||
}
|
||||
|
||||
options.opaque = s->vhost_user;
|
||||
options.opaque = &s->chr;
|
||||
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
||||
options.cc = b->conf.peers.ccs[i];
|
||||
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
|
||||
@@ -187,7 +185,6 @@ 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);
|
||||
@@ -218,15 +215,6 @@ static void cryptodev_vhost_user_init(
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -311,12 +299,6 @@ static void cryptodev_vhost_user_cleanup(
|
||||
backend->conf.peers.ccs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/mmap-alloc.h"
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
#include <numaif.h>
|
||||
@@ -263,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
|
||||
return backend->is_mapped;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
|
||||
{
|
||||
Object *obj = OBJECT(memdev);
|
||||
char *path = object_property_get_str(obj, "mem-path", NULL);
|
||||
size_t pagesize = qemu_mempath_getpagesize(path);
|
||||
|
||||
g_free(path);
|
||||
return pagesize;
|
||||
}
|
||||
#else
|
||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
|
||||
{
|
||||
return getpagesize();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
@@ -369,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc)
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_id(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
return g_strdup(backend->id);
|
||||
}
|
||||
|
||||
static void set_id(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
if (backend->id) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
backend->id = g_strdup(str);
|
||||
}
|
||||
|
||||
static bool host_memory_backend_get_share(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
@@ -416,11 +416,18 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
&HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, &error_abort);
|
||||
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
|
||||
object_class_property_add_bool(oc, "share",
|
||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void host_memory_backend_finalize(Object *o)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
g_free(backend->id);
|
||||
}
|
||||
|
||||
static const TypeInfo host_memory_backend_info = {
|
||||
.name = TYPE_MEMORY_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
@@ -429,6 +436,7 @@ static const TypeInfo host_memory_backend_info = {
|
||||
.class_init = host_memory_backend_class_init,
|
||||
.instance_size = sizeof(HostMemoryBackend),
|
||||
.instance_init = host_memory_backend_init,
|
||||
.instance_finalize = host_memory_backend_finalize,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
|
||||
131
block.c
131
block.c
@@ -27,14 +27,12 @@
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/nbd.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "module_block.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qnull.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
@@ -1228,9 +1226,9 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
|
||||
|
||||
ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
|
||||
if (ret < 0) {
|
||||
qobject_unref(bs->explicit_options);
|
||||
QDECREF(bs->explicit_options);
|
||||
bs->explicit_options = NULL;
|
||||
qobject_unref(bs->options);
|
||||
QDECREF(bs->options);
|
||||
bs->options = NULL;
|
||||
bdrv_unref(bs);
|
||||
return NULL;
|
||||
@@ -1459,9 +1457,9 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
options = qobject_to(QDict, options_obj);
|
||||
options = qobject_to_qdict(options_obj);
|
||||
if (!options) {
|
||||
qobject_unref(options_obj);
|
||||
qobject_decref(options_obj);
|
||||
error_setg(errp, "Invalid JSON object given");
|
||||
return NULL;
|
||||
}
|
||||
@@ -1491,7 +1489,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename,
|
||||
/* Options given in the filename have lower priority than options
|
||||
* specified directly */
|
||||
qdict_join(options, json_options, false);
|
||||
qobject_unref(json_options);
|
||||
QDECREF(json_options);
|
||||
*pfilename = NULL;
|
||||
}
|
||||
|
||||
@@ -1621,24 +1619,13 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
|
||||
|
||||
/* Returns whether the image file can be written to after the reopen queue @q
|
||||
* has been successfully applied, or right now if @q is NULL. */
|
||||
static bool bdrv_is_writable_after_reopen(BlockDriverState *bs,
|
||||
BlockReopenQueue *q)
|
||||
static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q)
|
||||
{
|
||||
int flags = bdrv_reopen_get_flags(q, bs);
|
||||
|
||||
return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether the BDS can be written to. This is not necessarily
|
||||
* the same as !bdrv_is_read_only(bs), as inactivated images may not
|
||||
* be written to but do not count as read-only images.
|
||||
*/
|
||||
bool bdrv_is_writable(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_is_writable_after_reopen(bs, NULL);
|
||||
}
|
||||
|
||||
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||
BdrvChild *c, const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
@@ -1676,7 +1663,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
||||
|
||||
/* Write permissions never work with read-only images */
|
||||
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
|
||||
!bdrv_is_writable_after_reopen(bs, q))
|
||||
!bdrv_is_writable(bs, q))
|
||||
{
|
||||
error_setg(errp, "Block node is read-only");
|
||||
return -EPERM;
|
||||
@@ -1968,7 +1955,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
&perm, &shared);
|
||||
|
||||
/* Format drivers may touch metadata even if the guest doesn't write */
|
||||
if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
|
||||
if (bdrv_is_writable(bs, reopen_queue)) {
|
||||
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||
}
|
||||
|
||||
@@ -2285,7 +2272,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
if (reference || qdict_haskey(options, "file.filename")) {
|
||||
backing_filename[0] = '\0';
|
||||
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
goto free_exit;
|
||||
} else {
|
||||
bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX,
|
||||
@@ -2293,7 +2280,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
goto free_exit;
|
||||
}
|
||||
}
|
||||
@@ -2301,7 +2288,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
if (!bs->drv || !bs->drv->supports_backing) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Driver doesn't support backing files");
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
@@ -2335,7 +2322,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
|
||||
free_exit:
|
||||
g_free(backing_filename);
|
||||
qobject_unref(tmp_parent_options);
|
||||
QDECREF(tmp_parent_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2368,7 +2355,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
|
||||
error_setg(errp, "A block device must be specified for \"%s\"",
|
||||
bdref_key);
|
||||
}
|
||||
qobject_unref(image_options);
|
||||
QDECREF(image_options);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -2446,7 +2433,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
}
|
||||
visit_complete(v, &obj);
|
||||
|
||||
qdict = qobject_to(QDict, obj);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
qdict_flatten(qdict);
|
||||
|
||||
/* bdrv_open_inherit() defaults to the values in bdrv_flags (for
|
||||
@@ -2461,7 +2448,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
obj = NULL;
|
||||
|
||||
fail:
|
||||
qobject_unref(obj);
|
||||
qobject_decref(obj);
|
||||
visit_free(v);
|
||||
return bs;
|
||||
}
|
||||
@@ -2531,7 +2518,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
out:
|
||||
qobject_unref(snapshot_options);
|
||||
QDECREF(snapshot_options);
|
||||
g_free(tmp_filename);
|
||||
return bs_snapshot;
|
||||
}
|
||||
@@ -2542,7 +2529,7 @@ out:
|
||||
* options is a QDict of options to pass to the block drivers, or NULL for an
|
||||
* empty set of options. The reference to the QDict belongs to the block layer
|
||||
* after the call (even on failure), so if the caller intends to reuse the
|
||||
* dictionary, it needs to use qobject_ref() before calling bdrv_open.
|
||||
* dictionary, it needs to use QINCREF() before calling bdrv_open.
|
||||
*
|
||||
* If *pbs is NULL, a new BDS will be created with a pointer to it stored there.
|
||||
* If it is not NULL, the referenced BDS will be reused.
|
||||
@@ -2573,7 +2560,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
|
||||
if (reference) {
|
||||
bool options_non_empty = options ? qdict_size(options) : false;
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
|
||||
if (filename || options_non_empty) {
|
||||
error_setg(errp, "Cannot reference an existing block device with "
|
||||
@@ -2658,13 +2645,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
|
||||
/* See cautionary note on accessing @options above */
|
||||
backing = qdict_get_try_str(options, "backing");
|
||||
if (qobject_to(QNull, qdict_get(options, "backing")) != NULL ||
|
||||
(backing && *backing == '\0'))
|
||||
{
|
||||
if (backing) {
|
||||
warn_report("Use of \"backing\": \"\" is deprecated; "
|
||||
"use \"backing\": null instead");
|
||||
}
|
||||
if (backing && *backing == '\0') {
|
||||
flags |= BDRV_O_NO_BACKING;
|
||||
qdict_del(options, "backing");
|
||||
}
|
||||
@@ -2764,7 +2745,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
|
||||
bdrv_parent_cb_change_media(bs, true);
|
||||
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
|
||||
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
|
||||
* temporary snapshot afterwards. */
|
||||
@@ -2788,10 +2769,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
|
||||
fail:
|
||||
blk_unref(file);
|
||||
qobject_unref(snapshot_options);
|
||||
qobject_unref(bs->explicit_options);
|
||||
qobject_unref(bs->options);
|
||||
qobject_unref(options);
|
||||
QDECREF(snapshot_options);
|
||||
QDECREF(bs->explicit_options);
|
||||
QDECREF(bs->options);
|
||||
QDECREF(options);
|
||||
bs->options = NULL;
|
||||
bs->explicit_options = NULL;
|
||||
bdrv_unref(bs);
|
||||
@@ -2800,8 +2781,8 @@ fail:
|
||||
|
||||
close_and_fail:
|
||||
bdrv_unref(bs);
|
||||
qobject_unref(snapshot_options);
|
||||
qobject_unref(options);
|
||||
QDECREF(snapshot_options);
|
||||
QDECREF(options);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
@@ -2896,28 +2877,20 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
old_options = qdict_clone_shallow(bs->explicit_options);
|
||||
}
|
||||
bdrv_join_options(bs, options, old_options);
|
||||
qobject_unref(old_options);
|
||||
QDECREF(old_options);
|
||||
|
||||
explicit_options = qdict_clone_shallow(options);
|
||||
|
||||
/* Inherit from parent node */
|
||||
if (parent_options) {
|
||||
QemuOpts *opts;
|
||||
QDict *options_copy;
|
||||
assert(!flags);
|
||||
role->inherit_options(&flags, options, parent_flags, parent_options);
|
||||
options_copy = qdict_clone_shallow(options);
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options_copy, NULL);
|
||||
update_flags_from_options(&flags, opts);
|
||||
qemu_opts_del(opts);
|
||||
qobject_unref(options_copy);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
QDECREF(old_options);
|
||||
|
||||
/* bdrv_open_inherit() sets and clears some additional flags internally */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
@@ -2929,8 +2902,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
||||
} else {
|
||||
qobject_unref(bs_entry->state.options);
|
||||
qobject_unref(bs_entry->state.explicit_options);
|
||||
QDECREF(bs_entry->state.options);
|
||||
QDECREF(bs_entry->state.explicit_options);
|
||||
}
|
||||
|
||||
bs_entry->state.bs = bs;
|
||||
@@ -3020,9 +2993,9 @@ cleanup:
|
||||
if (ret && bs_entry->prepared) {
|
||||
bdrv_reopen_abort(&bs_entry->state);
|
||||
} else if (ret) {
|
||||
qobject_unref(bs_entry->state.explicit_options);
|
||||
QDECREF(bs_entry->state.explicit_options);
|
||||
}
|
||||
qobject_unref(bs_entry->state.options);
|
||||
QDECREF(bs_entry->state.options);
|
||||
g_free(bs_entry);
|
||||
}
|
||||
g_free(bs_queue);
|
||||
@@ -3265,7 +3238,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
}
|
||||
|
||||
/* set BDS specific flags now */
|
||||
qobject_unref(bs->explicit_options);
|
||||
QDECREF(bs->explicit_options);
|
||||
|
||||
bs->explicit_options = reopen_state->explicit_options;
|
||||
bs->open_flags = reopen_state->flags;
|
||||
@@ -3308,7 +3281,7 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
|
||||
drv->bdrv_reopen_abort(reopen_state);
|
||||
}
|
||||
|
||||
qobject_unref(reopen_state->explicit_options);
|
||||
QDECREF(reopen_state->explicit_options);
|
||||
|
||||
bdrv_abort_perm_update(reopen_state->bs);
|
||||
}
|
||||
@@ -3355,11 +3328,11 @@ static void bdrv_close(BlockDriverState *bs)
|
||||
bs->total_sectors = 0;
|
||||
bs->encrypted = false;
|
||||
bs->sg = false;
|
||||
qobject_unref(bs->options);
|
||||
qobject_unref(bs->explicit_options);
|
||||
QDECREF(bs->options);
|
||||
QDECREF(bs->explicit_options);
|
||||
bs->options = NULL;
|
||||
bs->explicit_options = NULL;
|
||||
qobject_unref(bs->full_open_options);
|
||||
QDECREF(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
|
||||
bdrv_release_named_dirty_bitmaps(bs);
|
||||
@@ -3374,7 +3347,7 @@ static void bdrv_close(BlockDriverState *bs)
|
||||
|
||||
void bdrv_close_all(void)
|
||||
{
|
||||
assert(job_next(NULL) == NULL);
|
||||
block_job_cancel_sync_all();
|
||||
nbd_export_close_all();
|
||||
|
||||
/* Drop references from requests still in flight, such as canceled block
|
||||
@@ -3698,12 +3671,12 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||
GSList *ignore_children = g_slist_prepend(NULL, c);
|
||||
bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
|
||||
ignore_children, &local_err);
|
||||
g_slist_free(ignore_children);
|
||||
if (local_err) {
|
||||
ret = -EPERM;
|
||||
error_report_err(local_err);
|
||||
goto exit;
|
||||
}
|
||||
g_slist_free(ignore_children);
|
||||
|
||||
/* If so, update the backing file path in the image file */
|
||||
if (c->role->update_filename) {
|
||||
@@ -5008,19 +4981,15 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
|
||||
Error **errp)
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
|
||||
{
|
||||
if (!bs->drv) {
|
||||
error_setg(errp, "Node is ejected");
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (!bs->drv->bdrv_amend_options) {
|
||||
error_setg(errp, "Block driver '%s' does not support option amendment",
|
||||
bs->drv->format_name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
|
||||
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque);
|
||||
}
|
||||
|
||||
/* This function will be called by the bdrv_recurse_is_first_non_filter method
|
||||
@@ -5150,8 +5119,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
|
||||
continue;
|
||||
}
|
||||
|
||||
qdict_put_obj(d, qdict_entry_key(entry),
|
||||
qobject_ref(qdict_entry_value(entry)));
|
||||
qobject_incref(qdict_entry_value(entry));
|
||||
qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
|
||||
found_any = true;
|
||||
}
|
||||
|
||||
@@ -5190,21 +5159,21 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
||||
* information before refreshing it */
|
||||
bs->exact_filename[0] = '\0';
|
||||
if (bs->full_open_options) {
|
||||
qobject_unref(bs->full_open_options);
|
||||
QDECREF(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
append_open_options(opts, bs);
|
||||
drv->bdrv_refresh_filename(bs, opts);
|
||||
qobject_unref(opts);
|
||||
QDECREF(opts);
|
||||
} else if (bs->file) {
|
||||
/* Try to reconstruct valid information from the underlying file */
|
||||
bool has_open_options;
|
||||
|
||||
bs->exact_filename[0] = '\0';
|
||||
if (bs->full_open_options) {
|
||||
qobject_unref(bs->full_open_options);
|
||||
QDECREF(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
}
|
||||
|
||||
@@ -5223,12 +5192,12 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
||||
* suffices without querying the (exact_)filename of this BDS. */
|
||||
if (bs->file->bs->full_open_options) {
|
||||
qdict_put_str(opts, "driver", drv->format_name);
|
||||
qdict_put(opts, "file",
|
||||
qobject_ref(bs->file->bs->full_open_options));
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "file", bs->file->bs->full_open_options);
|
||||
|
||||
bs->full_open_options = opts;
|
||||
} else {
|
||||
qobject_unref(opts);
|
||||
QDECREF(opts);
|
||||
}
|
||||
} else if (!bs->full_open_options && qdict_size(bs->options)) {
|
||||
/* There is no underlying file BDS (at least referenced by BDS.file),
|
||||
@@ -5262,7 +5231,7 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
||||
QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
|
||||
snprintf(bs->filename, sizeof(bs->filename), "json:%s",
|
||||
qstring_get_str(json));
|
||||
qobject_unref(json);
|
||||
QDECREF(json);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o
|
||||
block-obj-y += write-threshold.o
|
||||
block-obj-y += backup.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
block-obj-y += throttle.o copy-on-read.o
|
||||
block-obj-y += throttle.o
|
||||
|
||||
block-obj-y += crypto.o
|
||||
|
||||
|
||||
@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
/* block_latency_histogram_compare_func:
|
||||
* Compare @key with interval [@it[0], @it[1]).
|
||||
* Return: -1 if @key < @it[0]
|
||||
* 0 if @key in [@it[0], @it[1])
|
||||
* +1 if @key >= @it[1]
|
||||
*/
|
||||
static int block_latency_histogram_compare_func(const void *key, const void *it)
|
||||
{
|
||||
uint64_t k = *(uint64_t *)key;
|
||||
uint64_t a = ((uint64_t *)it)[0];
|
||||
uint64_t b = ((uint64_t *)it)[1];
|
||||
|
||||
return k < a ? -1 : (k < b ? 0 : 1);
|
||||
}
|
||||
|
||||
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
|
||||
int64_t latency_ns)
|
||||
{
|
||||
uint64_t *pos;
|
||||
|
||||
if (hist->bins == NULL) {
|
||||
/* histogram disabled */
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (latency_ns < hist->boundaries[0]) {
|
||||
hist->bins[0]++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
|
||||
hist->bins[hist->nbins - 1]++;
|
||||
return;
|
||||
}
|
||||
|
||||
pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
|
||||
sizeof(hist->boundaries[0]),
|
||||
block_latency_histogram_compare_func);
|
||||
assert(pos != NULL);
|
||||
|
||||
hist->bins[pos - hist->boundaries + 1]++;
|
||||
}
|
||||
|
||||
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
uint64List *boundaries)
|
||||
{
|
||||
BlockLatencyHistogram *hist = &stats->latency_histogram[type];
|
||||
uint64List *entry;
|
||||
uint64_t *ptr;
|
||||
uint64_t prev = 0;
|
||||
int new_nbins = 1;
|
||||
|
||||
for (entry = boundaries; entry; entry = entry->next) {
|
||||
if (entry->value <= prev) {
|
||||
return -EINVAL;
|
||||
}
|
||||
new_nbins++;
|
||||
prev = entry->value;
|
||||
}
|
||||
|
||||
hist->nbins = new_nbins;
|
||||
g_free(hist->boundaries);
|
||||
hist->boundaries = g_new(uint64_t, hist->nbins - 1);
|
||||
for (entry = boundaries, ptr = hist->boundaries; entry;
|
||||
entry = entry->next, ptr++)
|
||||
{
|
||||
*ptr = entry->value;
|
||||
}
|
||||
|
||||
g_free(hist->bins);
|
||||
hist->bins = g_new0(uint64_t, hist->nbins);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void block_latency_histograms_clear(BlockAcctStats *stats)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
BlockLatencyHistogram *hist = &stats->latency_histogram[i];
|
||||
g_free(hist->bins);
|
||||
g_free(hist->boundaries);
|
||||
memset(hist, 0, sizeof(*hist));
|
||||
}
|
||||
}
|
||||
|
||||
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
bool failed)
|
||||
{
|
||||
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
stats->nr_ops[cookie->type]++;
|
||||
}
|
||||
|
||||
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
|
||||
latency_ns);
|
||||
|
||||
if (!failed || stats->account_failed) {
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
116
block/backup.c
116
block/backup.c
@@ -27,6 +27,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct BackupBlockJob {
|
||||
BlockJob common;
|
||||
@@ -34,10 +35,10 @@ typedef struct BackupBlockJob {
|
||||
/* bitmap for sync=incremental */
|
||||
BdrvDirtyBitmap *sync_bitmap;
|
||||
MirrorSyncMode sync_mode;
|
||||
RateLimit limit;
|
||||
BlockdevOnError on_source_error;
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t len;
|
||||
uint64_t bytes_read;
|
||||
int64_t cluster_size;
|
||||
bool compress;
|
||||
@@ -47,8 +48,6 @@ typedef struct BackupBlockJob {
|
||||
HBitmap *copy_bitmap;
|
||||
} BackupBlockJob;
|
||||
|
||||
static const BlockJobDriver backup_job_driver;
|
||||
|
||||
/* See if in-flight requests overlap and wait for them to complete */
|
||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
@@ -119,7 +118,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
n = MIN(job->cluster_size, job->len - start);
|
||||
n = MIN(job->cluster_size, job->common.len - start);
|
||||
|
||||
if (!bounce_buffer) {
|
||||
bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||
@@ -160,7 +159,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
* offset field is an opaque progress value, it is not a disk offset.
|
||||
*/
|
||||
job->bytes_read += n;
|
||||
job_progress_update(&job->common.job, n);
|
||||
job->common.offset += n;
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -191,12 +190,23 @@ static int coroutine_fn backup_before_write_notify(
|
||||
return backup_do_cow(job, req->offset, req->bytes, NULL, true);
|
||||
}
|
||||
|
||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
@@ -207,25 +217,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_commit(Job *job)
|
||||
static void backup_commit(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_abort(Job *job)
|
||||
static void backup_abort(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_clean(Job *job)
|
||||
static void backup_clean(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
assert(s->target);
|
||||
blk_unref(s->target);
|
||||
s->target = NULL;
|
||||
@@ -243,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t len;
|
||||
|
||||
assert(block_job_driver(job) == &backup_job_driver);
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
|
||||
error_setg(errp, "The backup job only supports block checkpoint in"
|
||||
@@ -251,7 +261,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size);
|
||||
len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
|
||||
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
||||
}
|
||||
|
||||
@@ -261,7 +271,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t start, end;
|
||||
|
||||
assert(block_job_driver(job) == &backup_job_driver);
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
@@ -274,7 +284,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job,
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t start, end;
|
||||
|
||||
assert(block_job_driver(job) == &backup_job_driver);
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
@@ -317,29 +327,33 @@ typedef struct {
|
||||
int ret;
|
||||
} BackupCompleteData;
|
||||
|
||||
static void backup_complete(Job *job, void *opaque)
|
||||
static void backup_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
BackupCompleteData *data = opaque;
|
||||
|
||||
job_completed(job, data->ret, NULL);
|
||||
block_job_completed(job, data->ret);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||
{
|
||||
uint64_t delay_ns;
|
||||
|
||||
if (job_is_cancelled(&job->common.job)) {
|
||||
if (block_job_is_cancelled(&job->common)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
|
||||
* return. Without a yield, the VM would not reboot. */
|
||||
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
job_sleep_ns(&job->common.job, delay_ns);
|
||||
/* we need to yield so that bdrv_drain_all() returns.
|
||||
* (without, VM does not reboot)
|
||||
*/
|
||||
if (job->common.speed) {
|
||||
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
|
||||
job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
block_job_sleep_ns(&job->common, delay_ns);
|
||||
} else {
|
||||
block_job_sleep_ns(&job->common, 0);
|
||||
}
|
||||
|
||||
if (job_is_cancelled(&job->common.job)) {
|
||||
if (block_job_is_cancelled(&job->common)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -406,9 +420,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
|
||||
}
|
||||
|
||||
/* TODO job_progress_set_remaining() would make more sense */
|
||||
job_progress_update(&job->common.job,
|
||||
job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
|
||||
job->common.offset = job->common.len -
|
||||
hbitmap_count(job->copy_bitmap) * job->cluster_size;
|
||||
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
}
|
||||
@@ -424,9 +437,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
|
||||
job_progress_set_remaining(&job->common.job, job->len);
|
||||
|
||||
nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
backup_incremental_init_copy_bitmap(job);
|
||||
@@ -441,16 +452,16 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||
* This does not actually require them to be copied. */
|
||||
while (!job_is_cancelled(&job->common.job)) {
|
||||
while (!block_job_is_cancelled(&job->common)) {
|
||||
/* Yield until the job is cancelled. We just let our before_write
|
||||
* notify callback service CoW requests. */
|
||||
job_yield(&job->common.job);
|
||||
block_job_yield(&job->common);
|
||||
}
|
||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
ret = backup_run_incremental(job);
|
||||
} else {
|
||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||
for (offset = 0; offset < job->len;
|
||||
for (offset = 0; offset < job->common.len;
|
||||
offset += job->cluster_size) {
|
||||
bool error_is_read;
|
||||
int alloced = 0;
|
||||
@@ -519,21 +530,17 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
job_defer_to_main_loop(&job->common.job, backup_complete, data);
|
||||
block_job_defer_to_main_loop(&job->common, backup_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver backup_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = JOB_TYPE_BACKUP,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.start = backup_run,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
},
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.start = backup_run,
|
||||
.set_speed = backup_set_speed,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
.attached_aio_context = backup_attached_aio_context,
|
||||
.drain = backup_drain,
|
||||
};
|
||||
@@ -546,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
BlockdevOnError on_target_error,
|
||||
int creation_flags,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
JobTxn *txn, Error **errp)
|
||||
BlockJobTxn *txn, Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
BlockDriverInfo bdi;
|
||||
@@ -613,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* job->len is fixed, so we can't allow resize */
|
||||
job = block_job_create(job_id, &backup_job_driver, txn, bs,
|
||||
/* job->common.len is fixed, so we can't allow resize */
|
||||
job = block_job_create(job_id, &backup_job_driver, bs,
|
||||
BLK_PERM_CONSISTENT_READ,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
|
||||
@@ -669,7 +676,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
/* Required permissions are already taken with target's blk_new() */
|
||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
job->len = len;
|
||||
job->common.len = len;
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
|
||||
return &job->common;
|
||||
|
||||
@@ -678,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
|
||||
}
|
||||
if (job) {
|
||||
backup_clean(&job->common.job);
|
||||
job_early_fail(&job->common.job);
|
||||
backup_clean(&job->common);
|
||||
block_job_early_fail(&job->common);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
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) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||
bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||
bs->file->bs->supported_zero_flags;
|
||||
ret = -EINVAL;
|
||||
|
||||
/* Set alignment overrides */
|
||||
@@ -846,12 +845,13 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "blkdebug");
|
||||
|
||||
qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "image", bs->file->bs->full_open_options);
|
||||
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||
qdict_put_obj(opts, qdict_entry_key(e),
|
||||
qobject_ref(qdict_entry_value(e)));
|
||||
qobject_incref(qdict_entry_value(e));
|
||||
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,6 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
return ret;
|
||||
|
||||
@@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
@@ -294,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "blkverify");
|
||||
|
||||
qdict_put(opts, "raw",
|
||||
qobject_ref(bs->file->bs->full_open_options));
|
||||
qdict_put(opts, "test",
|
||||
qobject_ref(s->test_file->bs->full_open_options));
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "raw", bs->file->bs->full_open_options);
|
||||
QINCREF(s->test_file->bs->full_open_options);
|
||||
qdict_put(opts, "test", s->test_file->bs->full_open_options);
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
@@ -31,13 +31,6 @@
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
|
||||
|
||||
typedef struct BlockBackendAioNotifier {
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque);
|
||||
void (*detach_aio_context)(void *opaque);
|
||||
void *opaque;
|
||||
QLIST_ENTRY(BlockBackendAioNotifier) list;
|
||||
} BlockBackendAioNotifier;
|
||||
|
||||
struct BlockBackend {
|
||||
char *name;
|
||||
int refcnt;
|
||||
@@ -76,7 +69,6 @@ struct BlockBackend {
|
||||
bool allow_write_beyond_eof;
|
||||
|
||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||
QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
|
||||
|
||||
int quiesce_counter;
|
||||
VMChangeStateEntry *vmsh;
|
||||
@@ -255,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blk_root_attach(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
BlockBackendAioNotifier *notifier;
|
||||
|
||||
trace_blk_root_attach(child, blk, child->bs);
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
bdrv_add_aio_context_notifier(child->bs,
|
||||
notifier->attached_aio_context,
|
||||
notifier->detach_aio_context,
|
||||
notifier->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_root_detach(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
BlockBackendAioNotifier *notifier;
|
||||
|
||||
trace_blk_root_detach(child, blk, child->bs);
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
bdrv_remove_aio_context_notifier(child->bs,
|
||||
notifier->attached_aio_context,
|
||||
notifier->detach_aio_context,
|
||||
notifier->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_root = {
|
||||
.inherit_options = blk_root_inherit_options,
|
||||
|
||||
@@ -298,9 +260,6 @@ static const BdrvChildRole child_root = {
|
||||
|
||||
.activate = blk_root_activate,
|
||||
.inactivate = blk_root_inactivate,
|
||||
|
||||
.attach = blk_root_attach,
|
||||
.detach = blk_root_detach,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -328,7 +287,6 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
notifier_list_init(&blk->insert_bs_notifiers);
|
||||
QLIST_INIT(&blk->aio_notifiers);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
|
||||
return blk;
|
||||
@@ -406,7 +364,6 @@ static void blk_delete(BlockBackend *blk)
|
||||
}
|
||||
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->aio_notifiers));
|
||||
QTAILQ_REMOVE(&block_backends, blk, link);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
@@ -419,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo)
|
||||
return;
|
||||
}
|
||||
qemu_opts_del(dinfo->opts);
|
||||
g_free(dinfo->serial);
|
||||
g_free(dinfo);
|
||||
}
|
||||
|
||||
@@ -1864,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason)
|
||||
|
||||
AioContext *blk_get_aio_context(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_get_aio_context(blk_bs(blk));
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
return bdrv_get_aio_context(bs);
|
||||
} else {
|
||||
return qemu_get_aio_context();
|
||||
}
|
||||
}
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
@@ -1893,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk,
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque),
|
||||
void (*detach_aio_context)(void *opaque), void *opaque)
|
||||
{
|
||||
BlockBackendAioNotifier *notifier;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
notifier = g_new(BlockBackendAioNotifier, 1);
|
||||
notifier->attached_aio_context = attached_aio_context;
|
||||
notifier->detach_aio_context = detach_aio_context;
|
||||
notifier->opaque = opaque;
|
||||
QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list);
|
||||
|
||||
if (bs) {
|
||||
bdrv_add_aio_context_notifier(bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
@@ -1914,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
|
||||
void (*detach_aio_context)(void *),
|
||||
void *opaque)
|
||||
{
|
||||
BlockBackendAioNotifier *notifier;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
bdrv_remove_aio_context_notifier(bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
if (notifier->attached_aio_context == attached_aio_context &&
|
||||
notifier->detach_aio_context == detach_aio_context &&
|
||||
notifier->opaque == opaque) {
|
||||
QLIST_REMOVE(notifier, list);
|
||||
g_free(notifier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
|
||||
@@ -2210,21 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
|
||||
{
|
||||
bdrv_unregister_buf(blk_bs(blk), host);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
|
||||
BlockBackend *blk_out, int64_t off_out,
|
||||
int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int r;
|
||||
r = blk_check_byte_request(blk_in, off_in, bytes);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
r = blk_check_byte_request(blk_out, off_out, bytes);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
return bdrv_co_copy_range(blk_in->root, off_in,
|
||||
blk_out->root, off_out,
|
||||
bytes, flags);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,11 @@ enum {
|
||||
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct CommitBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *commit_top_bs;
|
||||
BlockBackend *top;
|
||||
BlockBackend *base;
|
||||
@@ -72,10 +75,9 @@ typedef struct {
|
||||
int ret;
|
||||
} CommitCompleteData;
|
||||
|
||||
static void commit_complete(Job *job, void *opaque)
|
||||
static void commit_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||
CommitCompleteData *data = opaque;
|
||||
BlockDriverState *top = blk_bs(s->top);
|
||||
BlockDriverState *base = blk_bs(s->base);
|
||||
@@ -91,7 +93,7 @@ static void commit_complete(Job *job, void *opaque)
|
||||
* the normal backing chain can be restored. */
|
||||
blk_unref(s->base);
|
||||
|
||||
if (!job_is_cancelled(job) && ret == 0) {
|
||||
if (!block_job_is_cancelled(&s->common) && ret == 0) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
|
||||
s->backing_file_str);
|
||||
@@ -112,12 +114,12 @@ static void commit_complete(Job *job, void *opaque)
|
||||
blk_unref(s->top);
|
||||
|
||||
/* If there is more than one reference to the job (e.g. if called from
|
||||
* job_finish_sync()), job_completed() won't free it and therefore the
|
||||
* blockers on the intermediate nodes remain. This would cause
|
||||
* bdrv_set_backing_hd() to fail. */
|
||||
block_job_remove_all_bdrv(bjob);
|
||||
* block_job_finish_sync()), block_job_completed() won't free it and
|
||||
* therefore the blockers on the intermediate nodes remain. This would
|
||||
* cause bdrv_set_backing_hd() to fail. */
|
||||
block_job_remove_all_bdrv(job);
|
||||
|
||||
job_completed(job, ret, NULL);
|
||||
block_job_completed(&s->common, ret);
|
||||
g_free(data);
|
||||
|
||||
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
|
||||
@@ -144,21 +146,21 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf = NULL;
|
||||
int bytes_written = 0;
|
||||
int64_t len, base_len;
|
||||
int64_t base_len;
|
||||
|
||||
ret = len = blk_getlength(s->top);
|
||||
if (len < 0) {
|
||||
ret = s->common.len = blk_getlength(s->top);
|
||||
|
||||
if (s->common.len < 0) {
|
||||
goto out;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
ret = base_len = blk_getlength(s->base);
|
||||
if (base_len < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (base_len < len) {
|
||||
ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
|
||||
if (base_len < s->common.len) {
|
||||
ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
@@ -166,14 +168,14 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
|
||||
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
|
||||
|
||||
for (offset = 0; offset < len; offset += n) {
|
||||
for (offset = 0; offset < s->common.len; offset += n) {
|
||||
bool copy;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
job_sleep_ns(&s->common.job, delay_ns);
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
}
|
||||
/* Copy if allocated above the base */
|
||||
@@ -196,12 +198,10 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
}
|
||||
}
|
||||
/* Publish progress */
|
||||
job_progress_update(&s->common.job, n);
|
||||
s->common.offset += n;
|
||||
|
||||
if (copy) {
|
||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,18 +212,25 @@ out:
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
job_defer_to_main_loop(&s->common.job, commit_complete, data);
|
||||
block_job_defer_to_main_loop(&s->common, commit_complete, data);
|
||||
}
|
||||
|
||||
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = JOB_TYPE_COMMIT,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.start = commit_run,
|
||||
},
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = commit_set_speed,
|
||||
.start = commit_run,
|
||||
};
|
||||
|
||||
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
||||
@@ -282,8 +289,8 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
||||
s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
|
||||
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@@ -373,7 +380,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
s->on_error = on_error;
|
||||
|
||||
trace_commit_start(bs, base, top, s);
|
||||
job_start(&s->common.job);
|
||||
block_job_start(&s->common);
|
||||
return;
|
||||
|
||||
fail:
|
||||
@@ -386,7 +393,7 @@ fail:
|
||||
if (commit_top_bs) {
|
||||
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
||||
}
|
||||
job_early_fail(&s->common.job);
|
||||
block_job_early_fail(&s->common);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copy-on-read filter block driver
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Max Reitz <mreitz@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
|
||||
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
||||
errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void cor_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
|
||||
| BLK_PERM_WRITE \
|
||||
| BLK_PERM_RESIZE)
|
||||
#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH)
|
||||
|
||||
static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (c == NULL) {
|
||||
*nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED;
|
||||
*nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
|
||||
return;
|
||||
}
|
||||
|
||||
*nperm = (perm & PERM_PASSTHROUGH) |
|
||||
(c->perm & PERM_UNCHANGED);
|
||||
*nshared = (shared & PERM_PASSTHROUGH) |
|
||||
(c->shared_perm & PERM_UNCHANGED);
|
||||
}
|
||||
|
||||
|
||||
static int64_t cor_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
}
|
||||
|
||||
|
||||
static int cor_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
return bdrv_truncate(bs->file, offset, prealloc, errp);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov,
|
||||
flags | BDRV_REQ_COPY_ON_READ);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn cor_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
|
||||
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
}
|
||||
|
||||
|
||||
static void cor_eject(BlockDriverState *bs, bool eject_flag)
|
||||
{
|
||||
bdrv_eject(bs->file->bs, eject_flag);
|
||||
}
|
||||
|
||||
|
||||
static void cor_lock_medium(BlockDriverState *bs, bool locked)
|
||||
{
|
||||
bdrv_lock_medium(bs->file->bs, locked);
|
||||
}
|
||||
|
||||
|
||||
static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
{
|
||||
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
|
||||
}
|
||||
|
||||
|
||||
BlockDriver bdrv_copy_on_read = {
|
||||
.format_name = "copy-on-read",
|
||||
|
||||
.bdrv_open = cor_open,
|
||||
.bdrv_close = cor_close,
|
||||
.bdrv_child_perm = cor_child_perm,
|
||||
|
||||
.bdrv_getlength = cor_getlength,
|
||||
.bdrv_truncate = cor_truncate,
|
||||
|
||||
.bdrv_co_preadv = cor_co_preadv,
|
||||
.bdrv_co_pwritev = cor_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = cor_co_pdiscard,
|
||||
|
||||
.bdrv_eject = cor_eject,
|
||||
.bdrv_lock_medium = cor_lock_medium,
|
||||
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
||||
|
||||
.bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter,
|
||||
|
||||
.has_variable_length = true,
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
static void bdrv_copy_on_read_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_copy_on_read);
|
||||
}
|
||||
|
||||
block_init(bdrv_copy_on_read_init);
|
||||
@@ -24,51 +24,28 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/job.h"
|
||||
#include "qapi/qapi-commands-block-core.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
typedef struct BlockdevCreateJob {
|
||||
Job common;
|
||||
typedef struct BlockdevCreateCo {
|
||||
BlockDriver *drv;
|
||||
BlockdevCreateOptions *opts;
|
||||
int ret;
|
||||
Error *err;
|
||||
} BlockdevCreateJob;
|
||||
Error **errp;
|
||||
} BlockdevCreateCo;
|
||||
|
||||
static void blockdev_create_complete(Job *job, void *opaque)
|
||||
static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
|
||||
{
|
||||
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
|
||||
|
||||
job_completed(job, s->ret, s->err);
|
||||
BlockdevCreateCo *cco = opaque;
|
||||
cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
|
||||
}
|
||||
|
||||
static void coroutine_fn blockdev_create_run(void *opaque)
|
||||
void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
BlockdevCreateJob *s = opaque;
|
||||
|
||||
job_progress_set_remaining(&s->common, 1);
|
||||
s->ret = s->drv->bdrv_co_create(s->opts, &s->err);
|
||||
job_progress_update(&s->common, 1);
|
||||
|
||||
qapi_free_BlockdevCreateOptions(s->opts);
|
||||
job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL);
|
||||
}
|
||||
|
||||
static const JobDriver blockdev_create_job_driver = {
|
||||
.instance_size = sizeof(BlockdevCreateJob),
|
||||
.job_type = JOB_TYPE_CREATE,
|
||||
.start = blockdev_create_run,
|
||||
};
|
||||
|
||||
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateJob *s;
|
||||
const char *fmt = BlockdevDriver_str(options->driver);
|
||||
BlockDriver *drv = bdrv_find_format(fmt);
|
||||
Coroutine *co;
|
||||
BlockdevCreateCo cco;
|
||||
|
||||
/* If the driver is in the schema, we know that it exists. But it may not
|
||||
* be whitelisted. */
|
||||
@@ -78,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Error out if the driver doesn't support .bdrv_co_create */
|
||||
/* Call callback if it exists */
|
||||
if (!drv->bdrv_co_create) {
|
||||
error_setg(errp, "Driver does not support blockdev-create");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the block job */
|
||||
/* TODO Running in the main context. Block drivers need to error out or add
|
||||
* locking when they use a BDS in a different AioContext. */
|
||||
s = job_create(job_id, &blockdev_create_job_driver, NULL,
|
||||
qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
|
||||
NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
cco = (BlockdevCreateCo) {
|
||||
.drv = drv,
|
||||
.opts = options,
|
||||
.ret = -EINPROGRESS,
|
||||
.errp = errp,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (cco.ret == -EINPROGRESS) {
|
||||
aio_poll(qemu_get_aio_context(), true);
|
||||
}
|
||||
|
||||
s->drv = drv,
|
||||
s->opts = QAPI_CLONE(BlockdevCreateOptions, options),
|
||||
|
||||
job_start(&s->common);
|
||||
}
|
||||
|
||||
172
block/crypto.c
172
block/crypto.c
@@ -21,15 +21,15 @@
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "crypto/block.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "qapi/qapi-visit-crypto.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/option.h"
|
||||
#include "crypto.h"
|
||||
#include "block/crypto.h"
|
||||
|
||||
typedef struct BlockCrypto BlockCrypto;
|
||||
|
||||
@@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
||||
|
||||
|
||||
struct BlockCryptoCreateData {
|
||||
const char *filename;
|
||||
QemuOpts *opts;
|
||||
BlockBackend *blk;
|
||||
uint64_t size;
|
||||
};
|
||||
@@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
||||
Error **errp)
|
||||
{
|
||||
struct BlockCryptoCreateData *data = opaque;
|
||||
|
||||
if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) {
|
||||
error_setg(errp, "The requested file size is too large");
|
||||
return -EFBIG;
|
||||
}
|
||||
int ret;
|
||||
|
||||
/* User provided size should reflect amount of space made
|
||||
* available to the guest, so we must take account of that
|
||||
* which will be used by the crypto header
|
||||
*/
|
||||
return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
|
||||
errp);
|
||||
data->size += headerlen;
|
||||
|
||||
qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort);
|
||||
ret = bdrv_create_file(data->filename, data->opts, errp);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data->blk = blk_new_open(data->filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
|
||||
if (!data->blk) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,10 +170,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
|
||||
ret = g_new0(QCryptoBlockOpenOptions, 1);
|
||||
ret->format = format;
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(opts, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
|
||||
|
||||
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
||||
if (local_err) {
|
||||
@@ -213,10 +221,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
|
||||
ret = g_new0(QCryptoBlockCreateOptions, 1);
|
||||
ret->format = format;
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(opts, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
|
||||
|
||||
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
||||
if (local_err) {
|
||||
@@ -311,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
qobject_unref(cryptoopts);
|
||||
QDECREF(cryptoopts);
|
||||
qapi_free_QCryptoBlockOpenOptions(open_opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int block_crypto_co_create_generic(BlockDriverState *bs,
|
||||
int64_t size,
|
||||
QCryptoBlockCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int block_crypto_create_generic(QCryptoBlockFormat format,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockBackend *blk;
|
||||
int ret = -EINVAL;
|
||||
QCryptoBlockCreateOptions *create_opts = NULL;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
struct BlockCryptoCreateData data;
|
||||
struct BlockCryptoCreateData data = {
|
||||
.size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE),
|
||||
.opts = opts,
|
||||
.filename = filename,
|
||||
};
|
||||
QDict *cryptoopts;
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
cryptoopts = qemu_opts_to_qdict(opts, NULL);
|
||||
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto cleanup;
|
||||
create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
|
||||
if (!create_opts) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (struct BlockCryptoCreateData) {
|
||||
.blk = blk,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
crypto = qcrypto_block_create(opts, NULL,
|
||||
crypto = qcrypto_block_create(create_opts, NULL,
|
||||
block_crypto_init_func,
|
||||
block_crypto_write_func,
|
||||
&data,
|
||||
@@ -352,8 +358,10 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
QDECREF(cryptoopts);
|
||||
qcrypto_block_free(crypto);
|
||||
blk_unref(blk);
|
||||
blk_unref(data.blk);
|
||||
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -363,11 +371,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
|
||||
BlockCrypto *crypto = bs->opaque;
|
||||
uint64_t payload_offset =
|
||||
qcrypto_block_get_payload_offset(crypto->block);
|
||||
|
||||
if (payload_offset > INT64_MAX - offset) {
|
||||
error_setg(errp, "The requested file size is too large");
|
||||
return -EFBIG;
|
||||
}
|
||||
assert(payload_offset < (INT64_MAX - offset));
|
||||
|
||||
offset += payload_offset;
|
||||
|
||||
@@ -533,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
|
||||
|
||||
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
|
||||
assert(offset < INT64_MAX);
|
||||
|
||||
if (offset > len) {
|
||||
return -EIO;
|
||||
}
|
||||
assert(offset < len);
|
||||
|
||||
len -= offset;
|
||||
|
||||
@@ -561,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs,
|
||||
bs, options, flags, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsLUKS *luks_opts;
|
||||
BlockDriverState *bs = NULL;
|
||||
QCryptoBlockCreateOptions create_opts;
|
||||
int ret;
|
||||
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
|
||||
luks_opts = &create_options->u.luks;
|
||||
|
||||
bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
create_opts = (QCryptoBlockCreateOptions) {
|
||||
.format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
.u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts),
|
||||
};
|
||||
|
||||
ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoBlockCreateOptions *create_opts = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *cryptoopts;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
/* Parse options */
|
||||
size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
|
||||
cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
|
||||
&block_crypto_create_opts_luks,
|
||||
true);
|
||||
|
||||
create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
cryptoopts, errp);
|
||||
if (!create_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create protocol layer */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (!bs) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create format layer */
|
||||
ret = block_crypto_co_create_generic(bs, size, create_opts, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||
qobject_unref(cryptoopts);
|
||||
return ret;
|
||||
return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
filename, opts, errp);
|
||||
}
|
||||
|
||||
static int block_crypto_get_info_luks(BlockDriverState *bs,
|
||||
@@ -698,7 +623,6 @@ BlockDriver bdrv_crypto_luks = {
|
||||
.bdrv_open = block_crypto_open_luks,
|
||||
.bdrv_close = block_crypto_close,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = block_crypto_co_create_luks,
|
||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||
.bdrv_truncate = block_crypto_truncate,
|
||||
.create_opts = &block_crypto_create_opts_luks,
|
||||
|
||||
@@ -40,8 +40,6 @@ struct BdrvDirtyBitmap {
|
||||
QemuMutex *mutex;
|
||||
HBitmap *bitmap; /* Dirty bitmap implementation */
|
||||
HBitmap *meta; /* Meta dirty bitmap */
|
||||
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 */
|
||||
@@ -97,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
g_free(bitmap->name);
|
||||
bitmap->name = NULL;
|
||||
bitmap->persistent = false;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
@@ -176,18 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
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)
|
||||
{
|
||||
@@ -199,8 +194,6 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||
} 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;
|
||||
} else {
|
||||
@@ -241,26 +234,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bdrv_enable_dirty_bitmap(bitmap->successor);
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */
|
||||
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bitmap->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
assert(!bitmap->meta);
|
||||
QLIST_REMOVE(bitmap, list);
|
||||
hbitmap_free(bitmap->bitmap);
|
||||
g_free(bitmap->name);
|
||||
g_free(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a bitmap with a successor, yield our name to the successor,
|
||||
* delete the old bitmap, and return a handle to the new bitmap.
|
||||
@@ -294,11 +267,11 @@ 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 un-frozen, but not explicitly re-enabled.
|
||||
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *successor = parent->successor;
|
||||
|
||||
@@ -311,26 +284,12 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
bdrv_release_dirty_bitmap_locked(successor);
|
||||
bdrv_release_dirty_bitmap(bs, successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *ret;
|
||||
|
||||
qemu_mutex_lock(parent->mutex);
|
||||
ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp);
|
||||
qemu_mutex_unlock(parent->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates _all_ bitmaps attached to a BDS.
|
||||
* Called with BQL taken.
|
||||
@@ -349,12 +308,45 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !!bdrv_dirty_bitmap_name(bitmap);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
static void bdrv_do_release_matching_dirty_bitmap(
|
||||
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
bool (*cond)(BdrvDirtyBitmap *bitmap))
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
|
||||
assert(!bm->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
assert(!bm->meta);
|
||||
QLIST_REMOVE(bm, list);
|
||||
hbitmap_free(bm->bitmap);
|
||||
g_free(bm->name);
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bitmap) {
|
||||
abort();
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
bdrv_release_dirty_bitmap_locked(bitmap);
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,15 +357,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
*/
|
||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if (bdrv_dirty_bitmap_name(bm)) {
|
||||
bdrv_release_dirty_bitmap_locked(bm);
|
||||
}
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,19 +365,11 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
* bdrv_inactivate_recurse()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
* This function does not remove persistent bitmaps from the storage.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if (bdrv_dirty_bitmap_get_persistance(bm)) {
|
||||
bdrv_release_dirty_bitmap_locked(bm);
|
||||
}
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL,
|
||||
bdrv_dirty_bitmap_get_persistance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,20 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
@@ -728,21 +702,3 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
|
||||
{
|
||||
return hbitmap_next_zero(bitmap->bitmap, offset);
|
||||
}
|
||||
|
||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||
Error **errp)
|
||||
{
|
||||
/* only bitmaps from one bds are supported */
|
||||
assert(dest->mutex == src->mutex);
|
||||
|
||||
qemu_mutex_lock(dest->mutex);
|
||||
|
||||
assert(bdrv_dirty_bitmap_enabled(dest));
|
||||
assert(!bdrv_dirty_bitmap_readonly(dest));
|
||||
|
||||
if (!hbitmap_merge(dest->bitmap, src->bitmap)) {
|
||||
error_setg(errp, "Bitmaps are incompatible and can't be merged");
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(dest->mutex);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <linux/fd.h>
|
||||
#include <linux/fs.h>
|
||||
@@ -162,7 +161,6 @@ typedef struct BDRVRawState {
|
||||
bool page_cache_inconsistent:1;
|
||||
bool has_fallocate;
|
||||
bool needs_alignment;
|
||||
bool check_cache_dropped;
|
||||
|
||||
PRManager *pr_mgr;
|
||||
} BDRVRawState;
|
||||
@@ -170,7 +168,6 @@ typedef struct BDRVRawState {
|
||||
typedef struct BDRVRawReopenState {
|
||||
int fd;
|
||||
int open_flags;
|
||||
bool check_cache_dropped;
|
||||
} BDRVRawReopenState;
|
||||
|
||||
static int fd_open(BlockDriverState *bs);
|
||||
@@ -188,8 +185,6 @@ typedef struct RawPosixAIOData {
|
||||
#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
|
||||
off_t aio_offset;
|
||||
int aio_type;
|
||||
int aio_fd2;
|
||||
off_t aio_offset2;
|
||||
} RawPosixAIOData;
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
@@ -420,11 +415,6 @@ static QemuOptsList raw_runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "id of persistent reservation manager object (default: none)",
|
||||
},
|
||||
{
|
||||
.name = "x-check-cache-dropped",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "check that page cache was dropped on live migration (default: off)"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -510,9 +500,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -643,7 +630,7 @@ typedef enum {
|
||||
* file; if @unlock == true, also unlock the unneeded bytes.
|
||||
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
|
||||
*/
|
||||
static int raw_apply_lock_bytes(int fd,
|
||||
static int raw_apply_lock_bytes(BDRVRawState *s,
|
||||
uint64_t perm_lock_bits,
|
||||
uint64_t shared_perm_lock_bits,
|
||||
bool unlock, Error **errp)
|
||||
@@ -654,13 +641,13 @@ static int raw_apply_lock_bytes(int fd,
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_PERM_BASE + i;
|
||||
if (perm_lock_bits & (1ULL << i)) {
|
||||
ret = qemu_lock_fd(fd, off, 1, false);
|
||||
ret = qemu_lock_fd(s->lock_fd, off, 1, false);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to lock byte %d", off);
|
||||
return ret;
|
||||
}
|
||||
} else if (unlock) {
|
||||
ret = qemu_unlock_fd(fd, off, 1);
|
||||
ret = qemu_unlock_fd(s->lock_fd, off, 1);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to unlock byte %d", off);
|
||||
return ret;
|
||||
@@ -670,13 +657,13 @@ static int raw_apply_lock_bytes(int fd,
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_SHARED_BASE + i;
|
||||
if (shared_perm_lock_bits & (1ULL << i)) {
|
||||
ret = qemu_lock_fd(fd, off, 1, false);
|
||||
ret = qemu_lock_fd(s->lock_fd, off, 1, false);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to lock byte %d", off);
|
||||
return ret;
|
||||
}
|
||||
} else if (unlock) {
|
||||
ret = qemu_unlock_fd(fd, off, 1);
|
||||
ret = qemu_unlock_fd(s->lock_fd, off, 1);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to unlock byte %d", off);
|
||||
return ret;
|
||||
@@ -687,7 +674,8 @@ static int raw_apply_lock_bytes(int fd,
|
||||
}
|
||||
|
||||
/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */
|
||||
static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
|
||||
static int raw_check_lock_bytes(BDRVRawState *s,
|
||||
uint64_t perm, uint64_t shared_perm,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
@@ -697,7 +685,7 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
|
||||
int off = RAW_LOCK_SHARED_BASE + i;
|
||||
uint64_t p = 1ULL << i;
|
||||
if (perm & p) {
|
||||
ret = qemu_lock_fd_test(fd, off, 1, true);
|
||||
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
|
||||
if (ret) {
|
||||
char *perm_name = bdrv_perm_names(p);
|
||||
error_setg(errp,
|
||||
@@ -714,7 +702,7 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
|
||||
int off = RAW_LOCK_PERM_BASE + i;
|
||||
uint64_t p = 1ULL << i;
|
||||
if (!(shared_perm & p)) {
|
||||
ret = qemu_lock_fd_test(fd, off, 1, true);
|
||||
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
|
||||
if (ret) {
|
||||
char *perm_name = bdrv_perm_names(p);
|
||||
error_setg(errp,
|
||||
@@ -751,11 +739,11 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
|
||||
|
||||
switch (op) {
|
||||
case RAW_PL_PREPARE:
|
||||
ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
|
||||
ret = raw_apply_lock_bytes(s, s->perm | new_perm,
|
||||
~s->shared_perm | ~new_shared,
|
||||
false, errp);
|
||||
if (!ret) {
|
||||
ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp);
|
||||
ret = raw_check_lock_bytes(s, new_perm, new_shared, errp);
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
@@ -763,8 +751,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
|
||||
op = RAW_PL_ABORT;
|
||||
/* fall through to unlock bytes. */
|
||||
case RAW_PL_ABORT:
|
||||
raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
|
||||
true, &local_err);
|
||||
raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err);
|
||||
if (local_err) {
|
||||
/* Theoretically the above call only unlocks bytes and it cannot
|
||||
* fail. Something weird happened, report it.
|
||||
@@ -773,8 +760,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
|
||||
}
|
||||
break;
|
||||
case RAW_PL_COMMIT:
|
||||
raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
|
||||
true, &local_err);
|
||||
raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err);
|
||||
if (local_err) {
|
||||
/* Theoretically the above call only unlocks bytes and it cannot
|
||||
* fail. Something weird happened, report it.
|
||||
@@ -791,7 +777,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
{
|
||||
BDRVRawState *s;
|
||||
BDRVRawReopenState *rs;
|
||||
QemuOpts *opts;
|
||||
int ret = 0;
|
||||
Error *local_err = NULL;
|
||||
|
||||
@@ -802,19 +787,6 @@ 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);
|
||||
qemu_opts_absorb_qdict(opts, state->options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rs->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped",
|
||||
s->check_cache_dropped);
|
||||
|
||||
if (s->type == FTYPE_CD) {
|
||||
rs->open_flags |= O_NONBLOCK;
|
||||
@@ -822,6 +794,8 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
|
||||
raw_parse_flags(state->flags, &rs->open_flags);
|
||||
|
||||
rs->fd = -1;
|
||||
|
||||
int fcntl_flags = O_APPEND | O_NONBLOCK;
|
||||
#ifdef O_NOATIME
|
||||
fcntl_flags |= O_NOATIME;
|
||||
@@ -876,8 +850,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -886,7 +858,6 @@ static void raw_reopen_commit(BDRVReopenState *state)
|
||||
BDRVRawReopenState *rs = state->opaque;
|
||||
BDRVRawState *s = state->bs->opaque;
|
||||
|
||||
s->check_cache_dropped = rs->check_cache_dropped;
|
||||
s->open_flags = rs->open_flags;
|
||||
|
||||
qemu_close(s->fd);
|
||||
@@ -1450,49 +1421,6 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
#ifndef HAVE_COPY_FILE_RANGE
|
||||
static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd,
|
||||
off_t *out_off, size_t len, unsigned int flags)
|
||||
{
|
||||
#ifdef __NR_copy_file_range
|
||||
return syscall(__NR_copy_file_range, in_fd, in_off, out_fd,
|
||||
out_off, len, flags);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb)
|
||||
{
|
||||
uint64_t bytes = aiocb->aio_nbytes;
|
||||
off_t in_off = aiocb->aio_offset;
|
||||
off_t out_off = aiocb->aio_offset2;
|
||||
|
||||
while (bytes) {
|
||||
ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off,
|
||||
aiocb->aio_fd2, &out_off,
|
||||
bytes, 0);
|
||||
if (ret == -EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (errno == ENOSYS) {
|
||||
return -ENOTSUP;
|
||||
} else {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
/* No progress (e.g. when beyond EOF), fall back to buffer I/O. */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
bytes -= ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
@@ -1573,9 +1501,6 @@ static int aio_worker(void *arg)
|
||||
case QEMU_AIO_WRITE_ZEROES:
|
||||
ret = handle_aiocb_write_zeroes(aiocb);
|
||||
break;
|
||||
case QEMU_AIO_COPY_RANGE:
|
||||
ret = handle_aiocb_copy_range(aiocb);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||
ret = -EINVAL;
|
||||
@@ -1586,10 +1511,9 @@ static int aio_worker(void *arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int paio_submit_co_full(BlockDriverState *bs, int fd,
|
||||
int64_t offset, int fd2, int64_t offset2,
|
||||
QEMUIOVector *qiov,
|
||||
int bytes, int type)
|
||||
static int paio_submit_co(BlockDriverState *bs, int fd,
|
||||
int64_t offset, QEMUIOVector *qiov,
|
||||
int bytes, int type)
|
||||
{
|
||||
RawPosixAIOData *acb = g_new(RawPosixAIOData, 1);
|
||||
ThreadPool *pool;
|
||||
@@ -1597,8 +1521,6 @@ static int paio_submit_co_full(BlockDriverState *bs, int fd,
|
||||
acb->bs = bs;
|
||||
acb->aio_type = type;
|
||||
acb->aio_fildes = fd;
|
||||
acb->aio_fd2 = fd2;
|
||||
acb->aio_offset2 = offset2;
|
||||
|
||||
acb->aio_nbytes = bytes;
|
||||
acb->aio_offset = offset;
|
||||
@@ -1614,13 +1536,6 @@ static int paio_submit_co_full(BlockDriverState *bs, int fd,
|
||||
return thread_pool_submit_co(pool, aio_worker, acb);
|
||||
}
|
||||
|
||||
static inline int paio_submit_co(BlockDriverState *bs, int fd,
|
||||
int64_t offset, QEMUIOVector *qiov,
|
||||
int bytes, int type)
|
||||
{
|
||||
return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type);
|
||||
}
|
||||
|
||||
static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
int64_t offset, QEMUIOVector *qiov, int bytes,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
@@ -1786,7 +1701,6 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
|
||||
case PREALLOC_MODE_FULL:
|
||||
{
|
||||
int64_t num = 0, left = offset - current_length;
|
||||
off_t seek_result;
|
||||
|
||||
/*
|
||||
* Knowing the final size from the beginning could allow the file
|
||||
@@ -1801,8 +1715,8 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
|
||||
|
||||
buf = g_malloc0(65536);
|
||||
|
||||
seek_result = lseek(fd, current_length, SEEK_SET);
|
||||
if (seek_result < 0) {
|
||||
result = lseek(fd, current_length, SEEK_SET);
|
||||
if (result < 0) {
|
||||
result = -errno;
|
||||
error_setg_errno(errp, -result,
|
||||
"Failed to seek to the old end of file");
|
||||
@@ -2076,7 +1990,6 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsFile *file_opts;
|
||||
int fd;
|
||||
int perm, shared;
|
||||
int result = 0;
|
||||
|
||||
/* Validate options and set default values */
|
||||
@@ -2091,44 +2004,14 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
}
|
||||
|
||||
/* Create file */
|
||||
fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644);
|
||||
fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
error_setg_errno(errp, -result, "Could not create file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Take permissions: We want to discard everything, so we need
|
||||
* BLK_PERM_WRITE; and truncation to the desired size requires
|
||||
* BLK_PERM_RESIZE.
|
||||
* On the other hand, we cannot share the RESIZE permission
|
||||
* because we promise that after this function, the file has the
|
||||
* size given in the options. If someone else were to resize it
|
||||
* concurrently, we could not guarantee that.
|
||||
* Note that after this function, we can no longer guarantee that
|
||||
* the file is not touched by a third party, so it may be resized
|
||||
* then. */
|
||||
perm = BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
|
||||
|
||||
/* Step one: Take locks */
|
||||
result = raw_apply_lock_bytes(fd, perm, shared, false, errp);
|
||||
if (result < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Step two: Check that nobody else has taken conflicting locks */
|
||||
result = raw_check_lock_bytes(fd, perm, shared, errp);
|
||||
if (result < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Clear the file by truncating it to 0 */
|
||||
result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp);
|
||||
if (result < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (file_opts->nocow) {
|
||||
#ifdef __linux__
|
||||
/* Set NOCOW flag to solve performance issue on fs like btrfs.
|
||||
@@ -2144,8 +2027,6 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Resize and potentially preallocate the file to the desired
|
||||
* final size */
|
||||
result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
|
||||
errp);
|
||||
if (result < 0) {
|
||||
@@ -2233,12 +2114,7 @@ static int find_allocation(BlockDriverState *bs, off_t start,
|
||||
if (offs < 0) {
|
||||
return -errno; /* D3 or D4 */
|
||||
}
|
||||
|
||||
if (offs < start) {
|
||||
/* This is not a valid return by lseek(). We are safe to just return
|
||||
* -EIO in this case, and we'll treat it like D4. */
|
||||
return -EIO;
|
||||
}
|
||||
assert(offs >= start);
|
||||
|
||||
if (offs > start) {
|
||||
/* D2: in hole, next data at offs */
|
||||
@@ -2270,12 +2146,7 @@ static int find_allocation(BlockDriverState *bs, off_t start,
|
||||
if (offs < 0) {
|
||||
return -errno; /* D1 and (H3 or H4) */
|
||||
}
|
||||
|
||||
if (offs < start) {
|
||||
/* This is not a valid return by lseek(). We are safe to just return
|
||||
* -EIO in this case, and we'll treat it like H4. */
|
||||
return -EIO;
|
||||
}
|
||||
assert(offs >= start);
|
||||
|
||||
if (offs > start) {
|
||||
/*
|
||||
@@ -2354,120 +2225,6 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
|
||||
return ret | BDRV_BLOCK_OFFSET_VALID;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
/* Verify that the file is not in the page cache */
|
||||
static void check_cache_dropped(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
const size_t window_size = 128 * 1024 * 1024;
|
||||
BDRVRawState *s = bs->opaque;
|
||||
void *window = NULL;
|
||||
size_t length = 0;
|
||||
unsigned char *vec;
|
||||
size_t page_size;
|
||||
off_t offset;
|
||||
off_t end;
|
||||
|
||||
/* mincore(2) page status information requires 1 byte per page */
|
||||
page_size = sysconf(_SC_PAGESIZE);
|
||||
vec = g_malloc(DIV_ROUND_UP(window_size, page_size));
|
||||
|
||||
end = raw_getlength(bs);
|
||||
|
||||
for (offset = 0; offset < end; offset += window_size) {
|
||||
void *new_window;
|
||||
size_t new_length;
|
||||
size_t vec_end;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
/* Unmap previous window if size has changed */
|
||||
new_length = MIN(end - offset, window_size);
|
||||
if (new_length != length) {
|
||||
munmap(window, length);
|
||||
window = NULL;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
new_window = mmap(window, new_length, PROT_NONE, MAP_PRIVATE,
|
||||
s->fd, offset);
|
||||
if (new_window == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno, "mmap failed");
|
||||
break;
|
||||
}
|
||||
|
||||
window = new_window;
|
||||
length = new_length;
|
||||
|
||||
ret = mincore(window, length, vec);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, errno, "mincore failed");
|
||||
break;
|
||||
}
|
||||
|
||||
vec_end = DIV_ROUND_UP(length, page_size);
|
||||
for (i = 0; i < vec_end; i++) {
|
||||
if (vec[i] & 0x1) {
|
||||
error_setg(errp, "page cache still in use!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (window) {
|
||||
munmap(window, length);
|
||||
}
|
||||
|
||||
g_free(vec);
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = fd_open(bs);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "The file descriptor is not open");
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->open_flags & O_DIRECT) {
|
||||
return; /* No host kernel page cache */
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
/* This sets the scene for the next syscall... */
|
||||
ret = bdrv_co_flush(bs);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "flush failed");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Linux does not invalidate pages that are dirty, locked, or mmapped by a
|
||||
* process. These limitations are okay because we just fsynced the file,
|
||||
* we don't use mmap, and the file should not be in use by other processes.
|
||||
*/
|
||||
ret = posix_fadvise(s->fd, 0, 0, POSIX_FADV_DONTNEED);
|
||||
if (ret != 0) { /* the return value is a positive errno */
|
||||
error_setg_errno(errp, ret, "fadvise failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->check_cache_dropped) {
|
||||
check_cache_dropped(bs, errp);
|
||||
}
|
||||
#else /* __linux__ */
|
||||
/* Do nothing. Live migration to a remote host with cache.direct=off is
|
||||
* unsupported on other host operating systems. Cache consistency issues
|
||||
* may occur but no error is reported here, partly because that's the
|
||||
* historical behavior and partly because it's hard to differentiate valid
|
||||
* configurations that should not cause errors.
|
||||
*/
|
||||
#endif /* !__linux__ */
|
||||
}
|
||||
|
||||
static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
@@ -2544,35 +2301,6 @@ static void raw_abort_perm_update(BlockDriverState *bs)
|
||||
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs,
|
||||
BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
|
||||
BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
BDRVRawState *src_s;
|
||||
|
||||
assert(dst->bs == bs);
|
||||
if (src->bs->drv->bdrv_co_copy_range_to != raw_co_copy_range_to) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
src_s = src->bs->opaque;
|
||||
if (fd_open(bs) < 0 || fd_open(bs) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset,
|
||||
NULL, bytes, QEMU_AIO_COPY_RANGE);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_file = {
|
||||
.format_name = "file",
|
||||
.protocol_name = "file",
|
||||
@@ -2589,15 +2317,12 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_co_create_opts = raw_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = raw_co_block_status,
|
||||
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
|
||||
.bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
|
||||
|
||||
.bdrv_co_preadv = raw_co_preadv,
|
||||
.bdrv_co_pwritev = raw_co_pwritev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_aio_pdiscard = raw_aio_pdiscard,
|
||||
.bdrv_co_copy_range_from = raw_co_copy_range_from,
|
||||
.bdrv_co_copy_range_to = raw_co_copy_range_to,
|
||||
.bdrv_refresh_limits = raw_refresh_limits,
|
||||
.bdrv_io_plug = raw_aio_plug,
|
||||
.bdrv_io_unplug = raw_aio_unplug,
|
||||
@@ -3069,15 +2794,12 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_co_create_opts = hdev_co_create_opts,
|
||||
.create_opts = &raw_create_opts,
|
||||
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
|
||||
.bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
|
||||
|
||||
.bdrv_co_preadv = raw_co_preadv,
|
||||
.bdrv_co_pwritev = raw_co_pwritev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_aio_pdiscard = hdev_aio_pdiscard,
|
||||
.bdrv_co_copy_range_from = raw_co_copy_range_from,
|
||||
.bdrv_co_copy_range_to = raw_co_copy_range_to,
|
||||
.bdrv_refresh_limits = raw_refresh_limits,
|
||||
.bdrv_io_plug = raw_aio_plug,
|
||||
.bdrv_io_unplug = raw_aio_unplug,
|
||||
@@ -3194,7 +2916,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,
|
||||
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
|
||||
|
||||
|
||||
.bdrv_co_preadv = raw_co_preadv,
|
||||
|
||||
@@ -251,11 +251,7 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp)
|
||||
&dg.Geometry.BytesPerSector,
|
||||
&freeClusters, &totalClusters);
|
||||
bs->bl.request_alignment = dg.Geometry.BytesPerSector;
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX Does Windows support AIO on less than 512-byte alignment? */
|
||||
bs->bl.request_alignment = 512;
|
||||
}
|
||||
|
||||
static void raw_parse_flags(int flags, bool use_aio, int *access_flags,
|
||||
@@ -414,32 +410,32 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
|
||||
cb, opaque, QEMU_AIO_READ);
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||
nb_sectors, cb, opaque, QEMU_AIO_READ);
|
||||
} else {
|
||||
return paio_submit(bs, s->hfile, offset, qiov, bytes,
|
||||
return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
nb_sectors << BDRV_SECTOR_BITS,
|
||||
cb, opaque, QEMU_AIO_READ);
|
||||
}
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
|
||||
cb, opaque, QEMU_AIO_WRITE);
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||
nb_sectors, cb, opaque, QEMU_AIO_WRITE);
|
||||
} else {
|
||||
return paio_submit(bs, s->hfile, offset, qiov, bytes,
|
||||
return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
nb_sectors << BDRV_SECTOR_BITS,
|
||||
cb, opaque, QEMU_AIO_WRITE);
|
||||
}
|
||||
}
|
||||
@@ -636,8 +632,8 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_co_create_opts = raw_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
.bdrv_aio_preadv = raw_aio_preadv,
|
||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
@@ -712,12 +708,6 @@ static void hdev_parse_filename(const char *filename, QDict *options,
|
||||
bdrv_parse_filename_strip_prefix(filename, "host_device:", options);
|
||||
}
|
||||
|
||||
static void hdev_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
/* XXX Does Windows support AIO on less than 512-byte alignment? */
|
||||
bs->bl.request_alignment = 512;
|
||||
}
|
||||
|
||||
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_probe_device = hdev_probe_device,
|
||||
.bdrv_file_open = hdev_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_refresh_limits = hdev_refresh_limits,
|
||||
|
||||
.bdrv_aio_preadv = raw_aio_preadv,
|
||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
|
||||
.bdrv_detach_aio_context = raw_detach_aio_context,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include <glusterfs/api/glfs.h>
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -168,12 +167,7 @@ static QemuOptsList runtime_unix_opts = {
|
||||
{
|
||||
.name = GLUSTER_OPT_SOCKET,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "socket file path (legacy)",
|
||||
},
|
||||
{
|
||||
.name = GLUSTER_OPT_PATH,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "socket file path (QAPI)",
|
||||
.help = "socket file path)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
@@ -621,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH);
|
||||
if (!ptr) {
|
||||
ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET);
|
||||
} else if (qemu_opt_get(opts, GLUSTER_OPT_SOCKET)) {
|
||||
error_setg(&local_err,
|
||||
"Conflicting parameters 'path' and 'socket'");
|
||||
error_append_hint(&local_err, GERR_INDEX_HINT, i);
|
||||
goto out;
|
||||
}
|
||||
ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET);
|
||||
if (!ptr) {
|
||||
error_setg(&local_err, QERR_MISSING_PARAMETER,
|
||||
GLUSTER_OPT_PATH);
|
||||
GLUSTER_OPT_SOCKET);
|
||||
error_append_hint(&local_err, GERR_INDEX_HINT, i);
|
||||
goto out;
|
||||
}
|
||||
@@ -651,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
||||
}
|
||||
gsconf = NULL;
|
||||
|
||||
qobject_unref(backing_options);
|
||||
QDECREF(backing_options);
|
||||
backing_options = NULL;
|
||||
g_free(str);
|
||||
str = NULL;
|
||||
@@ -664,7 +650,7 @@ out:
|
||||
qapi_free_SocketAddress(gsconf);
|
||||
qemu_opts_del(opts);
|
||||
g_free(str);
|
||||
qobject_unref(backing_options);
|
||||
QDECREF(backing_options);
|
||||
errno = EINVAL;
|
||||
return -errno;
|
||||
}
|
||||
@@ -679,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
|
||||
if (filename) {
|
||||
ret = qemu_gluster_parse_uri(gconf, filename);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "invalid URI %s", filename);
|
||||
error_setg(errp, "invalid URI");
|
||||
error_append_hint(errp, "Usage: file=gluster[+transport]://"
|
||||
"[host[:port]]volume/path[?socket=...]"
|
||||
"[,file.debug=N]"
|
||||
@@ -698,7 +684,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
|
||||
"file.server.0.host=1.2.3.4,"
|
||||
"file.server.0.port=24007,"
|
||||
"file.server.1.transport=unix,"
|
||||
"file.server.1.path=/var/run/glusterd.socket ..."
|
||||
"file.server.1.socket=/var/run/glusterd.socket ..."
|
||||
"\n");
|
||||
return ret;
|
||||
}
|
||||
@@ -1195,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
|
||||
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
QEMUIOVector *qiov,
|
||||
int flags)
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
assert(!flags);
|
||||
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
|
||||
}
|
||||
|
||||
|
||||
185
block/io.c
185
block/io.c
@@ -92,8 +92,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
|
||||
/* Default alignment based on whether driver has byte interface */
|
||||
bs->bl.request_alignment = (drv->bdrv_co_preadv ||
|
||||
drv->bdrv_aio_preadv) ? 1 : 512;
|
||||
bs->bl.request_alignment = drv->bdrv_co_preadv ? 1 : 512;
|
||||
|
||||
/* Take some limits from the children as a default */
|
||||
if (bs->file) {
|
||||
@@ -250,7 +249,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
BdrvCoDrainData data;
|
||||
|
||||
/* Calling bdrv_drain() from a BH ensures the current coroutine yields and
|
||||
* other coroutines run if they were queued by aio_co_enter(). */
|
||||
* other coroutines run if they were queued from
|
||||
* qemu_co_queue_run_restart(). */
|
||||
|
||||
assert(qemu_in_coroutine());
|
||||
data = (BdrvCoDrainData) {
|
||||
@@ -925,14 +925,23 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
|
||||
return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
if (drv->bdrv_aio_preadv) {
|
||||
sector_num = offset >> BDRV_SECTOR_BITS;
|
||||
nb_sectors = bytes >> BDRV_SECTOR_BITS;
|
||||
|
||||
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS);
|
||||
|
||||
if (drv->bdrv_co_readv) {
|
||||
return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
|
||||
} else {
|
||||
BlockAIOCB *acb;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
|
||||
acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
acb = bs->drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
if (acb == NULL) {
|
||||
return -EIO;
|
||||
} else {
|
||||
@@ -940,16 +949,6 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
|
||||
return co.ret;
|
||||
}
|
||||
}
|
||||
|
||||
sector_num = offset >> BDRV_SECTOR_BITS;
|
||||
nb_sectors = bytes >> BDRV_SECTOR_BITS;
|
||||
|
||||
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS);
|
||||
assert(drv->bdrv_co_readv);
|
||||
|
||||
return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
|
||||
@@ -974,25 +973,6 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
|
||||
goto emulate_flags;
|
||||
}
|
||||
|
||||
if (drv->bdrv_aio_pwritev) {
|
||||
BlockAIOCB *acb;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
|
||||
acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov,
|
||||
flags & bs->supported_write_flags,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
flags &= ~bs->supported_write_flags;
|
||||
if (acb == NULL) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
qemu_coroutine_yield();
|
||||
ret = co.ret;
|
||||
}
|
||||
goto emulate_flags;
|
||||
}
|
||||
|
||||
sector_num = offset >> BDRV_SECTOR_BITS;
|
||||
nb_sectors = bytes >> BDRV_SECTOR_BITS;
|
||||
|
||||
@@ -1000,10 +980,28 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
|
||||
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS);
|
||||
|
||||
assert(drv->bdrv_co_writev);
|
||||
ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov,
|
||||
flags & bs->supported_write_flags);
|
||||
flags &= ~bs->supported_write_flags;
|
||||
if (drv->bdrv_co_writev_flags) {
|
||||
ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov,
|
||||
flags & bs->supported_write_flags);
|
||||
flags &= ~bs->supported_write_flags;
|
||||
} else if (drv->bdrv_co_writev) {
|
||||
assert(!bs->supported_write_flags);
|
||||
ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
|
||||
} else {
|
||||
BlockAIOCB *acb;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
|
||||
acb = bs->drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
if (acb == NULL) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
qemu_coroutine_yield();
|
||||
ret = co.ret;
|
||||
}
|
||||
}
|
||||
|
||||
emulate_flags:
|
||||
if (ret == 0 && (flags & BDRV_REQ_FUA)) {
|
||||
@@ -1118,15 +1116,13 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
|
||||
/* FIXME: Should we (perhaps conditionally) be setting
|
||||
* BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy
|
||||
* that still correctly reads as zero? */
|
||||
ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum,
|
||||
BDRV_REQ_WRITE_UNCHANGED);
|
||||
ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, 0);
|
||||
} else {
|
||||
/* This does not change the data on the disk, it is not
|
||||
* necessary to flush even in cache=writethrough mode.
|
||||
*/
|
||||
ret = bdrv_driver_pwritev(bs, cluster_offset, pnum,
|
||||
&local_qiov,
|
||||
BDRV_REQ_WRITE_UNCHANGED);
|
||||
&local_qiov, 0);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -1506,11 +1502,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
|
||||
assert(!waited || !req->serialising);
|
||||
assert(req->overlap_offset <= offset);
|
||||
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
|
||||
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
|
||||
assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE));
|
||||
} else {
|
||||
assert(child->perm & BLK_PERM_WRITE);
|
||||
}
|
||||
assert(child->perm & BLK_PERM_WRITE);
|
||||
assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE);
|
||||
|
||||
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
||||
@@ -2835,100 +2827,3 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host)
|
||||
bdrv_unregister_buf(child->bs, host);
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
|
||||
uint64_t src_offset,
|
||||
BdrvChild *dst,
|
||||
uint64_t dst_offset,
|
||||
uint64_t bytes,
|
||||
BdrvRequestFlags flags,
|
||||
bool recurse_src)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!src || !dst || !src->bs || !dst->bs) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
ret = bdrv_check_byte_request(src->bs, src_offset, bytes);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
if (flags & BDRV_REQ_ZERO_WRITE) {
|
||||
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags);
|
||||
}
|
||||
|
||||
if (!src->bs->drv->bdrv_co_copy_range_from
|
||||
|| !dst->bs->drv->bdrv_co_copy_range_to
|
||||
|| src->bs->encrypted || dst->bs->encrypted) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (recurse_src) {
|
||||
return src->bs->drv->bdrv_co_copy_range_from(src->bs,
|
||||
src, src_offset,
|
||||
dst, dst_offset,
|
||||
bytes, flags);
|
||||
} else {
|
||||
return dst->bs->drv->bdrv_co_copy_range_to(dst->bs,
|
||||
src, src_offset,
|
||||
dst, dst_offset,
|
||||
bytes, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy range from @src to @dst.
|
||||
*
|
||||
* See the comment of bdrv_co_copy_range for the parameter and return value
|
||||
* semantics. */
|
||||
int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset,
|
||||
bytes, flags, true);
|
||||
}
|
||||
|
||||
/* Copy range from @src to @dst.
|
||||
*
|
||||
* See the comment of bdrv_co_copy_range for the parameter and return value
|
||||
* semantics. */
|
||||
int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset,
|
||||
bytes, flags, false);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BdrvTrackedRequest src_req, dst_req;
|
||||
BlockDriverState *src_bs = src->bs;
|
||||
BlockDriverState *dst_bs = dst->bs;
|
||||
int ret;
|
||||
|
||||
bdrv_inc_in_flight(src_bs);
|
||||
bdrv_inc_in_flight(dst_bs);
|
||||
tracked_request_begin(&src_req, src_bs, src_offset,
|
||||
bytes, BDRV_TRACKED_READ);
|
||||
tracked_request_begin(&dst_req, dst_bs, dst_offset,
|
||||
bytes, BDRV_TRACKED_WRITE);
|
||||
|
||||
wait_serialising_requests(&src_req);
|
||||
wait_serialising_requests(&dst_req);
|
||||
ret = bdrv_co_copy_range_from(src, src_offset,
|
||||
dst, dst_offset,
|
||||
bytes, flags);
|
||||
|
||||
tracked_request_end(&src_req);
|
||||
tracked_request_end(&dst_req);
|
||||
bdrv_dec_in_flight(src_bs);
|
||||
bdrv_dec_in_flight(dst_bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
350
block/iscsi.c
350
block/iscsi.c
@@ -33,7 +33,6 @@
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "scsi/constants.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -69,7 +68,6 @@ typedef struct IscsiLun {
|
||||
QemuMutex mutex;
|
||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||
struct scsi_inquiry_block_limits bl;
|
||||
struct scsi_inquiry_device_designator *dd;
|
||||
unsigned char *zeroblock;
|
||||
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
||||
* allocated and which are not. In case a target returns zeros for
|
||||
@@ -557,20 +555,9 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
|
||||
offset / iscsilun->cluster_size) == size);
|
||||
}
|
||||
|
||||
static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask,
|
||||
IscsiLun *iscsilun)
|
||||
{
|
||||
while (!iTask->complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *iov, int flags)
|
||||
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *iov, int flags)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct IscsiTask iTask;
|
||||
@@ -629,7 +616,12 @@ retry:
|
||||
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
||||
iov->niov);
|
||||
#endif
|
||||
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
@@ -700,7 +692,13 @@ retry:
|
||||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
if (iTask.do_retry) {
|
||||
if (iTask.task != NULL) {
|
||||
@@ -864,8 +862,13 @@ retry:
|
||||
#if LIBISCSI_API_VERSION < (20160603)
|
||||
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
||||
#endif
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
iTask.task = NULL;
|
||||
@@ -902,7 +905,12 @@ retry:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
@@ -1134,7 +1142,12 @@ retry:
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
@@ -1230,7 +1243,12 @@ retry:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||
while (!iTask.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
qemu_coroutine_yield();
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
||||
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
||||
@@ -1714,34 +1732,14 @@ static QemuOptsList runtime_opts = {
|
||||
.name = "timeout",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static void iscsi_save_designator(IscsiLun *lun,
|
||||
struct scsi_inquiry_device_identification *inq_di)
|
||||
{
|
||||
struct scsi_inquiry_device_designator *desig, *copy = NULL;
|
||||
|
||||
for (desig = inq_di->designators; desig; desig = desig->next) {
|
||||
if (desig->association ||
|
||||
desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) {
|
||||
continue;
|
||||
}
|
||||
/* NAA works better than T10 vendor ID based designator. */
|
||||
if (!copy || copy->designator_type < desig->designator_type) {
|
||||
copy = desig;
|
||||
}
|
||||
}
|
||||
if (copy) {
|
||||
lun->dd = g_new(struct scsi_inquiry_device_designator, 1);
|
||||
*lun->dd = *copy;
|
||||
lun->dd->next = NULL;
|
||||
lun->dd->designator = g_malloc(copy->designator_length);
|
||||
memcpy(lun->dd->designator, copy->designator, copy->designator_length);
|
||||
}
|
||||
}
|
||||
|
||||
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -1753,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
char *initiator_name = NULL;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *transport_name, *portal, *target;
|
||||
const char *transport_name, *portal, *target, *filename;
|
||||
#if LIBISCSI_API_VERSION >= (20160603)
|
||||
enum iscsi_transport_type transport;
|
||||
#endif
|
||||
int i, ret = 0, timeout = 0, lun;
|
||||
|
||||
/* If we are given a filename, parse the filename, with precedence given to
|
||||
* filename encoded options */
|
||||
filename = qdict_get_try_str(options, "filename");
|
||||
if (filename) {
|
||||
warn_report("'filename' option specified. "
|
||||
"This is an unsupported option, and may be deprecated "
|
||||
"in the future");
|
||||
iscsi_parse_filename(filename, options, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
@@ -1909,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
struct scsi_task *inq_task;
|
||||
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
||||
struct scsi_inquiry_block_limits *inq_bl;
|
||||
struct scsi_inquiry_device_identification *inq_di;
|
||||
switch (inq_vpd->pages[i]) {
|
||||
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
||||
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||
@@ -1935,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
sizeof(struct scsi_inquiry_block_limits));
|
||||
scsi_free_scsi_task(inq_task);
|
||||
break;
|
||||
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
||||
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||
SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION,
|
||||
(void **) &inq_di, errp);
|
||||
if (inq_task == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
iscsi_save_designator(iscsilun, inq_di);
|
||||
scsi_free_scsi_task(inq_task);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1988,7 +1989,7 @@ out:
|
||||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2002,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs)
|
||||
iscsi_logout_sync(iscsi);
|
||||
}
|
||||
iscsi_destroy_context(iscsi);
|
||||
if (iscsilun->dd) {
|
||||
g_free(iscsilun->dd->designator);
|
||||
g_free(iscsilun->dd);
|
||||
}
|
||||
g_free(iscsilun->zeroblock);
|
||||
iscsi_allocmap_free(iscsilun);
|
||||
qemu_mutex_destroy(&iscsilun->mutex);
|
||||
@@ -2146,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt
|
||||
} else {
|
||||
ret = iscsi_open(bs, bs_options, 0, NULL);
|
||||
}
|
||||
qobject_unref(bs_options);
|
||||
QDECREF(bs_options);
|
||||
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
@@ -2187,221 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
|
||||
iscsi_allocmap_invalidate(iscsilun);
|
||||
}
|
||||
|
||||
static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs,
|
||||
BdrvChild *src,
|
||||
uint64_t src_offset,
|
||||
BdrvChild *dst,
|
||||
uint64_t dst_offset,
|
||||
uint64_t bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags);
|
||||
}
|
||||
|
||||
static struct scsi_task *iscsi_xcopy_task(int param_len)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = g_new0(struct scsi_task, 1);
|
||||
|
||||
task->cdb[0] = EXTENDED_COPY;
|
||||
task->cdb[10] = (param_len >> 24) & 0xFF;
|
||||
task->cdb[11] = (param_len >> 16) & 0xFF;
|
||||
task->cdb[12] = (param_len >> 8) & 0xFF;
|
||||
task->cdb[13] = param_len & 0xFF;
|
||||
task->cdb_size = 16;
|
||||
task->xfer_dir = SCSI_XFER_WRITE;
|
||||
task->expxferlen = param_len;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun)
|
||||
{
|
||||
struct scsi_inquiry_device_designator *dd = lun->dd;
|
||||
|
||||
memset(desc, 0, 32);
|
||||
desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */
|
||||
desc[4] = dd->code_set;
|
||||
desc[5] = (dd->designator_type & 0xF)
|
||||
| ((dd->association & 3) << 4);
|
||||
desc[7] = dd->designator_length;
|
||||
memcpy(desc + 8, dd->designator, dd->designator_length);
|
||||
|
||||
desc[28] = 0;
|
||||
desc[29] = (lun->block_size >> 16) & 0xFF;
|
||||
desc[30] = (lun->block_size >> 8) & 0xFF;
|
||||
desc[31] = lun->block_size & 0xFF;
|
||||
}
|
||||
|
||||
static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index,
|
||||
int dst_index)
|
||||
{
|
||||
hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */
|
||||
hdr[1] = ((dc << 1) | cat) & 0xFF;
|
||||
hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF;
|
||||
/* don't account for the first 4 bytes in descriptor header*/
|
||||
hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF;
|
||||
hdr[4] = (src_index >> 8) & 0xFF;
|
||||
hdr[5] = src_index & 0xFF;
|
||||
hdr[6] = (dst_index >> 8) & 0xFF;
|
||||
hdr[7] = dst_index & 0xFF;
|
||||
}
|
||||
|
||||
static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat,
|
||||
int src_index, int dst_index, int num_blks,
|
||||
uint64_t src_lba, uint64_t dst_lba)
|
||||
{
|
||||
iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index);
|
||||
|
||||
/* The caller should verify the request size */
|
||||
assert(num_blks < 65536);
|
||||
desc[10] = (num_blks >> 8) & 0xFF;
|
||||
desc[11] = num_blks & 0xFF;
|
||||
desc[12] = (src_lba >> 56) & 0xFF;
|
||||
desc[13] = (src_lba >> 48) & 0xFF;
|
||||
desc[14] = (src_lba >> 40) & 0xFF;
|
||||
desc[15] = (src_lba >> 32) & 0xFF;
|
||||
desc[16] = (src_lba >> 24) & 0xFF;
|
||||
desc[17] = (src_lba >> 16) & 0xFF;
|
||||
desc[18] = (src_lba >> 8) & 0xFF;
|
||||
desc[19] = src_lba & 0xFF;
|
||||
desc[20] = (dst_lba >> 56) & 0xFF;
|
||||
desc[21] = (dst_lba >> 48) & 0xFF;
|
||||
desc[22] = (dst_lba >> 40) & 0xFF;
|
||||
desc[23] = (dst_lba >> 32) & 0xFF;
|
||||
desc[24] = (dst_lba >> 24) & 0xFF;
|
||||
desc[25] = (dst_lba >> 16) & 0xFF;
|
||||
desc[26] = (dst_lba >> 8) & 0xFF;
|
||||
desc[27] = dst_lba & 0xFF;
|
||||
}
|
||||
|
||||
static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str,
|
||||
int list_id_usage, int prio,
|
||||
int tgt_desc_len,
|
||||
int seg_desc_len, int inline_data_len)
|
||||
{
|
||||
buf[0] = list_id;
|
||||
buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
|
||||
buf[2] = (tgt_desc_len >> 8) & 0xFF;
|
||||
buf[3] = tgt_desc_len & 0xFF;
|
||||
buf[8] = (seg_desc_len >> 24) & 0xFF;
|
||||
buf[9] = (seg_desc_len >> 16) & 0xFF;
|
||||
buf[10] = (seg_desc_len >> 8) & 0xFF;
|
||||
buf[11] = seg_desc_len & 0xFF;
|
||||
buf[12] = (inline_data_len >> 24) & 0xFF;
|
||||
buf[13] = (inline_data_len >> 16) & 0xFF;
|
||||
buf[14] = (inline_data_len >> 8) & 0xFF;
|
||||
buf[15] = inline_data_len & 0xFF;
|
||||
}
|
||||
|
||||
static void iscsi_xcopy_data(struct iscsi_data *data,
|
||||
IscsiLun *src, int64_t src_lba,
|
||||
IscsiLun *dst, int64_t dst_lba,
|
||||
uint16_t num_blocks)
|
||||
{
|
||||
uint8_t *buf;
|
||||
const int src_offset = XCOPY_DESC_OFFSET;
|
||||
const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE;
|
||||
const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE;
|
||||
|
||||
data->size = XCOPY_DESC_OFFSET +
|
||||
IDENT_DESCR_TGT_DESCR_SIZE * 2 +
|
||||
XCOPY_BLK2BLK_SEG_DESC_SIZE;
|
||||
data->data = g_malloc0(data->size);
|
||||
buf = data->data;
|
||||
|
||||
/* Initialise the parameter list header */
|
||||
iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */,
|
||||
0, 2 * IDENT_DESCR_TGT_DESCR_SIZE,
|
||||
XCOPY_BLK2BLK_SEG_DESC_SIZE,
|
||||
0);
|
||||
|
||||
/* Initialise CSCD list with one src + one dst descriptor */
|
||||
iscsi_populate_target_desc(&buf[src_offset], src);
|
||||
iscsi_populate_target_desc(&buf[dst_offset], dst);
|
||||
|
||||
/* Initialise one segment descriptor */
|
||||
iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks,
|
||||
src_lba, dst_lba);
|
||||
}
|
||||
|
||||
static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs,
|
||||
BdrvChild *src,
|
||||
uint64_t src_offset,
|
||||
BdrvChild *dst,
|
||||
uint64_t dst_offset,
|
||||
uint64_t bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
IscsiLun *dst_lun = dst->bs->opaque;
|
||||
IscsiLun *src_lun;
|
||||
struct IscsiTask iscsi_task;
|
||||
struct iscsi_data data;
|
||||
int r = 0;
|
||||
int block_size;
|
||||
|
||||
if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
src_lun = src->bs->opaque;
|
||||
|
||||
if (!src_lun->dd || !dst_lun->dd) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (dst_lun->block_size != src_lun->block_size ||
|
||||
!dst_lun->block_size) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
block_size = dst_lun->block_size;
|
||||
if (bytes / block_size > 65535) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
iscsi_xcopy_data(&data,
|
||||
src_lun, src_offset / block_size,
|
||||
dst_lun, dst_offset / block_size,
|
||||
bytes / block_size);
|
||||
|
||||
iscsi_co_init_iscsitask(dst_lun, &iscsi_task);
|
||||
|
||||
qemu_mutex_lock(&dst_lun->mutex);
|
||||
iscsi_task.task = iscsi_xcopy_task(data.size);
|
||||
retry:
|
||||
if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun,
|
||||
iscsi_task.task, iscsi_co_generic_cb,
|
||||
&data,
|
||||
&iscsi_task) != 0) {
|
||||
r = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
iscsi_co_wait_for_task(&iscsi_task, dst_lun);
|
||||
|
||||
if (iscsi_task.do_retry) {
|
||||
iscsi_task.complete = 0;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (iscsi_task.status != SCSI_STATUS_GOOD) {
|
||||
r = iscsi_task.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
g_free(iscsi_task.task);
|
||||
qemu_mutex_unlock(&dst_lun->mutex);
|
||||
g_free(iscsi_task.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
static QemuOptsList iscsi_create_opts = {
|
||||
.name = "iscsi-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
||||
@@ -2436,11 +2218,9 @@ static BlockDriver bdrv_iscsi = {
|
||||
|
||||
.bdrv_co_block_status = iscsi_co_block_status,
|
||||
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
||||
.bdrv_co_copy_range_from = iscsi_co_copy_range_from,
|
||||
.bdrv_co_copy_range_to = iscsi_co_copy_range_to,
|
||||
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
||||
.bdrv_co_readv = iscsi_co_readv,
|
||||
.bdrv_co_writev = iscsi_co_writev,
|
||||
.bdrv_co_writev_flags = iscsi_co_writev_flags,
|
||||
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -2464,7 +2244,7 @@ static BlockDriver bdrv_iser = {
|
||||
.create_opts = &iscsi_create_opts,
|
||||
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
||||
.bdrv_reopen_commit = iscsi_reopen_commit,
|
||||
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
|
||||
.bdrv_invalidate_cache = iscsi_invalidate_cache,
|
||||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_get_info = iscsi_get_info,
|
||||
@@ -2473,11 +2253,9 @@ static BlockDriver bdrv_iser = {
|
||||
|
||||
.bdrv_co_block_status = iscsi_co_block_status,
|
||||
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
||||
.bdrv_co_copy_range_from = iscsi_co_copy_range_from,
|
||||
.bdrv_co_copy_range_to = iscsi_co_copy_range_to,
|
||||
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
||||
.bdrv_co_readv = iscsi_co_readv,
|
||||
.bdrv_co_writev = iscsi_co_writev,
|
||||
.bdrv_co_writev_flags = iscsi_co_writev_flags,
|
||||
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
163
block/mirror.c
163
block/mirror.c
@@ -22,6 +22,7 @@
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "qemu/bitmap.h"
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
#define MAX_IN_FLIGHT 16
|
||||
#define MAX_IO_BYTES (1 << 20) /* 1 Mb */
|
||||
#define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES)
|
||||
@@ -35,6 +36,7 @@ typedef struct MirrorBuffer {
|
||||
|
||||
typedef struct MirrorBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockBackend *target;
|
||||
BlockDriverState *mirror_top_bs;
|
||||
BlockDriverState *source;
|
||||
@@ -119,14 +121,14 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||
}
|
||||
if (!s->initial_zeroing_ongoing) {
|
||||
job_progress_update(&s->common.job, op->bytes);
|
||||
s->common.offset += op->bytes;
|
||||
}
|
||||
}
|
||||
qemu_iovec_destroy(&op->qiov);
|
||||
g_free(op);
|
||||
|
||||
if (s->waiting_for_io) {
|
||||
qemu_coroutine_enter(s->common.job.co);
|
||||
qemu_coroutine_enter(s->common.co);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +347,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
mirror_wait_for_io(s);
|
||||
}
|
||||
|
||||
job_pause_point(&s->common.job);
|
||||
block_job_pause_point(&s->common);
|
||||
|
||||
/* Find the number of consective dirty chunks following the first dirty
|
||||
* one, and wait for in flight requests in them. */
|
||||
@@ -447,7 +449,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
assert(io_bytes);
|
||||
offset += io_bytes;
|
||||
nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity);
|
||||
delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct);
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, io_bytes_acct);
|
||||
}
|
||||
}
|
||||
return delay_ns;
|
||||
}
|
||||
@@ -484,10 +488,9 @@ typedef struct {
|
||||
int ret;
|
||||
} MirrorExitData;
|
||||
|
||||
static void mirror_exit(Job *job, void *opaque)
|
||||
static void mirror_exit(BlockJob *job, void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
MirrorExitData *data = opaque;
|
||||
AioContext *replace_aio_context = NULL;
|
||||
BlockDriverState *src = s->source;
|
||||
@@ -498,7 +501,7 @@ static void mirror_exit(Job *job, void *opaque)
|
||||
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
||||
|
||||
/* Make sure that the source BDS doesn't go away before we called
|
||||
* job_completed(). */
|
||||
* block_job_completed(). */
|
||||
bdrv_ref(src);
|
||||
bdrv_ref(mirror_top_bs);
|
||||
bdrv_ref(target_bs);
|
||||
@@ -569,7 +572,7 @@ static void mirror_exit(Job *job, void *opaque)
|
||||
* the blockers on the intermediate nodes so that the resulting state is
|
||||
* valid. Also give up permissions on mirror_top_bs->backing, which might
|
||||
* block the removal. */
|
||||
block_job_remove_all_bdrv(bjob);
|
||||
block_job_remove_all_bdrv(job);
|
||||
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
|
||||
@@ -577,11 +580,11 @@ static void mirror_exit(Job *job, void *opaque)
|
||||
/* We just changed the BDS the job BB refers to (with either or both of the
|
||||
* bdrv_replace_node() calls), so switch the BB back so the cleanup does
|
||||
* the right thing. We don't need any permissions any more now. */
|
||||
blk_remove_bs(bjob->blk);
|
||||
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
|
||||
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
|
||||
blk_remove_bs(job->blk);
|
||||
blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
|
||||
blk_insert_bs(job->blk, mirror_top_bs, &error_abort);
|
||||
|
||||
job_completed(job, data->ret, NULL);
|
||||
block_job_completed(&s->common, data->ret);
|
||||
|
||||
g_free(data);
|
||||
bdrv_drained_end(src);
|
||||
@@ -593,11 +596,11 @@ static void mirror_throttle(MirrorBlockJob *s)
|
||||
{
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
|
||||
if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) {
|
||||
if (now - s->last_pause_ns > SLICE_TIME) {
|
||||
s->last_pause_ns = now;
|
||||
job_sleep_ns(&s->common.job, 0);
|
||||
block_job_sleep_ns(&s->common, 0);
|
||||
} else {
|
||||
job_pause_point(&s->common.job);
|
||||
block_job_pause_point(&s->common);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,7 +626,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
||||
|
||||
mirror_throttle(s);
|
||||
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
s->initial_zeroing_ongoing = false;
|
||||
return 0;
|
||||
}
|
||||
@@ -651,7 +654,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
||||
|
||||
mirror_throttle(s);
|
||||
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -696,7 +699,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
checking for a NULL string */
|
||||
int ret = 0;
|
||||
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
@@ -727,13 +730,13 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
|
||||
if (s->bdev_length == 0) {
|
||||
/* Transition to the READY state and wait for complete. */
|
||||
job_transition_to_ready(&s->common.job);
|
||||
/* Report BLOCK_JOB_READY and wait for complete. */
|
||||
block_job_event_ready(&s->common);
|
||||
s->synced = true;
|
||||
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
|
||||
job_yield(&s->common.job);
|
||||
while (!block_job_is_cancelled(&s->common) && !s->should_complete) {
|
||||
block_job_yield(&s->common);
|
||||
}
|
||||
s->common.job.cancelled = false;
|
||||
s->common.cancelled = false;
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
@@ -769,7 +772,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
if (!s->is_none_mode) {
|
||||
ret = mirror_dirty_init(s);
|
||||
if (ret < 0 || job_is_cancelled(&s->common.job)) {
|
||||
if (ret < 0 || block_job_is_cancelled(&s->common)) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
}
|
||||
@@ -786,20 +789,22 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
job_pause_point(&s->common.job);
|
||||
block_job_pause_point(&s->common);
|
||||
|
||||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
|
||||
* the number of bytes currently being processed; together those are
|
||||
* the current remaining operation length */
|
||||
job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt);
|
||||
/* s->common.offset contains the number of bytes already processed so
|
||||
* far, cnt is the number of dirty bytes remaining and
|
||||
* s->bytes_in_flight is the number of bytes currently being
|
||||
* processed; together those are the current total operation length */
|
||||
s->common.len = s->common.offset + s->bytes_in_flight + cnt;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* periodically with no pending I/O so that bdrv_drain_all() returns.
|
||||
* We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is
|
||||
* an error, or when the source is clean, whichever comes first. */
|
||||
* We do so every SLICE_TIME nanoseconds, or when there is an error,
|
||||
* or when the source is clean, whichever comes first.
|
||||
*/
|
||||
delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns;
|
||||
if (delta < BLOCK_JOB_SLICE_TIME &&
|
||||
if (delta < SLICE_TIME &&
|
||||
s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||
(cnt == 0 && s->in_flight > 0)) {
|
||||
@@ -824,12 +829,12 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
* report completion. This way, block-job-cancel will leave
|
||||
* the target in a consistent state.
|
||||
*/
|
||||
job_transition_to_ready(&s->common.job);
|
||||
block_job_event_ready(&s->common);
|
||||
s->synced = true;
|
||||
}
|
||||
|
||||
should_complete = s->should_complete ||
|
||||
job_is_cancelled(&s->common.job);
|
||||
block_job_is_cancelled(&s->common);
|
||||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||
}
|
||||
|
||||
@@ -857,23 +862,21 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
* completion.
|
||||
*/
|
||||
assert(QLIST_EMPTY(&bs->tracked_requests));
|
||||
s->common.job.cancelled = false;
|
||||
s->common.cancelled = false;
|
||||
need_drain = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (s->synced && !should_complete) {
|
||||
delay_ns = (s->in_flight == 0 &&
|
||||
cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0);
|
||||
}
|
||||
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
|
||||
job_sleep_ns(&s->common.job, delay_ns);
|
||||
if (job_is_cancelled(&s->common.job) &&
|
||||
(!s->synced || s->common.job.force_cancel))
|
||||
{
|
||||
break;
|
||||
if (!s->synced) {
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
}
|
||||
} else if (!should_complete) {
|
||||
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
}
|
||||
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
}
|
||||
@@ -884,8 +887,7 @@ immediate_exit:
|
||||
* or it was cancelled prematurely so that we do not guarantee that
|
||||
* the target is a copy of the source.
|
||||
*/
|
||||
assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) &&
|
||||
job_is_cancelled(&s->common.job)));
|
||||
assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
|
||||
assert(need_drain);
|
||||
mirror_wait_for_all_io(s);
|
||||
}
|
||||
@@ -902,12 +904,23 @@ immediate_exit:
|
||||
if (need_drain) {
|
||||
bdrv_drained_begin(bs);
|
||||
}
|
||||
job_defer_to_main_loop(&s->common.job, mirror_exit, data);
|
||||
block_job_defer_to_main_loop(&s->common, mirror_exit, data);
|
||||
}
|
||||
|
||||
static void mirror_complete(Job *job, Error **errp)
|
||||
static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void mirror_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
BlockDriverState *target;
|
||||
|
||||
target = blk_bs(s->target);
|
||||
@@ -954,12 +967,12 @@ static void mirror_complete(Job *job, Error **errp)
|
||||
}
|
||||
|
||||
s->should_complete = true;
|
||||
job_enter(job);
|
||||
block_job_enter(&s->common);
|
||||
}
|
||||
|
||||
static void mirror_pause(Job *job)
|
||||
static void mirror_pause(BlockJob *job)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
|
||||
mirror_wait_for_all_io(s);
|
||||
}
|
||||
@@ -987,31 +1000,23 @@ static void mirror_drain(BlockJob *job)
|
||||
}
|
||||
|
||||
static const BlockJobDriver mirror_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = JOB_TYPE_MIRROR,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.start = mirror_run,
|
||||
.pause = mirror_pause,
|
||||
.complete = mirror_complete,
|
||||
},
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_MIRROR,
|
||||
.set_speed = mirror_set_speed,
|
||||
.start = mirror_run,
|
||||
.complete = mirror_complete,
|
||||
.pause = mirror_pause,
|
||||
.attached_aio_context = mirror_attached_aio_context,
|
||||
.drain = mirror_drain,
|
||||
};
|
||||
|
||||
static const BlockJobDriver commit_active_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = JOB_TYPE_COMMIT,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.start = mirror_run,
|
||||
.pause = mirror_pause,
|
||||
.complete = mirror_complete,
|
||||
},
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = mirror_set_speed,
|
||||
.start = mirror_run,
|
||||
.complete = mirror_complete,
|
||||
.pause = mirror_pause,
|
||||
.attached_aio_context = mirror_attached_aio_context,
|
||||
.drain = mirror_drain,
|
||||
};
|
||||
@@ -1145,8 +1150,6 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
mirror_top_bs->implicit = true;
|
||||
}
|
||||
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_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs));
|
||||
|
||||
/* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
|
||||
@@ -1163,7 +1166,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Make sure that the source is not resized while the job is running */
|
||||
s = block_job_create(job_id, driver, NULL, mirror_top_bs,
|
||||
s = block_job_create(job_id, driver, mirror_top_bs,
|
||||
BLK_PERM_CONSISTENT_READ,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
|
||||
@@ -1248,7 +1251,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
trace_mirror_start(bs, s, opaque);
|
||||
job_start(&s->common.job);
|
||||
block_job_start(&s->common);
|
||||
return;
|
||||
|
||||
fail:
|
||||
@@ -1259,7 +1262,7 @@ fail:
|
||||
|
||||
g_free(s->replaces);
|
||||
blk_unref(s->target);
|
||||
job_early_fail(&s->common.job);
|
||||
block_job_early_fail(&s->common);
|
||||
}
|
||||
|
||||
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
||||
@@ -1286,7 +1289,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
||||
mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces,
|
||||
mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
|
||||
speed, granularity, buf_size, backing_mode,
|
||||
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||
&mirror_job_driver, is_none_mode, base, false,
|
||||
|
||||
@@ -228,52 +228,6 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nbd_parse_blockstatus_payload
|
||||
* support only one extent in reply and only for
|
||||
* base:allocation context
|
||||
*/
|
||||
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
||||
NBDStructuredReplyChunk *chunk,
|
||||
uint8_t *payload, uint64_t orig_length,
|
||||
NBDExtent *extent, Error **errp)
|
||||
{
|
||||
uint32_t context_id;
|
||||
|
||||
if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
|
||||
error_setg(errp, "Protocol error: invalid payload for "
|
||||
"NBD_REPLY_TYPE_BLOCK_STATUS");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
context_id = payload_advance32(&payload);
|
||||
if (client->info.meta_base_allocation_id != context_id) {
|
||||
error_setg(errp, "Protocol error: unexpected context id %d for "
|
||||
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
|
||||
"id is %d", context_id,
|
||||
client->info.meta_base_allocation_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
extent->length = payload_advance32(&payload);
|
||||
extent->flags = payload_advance32(&payload);
|
||||
|
||||
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 "
|
||||
"invalid length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nbd_parse_error_payload
|
||||
* on success @errp contains message describing nbd error reply
|
||||
*/
|
||||
@@ -527,7 +481,6 @@ static coroutine_fn int nbd_co_receive_one_chunk(
|
||||
|
||||
typedef struct NBDReplyChunkIter {
|
||||
int ret;
|
||||
bool fatal;
|
||||
Error *err;
|
||||
bool done, only_structured;
|
||||
} NBDReplyChunkIter;
|
||||
@@ -537,12 +490,11 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
|
||||
{
|
||||
assert(ret < 0);
|
||||
|
||||
if ((fatal && !iter->fatal) || iter->ret == 0) {
|
||||
if (fatal || iter->ret == 0) {
|
||||
if (iter->ret != 0) {
|
||||
error_free(iter->err);
|
||||
iter->err = NULL;
|
||||
}
|
||||
iter->fatal = fatal;
|
||||
iter->ret = ret;
|
||||
error_propagate(&iter->err, *local_err);
|
||||
} else {
|
||||
@@ -688,68 +640,6 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
||||
return iter.ret;
|
||||
}
|
||||
|
||||
static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||
uint64_t handle, uint64_t length,
|
||||
NBDExtent *extent, Error **errp)
|
||||
{
|
||||
NBDReplyChunkIter iter;
|
||||
NBDReply reply;
|
||||
void *payload = NULL;
|
||||
Error *local_err = NULL;
|
||||
bool received = false;
|
||||
|
||||
assert(!extent->length);
|
||||
NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
|
||||
NULL, &reply, &payload)
|
||||
{
|
||||
int ret;
|
||||
NBDStructuredReplyChunk *chunk = &reply.structured;
|
||||
|
||||
assert(nbd_reply_is_structured(&reply));
|
||||
|
||||
switch (chunk->type) {
|
||||
case NBD_REPLY_TYPE_BLOCK_STATUS:
|
||||
if (received) {
|
||||
s->quit = true;
|
||||
error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
|
||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
||||
}
|
||||
received = true;
|
||||
|
||||
ret = nbd_parse_blockstatus_payload(s, &reply.structured,
|
||||
payload, length, extent,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
s->quit = true;
|
||||
nbd_iter_error(&iter, true, ret, &local_err);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!nbd_reply_type_is_error(chunk->type)) {
|
||||
s->quit = true;
|
||||
error_setg(&local_err,
|
||||
"Unexpected reply type: %d (%s) "
|
||||
"for CMD_BLOCK_STATUS",
|
||||
chunk->type, nbd_reply_type_lookup(chunk->type));
|
||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
||||
}
|
||||
}
|
||||
|
||||
g_free(payload);
|
||||
payload = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
return iter.ret;
|
||||
}
|
||||
|
||||
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
|
||||
QEMUIOVector *write_qiov)
|
||||
{
|
||||
@@ -892,51 +782,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
|
||||
return nbd_co_request(bs, &request, NULL);
|
||||
}
|
||||
|
||||
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
|
||||
bool want_zero,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
int64_t ret;
|
||||
NBDExtent extent = { 0 };
|
||||
NBDClientSession *client = nbd_get_client_session(bs);
|
||||
Error *local_err = NULL;
|
||||
|
||||
NBDRequest request = {
|
||||
.type = NBD_CMD_BLOCK_STATUS,
|
||||
.from = offset,
|
||||
.len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
|
||||
bs->bl.request_alignment),
|
||||
client->info.max_block), bytes),
|
||||
.flags = NBD_CMD_FLAG_REQ_ONE,
|
||||
};
|
||||
|
||||
if (!client->info.base_allocation) {
|
||||
*pnum = bytes;
|
||||
return BDRV_BLOCK_DATA;
|
||||
}
|
||||
|
||||
ret = nbd_co_send_request(bs, &request, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes,
|
||||
&extent, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(extent.length);
|
||||
*pnum = extent.length;
|
||||
return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
|
||||
(extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0);
|
||||
}
|
||||
|
||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
NBDClientSession *client = nbd_get_client_session(bs);
|
||||
@@ -981,7 +826,6 @@ int nbd_client_init(BlockDriverState *bs,
|
||||
|
||||
client->info.request_sizes = true;
|
||||
client->info.structured_reply = true;
|
||||
client->info.base_allocation = true;
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
||||
tlscreds, hostname,
|
||||
&client->ioc, &client->info, errp);
|
||||
|
||||
@@ -61,10 +61,4 @@ void nbd_client_detach_aio_context(BlockDriverState *bs);
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context);
|
||||
|
||||
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
|
||||
bool want_zero,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file);
|
||||
|
||||
#endif /* NBD_CLIENT_H */
|
||||
|
||||
23
block/nbd.c
23
block/nbd.c
@@ -27,8 +27,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "nbd-client.h"
|
||||
#include "block/qdict.h"
|
||||
#include "block/nbd-client.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "block/block_int.h"
|
||||
@@ -263,6 +262,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
||||
{
|
||||
SocketAddress *saddr = NULL;
|
||||
QDict *addr = NULL;
|
||||
QObject *crumpled_addr = NULL;
|
||||
Visitor *iv = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
@@ -272,11 +272,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
||||
goto done;
|
||||
}
|
||||
|
||||
iv = qobject_input_visitor_new_flat_confused(addr, errp);
|
||||
if (!iv) {
|
||||
crumpled_addr = qdict_crumple(addr, errp);
|
||||
if (!crumpled_addr) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
|
||||
* server.type=inet. .to doesn't matter, it's ignored anyway.
|
||||
* That's because when @options come from -blockdev or
|
||||
* blockdev_add, members are typed according to the QAPI schema,
|
||||
* but when they come from -drive, they're all QString. The
|
||||
* visitor expects the former.
|
||||
*/
|
||||
iv = qobject_input_visitor_new(crumpled_addr);
|
||||
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -284,7 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
||||
}
|
||||
|
||||
done:
|
||||
qobject_unref(addr);
|
||||
QDECREF(addr);
|
||||
qobject_decref(crumpled_addr);
|
||||
visit_free(iv);
|
||||
return saddr;
|
||||
}
|
||||
@@ -575,7 +585,6 @@ static BlockDriver bdrv_nbd = {
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_tcp = {
|
||||
@@ -595,7 +604,6 @@ static BlockDriver bdrv_nbd_tcp = {
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_unix = {
|
||||
@@ -615,7 +623,6 @@ static BlockDriver bdrv_nbd_unix = {
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
|
||||
18
block/nfs.c
18
block/nfs.c
@@ -29,7 +29,6 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -556,29 +555,24 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevOptionsNfs *opts = NULL;
|
||||
QObject *crumpled = NULL;
|
||||
Visitor *v;
|
||||
const QDictEntry *e;
|
||||
Error *local_err = NULL;
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
||||
if (!v) {
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove the processed options from the QDict (the visitor processes
|
||||
* _all_ options in the QDict) */
|
||||
while ((e = qdict_first(options))) {
|
||||
qdict_del(options, e->key);
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -689,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
48
block/null.c
48
block/null.c
@@ -93,7 +93,6 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
|
||||
qemu_opts_del(opts);
|
||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -117,22 +116,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
if (s->read_zeroes) {
|
||||
qemu_iovec_memset(qiov, 0, 0, bytes);
|
||||
qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
return null_co_common(bs);
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
return null_co_common(bs);
|
||||
}
|
||||
@@ -187,26 +186,26 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
if (s->read_zeroes) {
|
||||
qemu_iovec_memset(qiov, 0, 0, bytes);
|
||||
qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
@@ -245,6 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
|
||||
|
||||
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
{
|
||||
QINCREF(opts);
|
||||
qdict_del(opts, "filename");
|
||||
|
||||
if (!qdict_size(opts)) {
|
||||
@@ -253,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
}
|
||||
|
||||
qdict_put_str(opts, "driver", bs->drv->format_name);
|
||||
bs->full_open_options = qobject_ref(opts);
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_null_co = {
|
||||
@@ -266,8 +266,8 @@ static BlockDriver bdrv_null_co = {
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_co_preadv = null_co_preadv,
|
||||
.bdrv_co_pwritev = null_co_pwritev,
|
||||
.bdrv_co_readv = null_co_readv,
|
||||
.bdrv_co_writev = null_co_writev,
|
||||
.bdrv_co_flush_to_disk = null_co_flush,
|
||||
.bdrv_reopen_prepare = null_reopen_prepare,
|
||||
|
||||
@@ -286,8 +286,8 @@ static BlockDriver bdrv_null_aio = {
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_aio_preadv = null_aio_preadv,
|
||||
.bdrv_aio_pwritev = null_aio_pwritev,
|
||||
.bdrv_aio_readv = null_aio_readv,
|
||||
.bdrv_aio_writev = null_aio_writev,
|
||||
.bdrv_aio_flush = null_aio_flush,
|
||||
.bdrv_reopen_prepare = null_reopen_prepare,
|
||||
|
||||
|
||||
14
block/nvme.c
14
block/nvme.c
@@ -695,11 +695,12 @@ static void nvme_parse_filename(const char *filename, QDict *options,
|
||||
unsigned long ns;
|
||||
const char *slash = strchr(tmp, '/');
|
||||
if (!slash) {
|
||||
qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp);
|
||||
qdict_put(options, NVME_BLOCK_OPT_DEVICE,
|
||||
qstring_from_str(tmp));
|
||||
return;
|
||||
}
|
||||
device = g_strndup(tmp, slash - tmp);
|
||||
qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, device);
|
||||
qdict_put(options, NVME_BLOCK_OPT_DEVICE, qstring_from_str(device));
|
||||
g_free(device);
|
||||
namespace = slash + 1;
|
||||
if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) {
|
||||
@@ -707,8 +708,8 @@ static void nvme_parse_filename(const char *filename, QDict *options,
|
||||
namespace);
|
||||
return;
|
||||
}
|
||||
qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE,
|
||||
*namespace ? namespace : "1");
|
||||
qdict_put(options, NVME_BLOCK_OPT_NAMESPACE,
|
||||
qstring_from_str(*namespace ? namespace : "1"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,6 +1074,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
|
||||
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
{
|
||||
QINCREF(opts);
|
||||
qdict_del(opts, "filename");
|
||||
|
||||
if (!qdict_size(opts)) {
|
||||
@@ -1080,8 +1082,8 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
bs->drv->format_name);
|
||||
}
|
||||
|
||||
qdict_put_str(opts, "driver", bs->drv->format_name);
|
||||
bs->full_open_options = qobject_ref(opts);
|
||||
qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
|
||||
@@ -31,13 +31,9 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "migration/blocker.h"
|
||||
@@ -83,25 +79,6 @@ static QemuOptsList parallels_runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static QemuOptsList parallels_create_opts = {
|
||||
.name = "parallels-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size",
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Parallels image cluster size",
|
||||
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
|
||||
{
|
||||
@@ -312,15 +289,13 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
uint64_t bytes_done = 0;
|
||||
QEMUIOVector hd_qiov;
|
||||
int ret = 0;
|
||||
|
||||
assert(!flags);
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
@@ -505,67 +480,46 @@ out:
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
||||
Error **errp)
|
||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsParallels *parallels_opts;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
int64_t total_size, cl_size;
|
||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *file;
|
||||
uint32_t bat_entries, bat_sectors;
|
||||
ParallelsHeader header;
|
||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||
int ret;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
|
||||
parallels_opts = &opts->u.parallels;
|
||||
|
||||
/* Sanity checks */
|
||||
total_size = parallels_opts->size;
|
||||
|
||||
if (parallels_opts->has_cluster_size) {
|
||||
cl_size = parallels_opts->cluster_size;
|
||||
} else {
|
||||
cl_size = DEFAULT_CLUSTER_SIZE;
|
||||
}
|
||||
|
||||
/* XXX What is the real limit here? This is an insanely large maximum. */
|
||||
if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) {
|
||||
error_setg(errp, "Cluster size is too large");
|
||||
return -EINVAL;
|
||||
}
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
|
||||
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
|
||||
error_setg(errp, "Image size is too large for this cluster size");
|
||||
error_propagate(errp, local_err);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
||||
return -EINVAL;
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "Cluster size must be a multiple of 512 bytes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(parallels_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
file = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (file == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
blk_set_allow_write_beyond_eof(file, true);
|
||||
|
||||
/* Create image format */
|
||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bat_entries = DIV_ROUND_UP(total_size, cl_size);
|
||||
@@ -588,103 +542,24 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
memcpy(tmp, &header, sizeof(header));
|
||||
|
||||
ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0);
|
||||
ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
|
||||
ret = blk_pwrite_zeroes(file, BDRV_SECTOR_SIZE,
|
||||
(bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
|
||||
done:
|
||||
blk_unref(file);
|
||||
return ret;
|
||||
|
||||
exit:
|
||||
error_setg_errno(errp, -ret, "Failed to create Parallels image");
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, ¶llels_create_opts,
|
||||
true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "parallels");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Silently round up sizes */
|
||||
create_options->u.parallels.size =
|
||||
ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE);
|
||||
create_options->u.parallels.cluster_size =
|
||||
ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the Parallels image (format layer) */
|
||||
ret = parallels_co_create(create_options, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
@@ -896,6 +771,25 @@ static void parallels_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static QemuOptsList parallels_create_opts = {
|
||||
.name = "parallels-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size",
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Parallels image cluster size",
|
||||
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_parallels = {
|
||||
.format_name = "parallels",
|
||||
.instance_size = sizeof(BDRVParallelsState),
|
||||
@@ -909,7 +803,6 @@ static BlockDriver bdrv_parallels = {
|
||||
.bdrv_co_readv = parallels_co_readv,
|
||||
.bdrv_co_writev = parallels_co_writev,
|
||||
.supports_backing = true,
|
||||
.bdrv_co_create = parallels_co_create,
|
||||
.bdrv_co_create_opts = parallels_co_create_opts,
|
||||
.bdrv_co_check = parallels_co_check,
|
||||
.create_opts = ¶llels_create_opts,
|
||||
|
||||
55
block/qapi.c
55
block/qapi.c
@@ -394,37 +394,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static uint64List *uint64_list(uint64_t *list, int size)
|
||||
{
|
||||
int i;
|
||||
uint64List *out_list = NULL;
|
||||
uint64List **pout_list = &out_list;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
uint64List *entry = g_new(uint64List, 1);
|
||||
entry->value = list[i];
|
||||
*pout_list = entry;
|
||||
pout_list = &entry->next;
|
||||
}
|
||||
|
||||
*pout_list = NULL;
|
||||
|
||||
return out_list;
|
||||
}
|
||||
|
||||
static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist,
|
||||
bool *not_null,
|
||||
BlockLatencyHistogramInfo **info)
|
||||
{
|
||||
*not_null = hist->bins != NULL;
|
||||
if (*not_null) {
|
||||
*info = g_new0(BlockLatencyHistogramInfo, 1);
|
||||
|
||||
(*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1);
|
||||
(*info)->bins = uint64_list(hist->bins, hist->nbins);
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
{
|
||||
BlockAcctStats *stats = blk_get_stats(blk);
|
||||
@@ -490,16 +459,6 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
|
||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
|
||||
&ds->has_x_rd_latency_histogram,
|
||||
&ds->x_rd_latency_histogram);
|
||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
|
||||
&ds->has_x_wr_latency_histogram,
|
||||
&ds->x_wr_latency_histogram);
|
||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
|
||||
&ds->has_x_flush_latency_histogram,
|
||||
&ds->x_flush_latency_histogram);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
|
||||
@@ -688,29 +647,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
||||
{
|
||||
switch (qobject_type(obj)) {
|
||||
case QTYPE_QNUM: {
|
||||
QNum *value = qobject_to(QNum, obj);
|
||||
QNum *value = qobject_to_qnum(obj);
|
||||
char *tmp = qnum_to_string(value);
|
||||
func_fprintf(f, "%s", tmp);
|
||||
g_free(tmp);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QSTRING: {
|
||||
QString *value = qobject_to(QString, obj);
|
||||
QString *value = qobject_to_qstring(obj);
|
||||
func_fprintf(f, "%s", qstring_get_str(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_QDICT: {
|
||||
QDict *value = qobject_to(QDict, obj);
|
||||
QDict *value = qobject_to_qdict(obj);
|
||||
dump_qdict(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QLIST: {
|
||||
QList *value = qobject_to(QList, obj);
|
||||
QList *value = qobject_to_qlist(obj);
|
||||
dump_qlist(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QBOOL: {
|
||||
QBool *value = qobject_to(QBool, obj);
|
||||
QBool *value = qobject_to_qbool(obj);
|
||||
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
@@ -771,9 +730,9 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
|
||||
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
|
||||
visit_complete(v, &obj);
|
||||
data = qdict_get(qobject_to(QDict, obj), "data");
|
||||
data = qdict_get(qobject_to_qdict(obj), "data");
|
||||
dump_qobject(func_fprintf, f, 1, data);
|
||||
qobject_unref(obj);
|
||||
qobject_decref(obj);
|
||||
visit_free(v);
|
||||
}
|
||||
|
||||
|
||||
207
block/qcow.c
207
block/qcow.c
@@ -26,7 +26,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -34,11 +33,9 @@
|
||||
#include <zlib.h>
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "crypto/block.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "crypto.h"
|
||||
#include "block/crypto.h"
|
||||
|
||||
/**************************************************************/
|
||||
/* QEMU COW block driver with compression and encryption support */
|
||||
@@ -89,8 +86,6 @@ typedef struct BDRVQcowState {
|
||||
Error *migration_blocker;
|
||||
} BDRVQcowState;
|
||||
|
||||
static QemuOptsList qcow_create_opts;
|
||||
|
||||
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||
|
||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
@@ -316,7 +311,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qobject_unref(encryptopts);
|
||||
QDECREF(encryptopts);
|
||||
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
@@ -327,7 +322,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
g_free(s->cluster_cache);
|
||||
g_free(s->cluster_data);
|
||||
qcrypto_block_free(s->crypto);
|
||||
qobject_unref(encryptopts);
|
||||
QDECREF(encryptopts);
|
||||
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
||||
return ret;
|
||||
}
|
||||
@@ -721,8 +716,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster;
|
||||
@@ -733,7 +727,6 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf;
|
||||
void *orig_buf;
|
||||
|
||||
assert(!flags);
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
|
||||
/* We must always copy the iov when encrypting, so we
|
||||
@@ -817,50 +810,62 @@ static void qcow_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsQcow *qcow_opts;
|
||||
int header_size, backing_filename_len, l1_size, shift, i;
|
||||
QCowHeader header;
|
||||
uint8_t *tmp;
|
||||
int64_t total_size = 0;
|
||||
char *backing_file = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *qcow_blk;
|
||||
char *encryptfmt = NULL;
|
||||
QDict *options;
|
||||
QDict *encryptopts = NULL;
|
||||
QCryptoBlockCreateOptions *crypto_opts = NULL;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_QCOW);
|
||||
qcow_opts = &opts->u.qcow;
|
||||
|
||||
/* Sanity checks */
|
||||
total_size = qcow_opts->size;
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
if (total_size == 0) {
|
||||
error_setg(errp, "Image size is too small, cannot be zero length");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcow_opts->has_encrypt &&
|
||||
qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW)
|
||||
{
|
||||
error_setg(errp, "Unsupported encryption format");
|
||||
return -EINVAL;
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||
if (encryptfmt) {
|
||||
if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
|
||||
error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
|
||||
BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
|
||||
encryptfmt = g_strdup("aes");
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(qcow_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(qcow_blk, bs, errp);
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
qcow_blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (qcow_blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||
|
||||
/* Create image format */
|
||||
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
@@ -872,15 +877,16 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
header.size = cpu_to_be64(total_size);
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (qcow_opts->has_backing_file) {
|
||||
if (strcmp(qcow_opts->backing_file, "fat:")) {
|
||||
if (backing_file) {
|
||||
if (strcmp(backing_file, "fat:")) {
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(qcow_opts->backing_file);
|
||||
backing_filename_len = strlen(backing_file);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
} else {
|
||||
/* special backing file for vvfat */
|
||||
qcow_opts->has_backing_file = false;
|
||||
g_free(backing_file);
|
||||
backing_file = NULL;
|
||||
}
|
||||
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||
unmodified sectors */
|
||||
@@ -895,10 +901,26 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
|
||||
header.l1_table_offset = cpu_to_be64(header_size);
|
||||
|
||||
if (qcow_opts->has_encrypt) {
|
||||
options = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_extract_subqdict(options, &encryptopts, "encrypt.");
|
||||
QDECREF(options);
|
||||
if (encryptfmt) {
|
||||
if (!g_str_equal(encryptfmt, "aes")) {
|
||||
error_setg(errp, "Unknown encryption format '%s', expected 'aes'",
|
||||
encryptfmt);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
|
||||
crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.",
|
||||
crypto_opts = block_crypto_create_opts_init(
|
||||
Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
|
||||
if (!crypto_opts) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
crypto = qcrypto_block_create(crypto_opts, "encrypt.",
|
||||
NULL, NULL, NULL, errp);
|
||||
if (!crypto) {
|
||||
ret = -EINVAL;
|
||||
@@ -914,9 +936,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (qcow_opts->has_backing_file) {
|
||||
if (backing_file) {
|
||||
ret = blk_pwrite(qcow_blk, sizeof(header),
|
||||
qcow_opts->backing_file, backing_filename_len, 0);
|
||||
backing_file, backing_filename_len, 0);
|
||||
if (ret != backing_filename_len) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -937,96 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
ret = 0;
|
||||
exit:
|
||||
blk_unref(qcow_blk);
|
||||
cleanup:
|
||||
QDECREF(encryptopts);
|
||||
g_free(encryptfmt);
|
||||
qcrypto_block_free(crypto);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow_co_create_opts(const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
const char *val;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
||||
{ BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true);
|
||||
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
|
||||
if (val && !strcmp(val, "on")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
|
||||
} else if (val && !strcmp(val, "off")) {
|
||||
qdict_del(qdict, BLOCK_OPT_ENCRYPT);
|
||||
}
|
||||
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||
if (val && !strcmp(val, "aes")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
|
||||
}
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "qcow");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_QCOW);
|
||||
create_options->u.qcow.size =
|
||||
ROUND_UP(create_options->u.qcow.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the qcow image (format layer) */
|
||||
ret = qcow_co_create(create_options, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
qapi_free_QCryptoBlockCreateOptions(crypto_opts);
|
||||
g_free(backing_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1109,7 +1047,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||
/* could not compress: write normal cluster */
|
||||
ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS, qiov, 0);
|
||||
bytes >> BDRV_SECTOR_BITS, qiov);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1190,7 +1128,6 @@ static BlockDriver bdrv_qcow = {
|
||||
.bdrv_close = qcow_close,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
||||
.bdrv_co_create = qcow_co_create,
|
||||
.bdrv_co_create_opts = qcow_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.supports_backing = true,
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "qcow2.h"
|
||||
#include "block/qcow2.h"
|
||||
|
||||
/* NOTICE: BME here means Bitmaps Extension and used as a namespace for
|
||||
* _internal_ constants. Please do not use this _internal_ abbreviation for
|
||||
@@ -254,6 +254,7 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
|
||||
|
||||
ret = bitmap_table_load(bs, tb, &bitmap_table);
|
||||
if (ret < 0) {
|
||||
assert(bitmap_table == NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1003,8 +1004,7 @@ fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
Error **errp)
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2BitmapList *bm_list;
|
||||
@@ -1012,10 +1012,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
GSList *ro_dirty_bitmaps = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (header_updated != NULL) {
|
||||
*header_updated = false;
|
||||
}
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
/* No bitmaps - nothing to do */
|
||||
return 0;
|
||||
@@ -1059,9 +1055,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
goto out;
|
||||
}
|
||||
if (header_updated != NULL) {
|
||||
*header_updated = true;
|
||||
}
|
||||
g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
|
||||
}
|
||||
|
||||
@@ -1072,11 +1065,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
|
||||
}
|
||||
|
||||
/* store_bitmap_data()
|
||||
* Store bitmap to image, filling bitmap table accordingly.
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qcow2.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qcow2.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/cutils.h"
|
||||
@@ -839,13 +839,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
|
||||
}
|
||||
ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
|
||||
/* If the caller needs to restart the search for free clusters,
|
||||
* try the same ones first to see if they're still free. */
|
||||
if (ret == -EAGAIN) {
|
||||
if (s->free_cluster_index > (start >> s->cluster_bits)) {
|
||||
s->free_cluster_index = (start >> s->cluster_bits);
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1577,9 +1570,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
|
||||
if (l2_entry & QCOW_OFLAG_COPIED) {
|
||||
fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": "
|
||||
fprintf(stderr, "ERROR: cluster %" PRId64 ": "
|
||||
"copied flag must never be set for compressed "
|
||||
"clusters\n", l2_entry & s->cluster_offset_mask);
|
||||
"clusters\n", l2_entry >> s->cluster_bits);
|
||||
l2_entry &= ~QCOW_OFLAG_COPIED;
|
||||
res->corruptions++;
|
||||
}
|
||||
@@ -1799,19 +1792,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
int ret;
|
||||
uint64_t refcount;
|
||||
int i, j;
|
||||
bool repair;
|
||||
|
||||
if (fix & BDRV_FIX_ERRORS) {
|
||||
/* Always repair */
|
||||
repair = true;
|
||||
} else if (fix & BDRV_FIX_LEAKS) {
|
||||
/* Repair only if that seems safe: This function is always
|
||||
* called after the refcounts have been fixed, so the refcount
|
||||
* is accurate if that repair was successful */
|
||||
repair = !res->check_errors && !res->corruptions && !res->leaks;
|
||||
} else {
|
||||
repair = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->l1_size; i++) {
|
||||
uint64_t l1_entry = s->l1_table[i];
|
||||
@@ -1831,8 +1811,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
|
||||
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||
repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
|
||||
if (repair) {
|
||||
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||
"ERROR",
|
||||
i, l1_entry, refcount);
|
||||
if (fix & BDRV_FIX_ERRORS) {
|
||||
s->l1_table[i] = refcount == 1
|
||||
? l1_entry | QCOW_OFLAG_COPIED
|
||||
: l1_entry & ~QCOW_OFLAG_COPIED;
|
||||
@@ -1873,8 +1855,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
|
||||
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||
repair ? "Repairing" : "ERROR", l2_entry, refcount);
|
||||
if (repair) {
|
||||
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||
"ERROR",
|
||||
l2_entry, refcount);
|
||||
if (fix & BDRV_FIX_ERRORS) {
|
||||
l2_table[j] = cpu_to_be64(refcount == 1
|
||||
? l2_entry | QCOW_OFLAG_COPIED
|
||||
: l2_entry & ~QCOW_OFLAG_COPIED);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qcow2.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
373
block/qcow2.c
373
block/qcow2.c
@@ -24,11 +24,10 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "qcow2.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-block-core.h"
|
||||
@@ -40,7 +39,7 @@
|
||||
#include "qemu/bswap.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "crypto.h"
|
||||
#include "block/crypto.h"
|
||||
|
||||
/*
|
||||
Differences with QCOW:
|
||||
@@ -769,7 +768,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t combined_cache_size;
|
||||
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
|
||||
int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size;
|
||||
|
||||
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);
|
||||
@@ -804,28 +802,23 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
} else if (refcount_cache_size_set) {
|
||||
*l2_cache_size = combined_cache_size - *refcount_cache_size;
|
||||
} else {
|
||||
uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);
|
||||
|
||||
/* Assign as much memory as possible to the L2 cache, and
|
||||
* use the remainder for the refcount cache */
|
||||
if (combined_cache_size >= max_l2_cache + min_refcount_cache) {
|
||||
*l2_cache_size = max_l2_cache;
|
||||
*refcount_cache_size = combined_cache_size - *l2_cache_size;
|
||||
} else {
|
||||
*refcount_cache_size =
|
||||
MIN(combined_cache_size, min_refcount_cache);
|
||||
*l2_cache_size = combined_cache_size - *refcount_cache_size;
|
||||
}
|
||||
*refcount_cache_size = combined_cache_size
|
||||
/ (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1);
|
||||
*l2_cache_size = combined_cache_size - *refcount_cache_size;
|
||||
}
|
||||
} else {
|
||||
if (!l2_cache_size_set) {
|
||||
if (!l2_cache_size_set && !refcount_cache_size_set) {
|
||||
*l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
|
||||
(uint64_t)DEFAULT_L2_CACHE_CLUSTERS
|
||||
* s->cluster_size);
|
||||
}
|
||||
if (!refcount_cache_size_set) {
|
||||
*refcount_cache_size = min_refcount_cache;
|
||||
*refcount_cache_size = *l2_cache_size
|
||||
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO;
|
||||
} else if (!l2_cache_size_set) {
|
||||
*l2_cache_size = *refcount_cache_size
|
||||
* DEFAULT_L2_REFCOUNT_SIZE_RATIO;
|
||||
} else if (!refcount_cache_size_set) {
|
||||
*refcount_cache_size = *l2_cache_size
|
||||
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1070,7 +1063,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qobject_unref(encryptopts);
|
||||
QDECREF(encryptopts);
|
||||
qemu_opts_del(opts);
|
||||
opts = NULL;
|
||||
return ret;
|
||||
@@ -1149,7 +1142,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
uint64_t ext_end;
|
||||
uint64_t l1_vm_state_index;
|
||||
bool update_header = false;
|
||||
bool header_updated = false;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
@@ -1488,23 +1480,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
|
||||
}
|
||||
|
||||
if (s->dirty_bitmaps_loaded) {
|
||||
/* It's some kind of reopen. There are no known cases where we need to
|
||||
* reload bitmaps in such a situation, so it's safer to skip them.
|
||||
*
|
||||
* Moreover, if we have some readonly bitmaps and we are reopening for
|
||||
* rw we should reopen bitmaps correspondingly.
|
||||
*/
|
||||
if (bdrv_has_readonly_bitmaps(bs) &&
|
||||
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE))
|
||||
{
|
||||
qcow2_reopen_bitmaps_rw_hint(bs, &header_updated, &local_err);
|
||||
}
|
||||
} else {
|
||||
header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
|
||||
s->dirty_bitmaps_loaded = true;
|
||||
if (qcow2_load_dirty_bitmaps(bs, &local_err)) {
|
||||
update_header = false;
|
||||
}
|
||||
update_header = update_header && !header_updated;
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
@@ -1762,39 +1740,6 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
||||
return status;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs,
|
||||
QCowL2Meta **pl2meta,
|
||||
bool link_l2)
|
||||
{
|
||||
int ret = 0;
|
||||
QCowL2Meta *l2meta = *pl2meta;
|
||||
|
||||
while (l2meta != NULL) {
|
||||
QCowL2Meta *next;
|
||||
|
||||
if (!ret && link_l2) {
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take the request off the list of running requests */
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
|
||||
next = l2meta->next;
|
||||
g_free(l2meta);
|
||||
l2meta = next;
|
||||
}
|
||||
out:
|
||||
*pl2meta = l2meta;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
@@ -2081,9 +2026,24 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_handle_l2meta(bs, &l2meta, true);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
while (l2meta != NULL) {
|
||||
QCowL2Meta *next;
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Take the request off the list of running requests */
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
|
||||
next = l2meta->next;
|
||||
g_free(l2meta);
|
||||
l2meta = next;
|
||||
}
|
||||
|
||||
bytes -= cur_bytes;
|
||||
@@ -2094,7 +2054,18 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
qcow2_handle_l2meta(bs, &l2meta, false);
|
||||
while (l2meta != NULL) {
|
||||
QCowL2Meta *next;
|
||||
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
|
||||
next = l2meta->next;
|
||||
g_free(l2meta);
|
||||
l2meta = next;
|
||||
}
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
@@ -2197,7 +2168,7 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_do_open(bs, options, flags, &local_err);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qcow2 layer: ");
|
||||
@@ -3080,7 +3051,8 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
@@ -3151,12 +3123,15 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to_qdict(qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
@@ -3178,7 +3153,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
|
||||
|
||||
ret = 0;
|
||||
finish:
|
||||
qobject_unref(qdict);
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
@@ -3277,166 +3252,6 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
qcow2_co_copy_range_from(BlockDriverState *bs,
|
||||
BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int ret;
|
||||
unsigned int cur_bytes; /* number of bytes in current iteration */
|
||||
BdrvChild *child = NULL;
|
||||
BdrvRequestFlags cur_flags;
|
||||
|
||||
assert(!bs->encrypted);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
while (bytes != 0) {
|
||||
uint64_t copy_offset = 0;
|
||||
/* prepare next request */
|
||||
cur_bytes = MIN(bytes, INT_MAX);
|
||||
cur_flags = flags;
|
||||
|
||||
ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (bs->backing && bs->backing->bs) {
|
||||
int64_t backing_length = bdrv_getlength(bs->backing->bs);
|
||||
if (src_offset >= backing_length) {
|
||||
cur_flags |= BDRV_REQ_ZERO_WRITE;
|
||||
} else {
|
||||
child = bs->backing;
|
||||
cur_bytes = MIN(cur_bytes, backing_length - src_offset);
|
||||
copy_offset = src_offset;
|
||||
}
|
||||
} else {
|
||||
cur_flags |= BDRV_REQ_ZERO_WRITE;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
cur_flags |= BDRV_REQ_ZERO_WRITE;
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
ret = -ENOTSUP;
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
child = bs->file;
|
||||
copy_offset += offset_into_cluster(s, src_offset);
|
||||
if ((copy_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_copy_range_from(child,
|
||||
copy_offset,
|
||||
dst, dst_offset,
|
||||
cur_bytes, cur_flags);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
bytes -= cur_bytes;
|
||||
src_offset += cur_bytes;
|
||||
dst_offset += cur_bytes;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
qcow2_co_copy_range_to(BlockDriverState *bs,
|
||||
BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int offset_in_cluster;
|
||||
int ret;
|
||||
unsigned int cur_bytes; /* number of sectors in current iteration */
|
||||
uint64_t cluster_offset;
|
||||
uint8_t *cluster_data = NULL;
|
||||
QCowL2Meta *l2meta = NULL;
|
||||
|
||||
assert(!bs->encrypted);
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
while (bytes != 0) {
|
||||
|
||||
l2meta = NULL;
|
||||
|
||||
offset_in_cluster = offset_into_cluster(s, dst_offset);
|
||||
cur_bytes = MIN(bytes, INT_MAX);
|
||||
|
||||
/* TODO:
|
||||
* If src->bs == dst->bs, we could simply copy by incrementing
|
||||
* the refcnt, without copying user data.
|
||||
* Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */
|
||||
ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes,
|
||||
&cluster_offset, &l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert((cluster_offset & 511) == 0);
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
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,
|
||||
bs->file,
|
||||
cluster_offset + offset_in_cluster,
|
||||
cur_bytes, flags);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = qcow2_handle_l2meta(bs, &l2meta, true);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bytes -= cur_bytes;
|
||||
dst_offset += cur_bytes;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
qcow2_handle_l2meta(bs, &l2meta, false);
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
qemu_vfree(cluster_data);
|
||||
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
@@ -4212,21 +4027,22 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
* have to be removed.
|
||||
*/
|
||||
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
|
||||
Error **errp)
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int current_version = s->qcow_version;
|
||||
int ret;
|
||||
|
||||
/* This is qcow2_downgrade(), not qcow2_upgrade() */
|
||||
assert(target_version < current_version);
|
||||
|
||||
/* There are no other versions (now) that you can downgrade to */
|
||||
assert(target_version == 2);
|
||||
if (target_version == current_version) {
|
||||
return 0;
|
||||
} else if (target_version > current_version) {
|
||||
return -EINVAL;
|
||||
} else if (target_version != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (s->refcount_order != 4) {
|
||||
error_setg(errp, "compat=0.10 requires refcount_bits=16");
|
||||
error_report("compat=0.10 requires refcount_bits=16");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -4234,7 +4050,6 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
|
||||
ret = qcow2_mark_clean(bs);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to make the image clean");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -4244,8 +4059,6 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
* best thing to do anyway */
|
||||
|
||||
if (s->incompatible_features) {
|
||||
error_setg(errp, "Cannot downgrade an image with incompatible features "
|
||||
"%#" PRIx64 " set", s->incompatible_features);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -4259,7 +4072,6 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
|
||||
ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to turn zero into data clusters");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -4267,7 +4079,6 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->qcow_version = current_version;
|
||||
error_setg_errno(errp, -ret, "Failed to update the image header");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
@@ -4345,8 +4156,7 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
|
||||
|
||||
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque,
|
||||
Error **errp)
|
||||
void *cb_opaque)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int old_version = s->qcow_version, new_version = old_version;
|
||||
@@ -4358,6 +4168,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
bool encrypt;
|
||||
int encformat;
|
||||
int refcount_bits = s->refcount_bits;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
QemuOptDesc *desc = opts->list->desc;
|
||||
Qcow2AmendHelperCBInfo helper_cb_info;
|
||||
@@ -4378,11 +4189,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
} else if (!strcmp(compat, "1.1")) {
|
||||
new_version = 3;
|
||||
} else {
|
||||
error_setg(errp, "Unknown compatibility level %s", compat);
|
||||
error_report("Unknown compatibility level %s", compat);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
|
||||
error_setg(errp, "Cannot change preallocation mode");
|
||||
error_report("Cannot change preallocation mode");
|
||||
return -ENOTSUP;
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
|
||||
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
|
||||
@@ -4395,8 +4206,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
!!s->crypto);
|
||||
|
||||
if (encrypt != !!s->crypto) {
|
||||
error_setg(errp,
|
||||
"Changing the encryption flag is not supported");
|
||||
error_report("Changing the encryption flag is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) {
|
||||
@@ -4404,19 +4214,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT));
|
||||
|
||||
if (encformat != s->crypt_method_header) {
|
||||
error_setg(errp,
|
||||
"Changing the encryption format is not supported");
|
||||
error_report("Changing the encryption format is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (g_str_has_prefix(desc->name, "encrypt.")) {
|
||||
error_setg(errp,
|
||||
"Changing the encryption parameters is not supported");
|
||||
error_report("Changing the encryption parameters is not supported");
|
||||
return -ENOTSUP;
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
cluster_size);
|
||||
if (cluster_size != s->cluster_size) {
|
||||
error_setg(errp, "Changing the cluster size is not supported");
|
||||
error_report("Changing the cluster size is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
|
||||
@@ -4429,8 +4237,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
if (refcount_bits <= 0 || refcount_bits > 64 ||
|
||||
!is_power_of_2(refcount_bits))
|
||||
{
|
||||
error_setg(errp, "Refcount width must be a power of two and "
|
||||
"may not exceed 64 bits");
|
||||
error_report("Refcount width must be a power of two and may "
|
||||
"not exceed 64 bits");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
@@ -4455,7 +4263,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->qcow_version = old_version;
|
||||
error_setg_errno(errp, -ret, "Failed to update the image header");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -4464,17 +4271,18 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
int refcount_order = ctz32(refcount_bits);
|
||||
|
||||
if (new_version < 3 && refcount_bits != 16) {
|
||||
error_setg(errp, "Refcount widths other than 16 bits require "
|
||||
"compatibility level 1.1 or above (use compat=1.1 or "
|
||||
"greater)");
|
||||
error_report("Different refcount widths than 16 bits require "
|
||||
"compatibility level 1.1 or above (use compat=1.1 or "
|
||||
"greater)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
|
||||
ret = qcow2_change_refcount_order(bs, refcount_order,
|
||||
&qcow2_amend_helper_cb,
|
||||
&helper_cb_info, errp);
|
||||
&helper_cb_info, &local_err);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -4484,7 +4292,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
backing_file ?: s->image_backing_file,
|
||||
backing_format ?: s->image_backing_format);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to change the backing file");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -4492,16 +4299,14 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
if (s->use_lazy_refcounts != lazy_refcounts) {
|
||||
if (lazy_refcounts) {
|
||||
if (new_version < 3) {
|
||||
error_setg(errp, "Lazy refcounts only supported with "
|
||||
"compatibility level 1.1 and above (use compat=1.1 "
|
||||
"or greater)");
|
||||
error_report("Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)");
|
||||
return -EINVAL;
|
||||
}
|
||||
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
|
||||
error_setg_errno(errp, -ret, "Failed to update the image header");
|
||||
return ret;
|
||||
}
|
||||
s->use_lazy_refcounts = true;
|
||||
@@ -4509,7 +4314,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
/* make image clean first */
|
||||
ret = qcow2_mark_clean(bs);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to make the image clean");
|
||||
return ret;
|
||||
}
|
||||
/* now disallow lazy refcounts */
|
||||
@@ -4517,7 +4321,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
|
||||
error_setg_errno(errp, -ret, "Failed to update the image header");
|
||||
return ret;
|
||||
}
|
||||
s->use_lazy_refcounts = false;
|
||||
@@ -4526,15 +4329,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
|
||||
if (new_size) {
|
||||
BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
ret = blk_insert_bs(blk, bs, &local_err);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
blk_unref(blk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp);
|
||||
ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err);
|
||||
blk_unref(blk);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -4543,7 +4348,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
if (new_version < old_version) {
|
||||
helper_cb_info.current_operation = QCOW2_DOWNGRADING;
|
||||
ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
|
||||
&helper_cb_info, errp);
|
||||
&helper_cb_info);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -4566,7 +4371,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||
char *message;
|
||||
va_list ap;
|
||||
|
||||
fatal = fatal && bdrv_is_writable(bs);
|
||||
fatal = fatal && !bs->read_only;
|
||||
|
||||
if (s->signaled_corruption &&
|
||||
(!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT)))
|
||||
@@ -4695,8 +4500,6 @@ BlockDriver bdrv_qcow2 = {
|
||||
|
||||
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = qcow2_co_pdiscard,
|
||||
.bdrv_co_copy_range_from = qcow2_co_copy_range_from,
|
||||
.bdrv_co_copy_range_to = qcow2_co_copy_range_to,
|
||||
.bdrv_truncate = qcow2_truncate,
|
||||
.bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
|
||||
.bdrv_make_empty = qcow2_make_empty,
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
|
||||
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
|
||||
|
||||
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
|
||||
* clusters */
|
||||
#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4
|
||||
|
||||
#define DEFAULT_CLUSTER_SIZE 65536
|
||||
|
||||
|
||||
@@ -294,7 +298,6 @@ typedef struct BDRVQcow2State {
|
||||
uint32_t nb_bitmaps;
|
||||
uint64_t bitmap_directory_size;
|
||||
uint64_t bitmap_directory_offset;
|
||||
bool dirty_bitmaps_loaded;
|
||||
|
||||
int flags;
|
||||
int qcow_version;
|
||||
@@ -668,8 +671,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
void **refcount_table,
|
||||
int64_t *refcount_table_size);
|
||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
Error **errp);
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
||||
|
||||
210
block/qed.c
210
block/qed.c
@@ -13,7 +13,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bswap.h"
|
||||
@@ -21,11 +20,6 @@
|
||||
#include "trace.h"
|
||||
#include "qed.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
static QemuOptsList qed_create_opts;
|
||||
|
||||
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
|
||||
const char *filename)
|
||||
@@ -600,78 +594,43 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
||||
qemu_vfree(s->l1_table);
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
uint64_t image_size, uint32_t table_size,
|
||||
const char *backing_file, const char *backing_fmt,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsQed *qed_opts;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
QEDHeader header;
|
||||
QEDHeader le_header;
|
||||
uint8_t *l1_table = NULL;
|
||||
size_t l1_size;
|
||||
int ret = 0;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_QED);
|
||||
qed_opts = &opts->u.qed;
|
||||
|
||||
/* Validate options and set default values */
|
||||
if (!qed_opts->has_cluster_size) {
|
||||
qed_opts->cluster_size = QED_DEFAULT_CLUSTER_SIZE;
|
||||
}
|
||||
if (!qed_opts->has_table_size) {
|
||||
qed_opts->table_size = QED_DEFAULT_TABLE_SIZE;
|
||||
}
|
||||
|
||||
if (!qed_is_cluster_size_valid(qed_opts->cluster_size)) {
|
||||
error_setg(errp, "QED cluster size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_table_size_valid(qed_opts->table_size)) {
|
||||
error_setg(errp, "QED table size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_image_size_valid(qed_opts->size, qed_opts->cluster_size,
|
||||
qed_opts->table_size))
|
||||
{
|
||||
error_setg(errp, "QED image size must be a non-zero multiple of "
|
||||
"cluster size and less than %" PRIu64 " bytes",
|
||||
qed_max_image_size(qed_opts->cluster_size,
|
||||
qed_opts->table_size));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(qed_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Prepare image format */
|
||||
header = (QEDHeader) {
|
||||
QEDHeader header = {
|
||||
.magic = QED_MAGIC,
|
||||
.cluster_size = qed_opts->cluster_size,
|
||||
.table_size = qed_opts->table_size,
|
||||
.cluster_size = cluster_size,
|
||||
.table_size = table_size,
|
||||
.header_size = 1,
|
||||
.features = 0,
|
||||
.compat_features = 0,
|
||||
.l1_table_offset = qed_opts->cluster_size,
|
||||
.image_size = qed_opts->size,
|
||||
.l1_table_offset = cluster_size,
|
||||
.image_size = image_size,
|
||||
};
|
||||
QEDHeader le_header;
|
||||
uint8_t *l1_table = NULL;
|
||||
size_t l1_size = header.cluster_size * header.table_size;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
BlockBackend *blk;
|
||||
|
||||
l1_size = header.cluster_size * header.table_size;
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* File must start empty and grow, check truncate is supported */
|
||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
@@ -679,16 +638,13 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qed_opts->has_backing_file) {
|
||||
if (backing_file) {
|
||||
header.features |= QED_F_BACKING_FILE;
|
||||
header.backing_filename_offset = sizeof(le_header);
|
||||
header.backing_filename_size = strlen(qed_opts->backing_file);
|
||||
header.backing_filename_size = strlen(backing_file);
|
||||
|
||||
if (qed_opts->has_backing_fmt) {
|
||||
const char *backing_fmt = BlockdevDriver_str(qed_opts->backing_fmt);
|
||||
if (qed_fmt_is_raw(backing_fmt)) {
|
||||
header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
|
||||
}
|
||||
if (qed_fmt_is_raw(backing_fmt)) {
|
||||
header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,7 +653,7 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file,
|
||||
ret = blk_pwrite(blk, sizeof(le_header), backing_file,
|
||||
header.backing_filename_size, 0);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -713,7 +669,6 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
out:
|
||||
g_free(l1_table);
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -721,74 +676,51 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
uint64_t image_size = 0;
|
||||
uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
|
||||
uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
|
||||
char *backing_file = NULL;
|
||||
char *backing_fmt = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
||||
{ BLOCK_OPT_BACKING_FMT, "backing-fmt" },
|
||||
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
||||
{ BLOCK_OPT_TABLE_SIZE, "table-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
|
||||
cluster_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
QED_DEFAULT_CLUSTER_SIZE);
|
||||
table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE,
|
||||
QED_DEFAULT_TABLE_SIZE);
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
if (!qed_is_cluster_size_valid(cluster_size)) {
|
||||
error_setg(errp, "QED cluster size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "qed");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
if (!qed_is_table_size_valid(table_size)) {
|
||||
error_setg(errp, "QED table size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
|
||||
error_setg(errp, "QED image size must be a non-zero multiple of "
|
||||
"cluster size and less than %" PRIu64 " bytes",
|
||||
qed_max_image_size(cluster_size, table_size));
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_QED);
|
||||
create_options->u.qed.size =
|
||||
ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE);
|
||||
ret = qed_create(filename, cluster_size, image_size, table_size,
|
||||
backing_file, backing_fmt, opts, errp);
|
||||
|
||||
/* Create the qed image (format layer) */
|
||||
ret = bdrv_qed_co_create(create_options, errp);
|
||||
|
||||
fail:
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
finish:
|
||||
g_free(backing_file);
|
||||
g_free(backing_fmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1434,9 +1366,8 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs,
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
assert(!flags);
|
||||
return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE);
|
||||
}
|
||||
|
||||
@@ -1671,7 +1602,6 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_close = bdrv_qed_close,
|
||||
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = bdrv_qed_co_create,
|
||||
.bdrv_co_create_opts = bdrv_qed_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = bdrv_qed_co_block_status,
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/option.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-block.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
@@ -116,7 +115,6 @@ struct QuorumAIOCB {
|
||||
/* Request metadata */
|
||||
uint64_t offset;
|
||||
uint64_t bytes;
|
||||
int flags;
|
||||
|
||||
QEMUIOVector *qiov; /* calling IOV */
|
||||
|
||||
@@ -159,8 +157,7 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
||||
QEMUIOVector *qiov,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
int flags)
|
||||
uint64_t bytes)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
|
||||
@@ -171,7 +168,6 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
||||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.flags = flags,
|
||||
.qiov = qiov,
|
||||
.votes.compare = quorum_sha256_compare,
|
||||
.votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list),
|
||||
@@ -275,11 +271,9 @@ static void quorum_rewrite_entry(void *opaque)
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
|
||||
/* Ignore any errors, it's just a correction attempt for already
|
||||
* corrupted data.
|
||||
* Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the
|
||||
* area with different data from the other children. */
|
||||
* corrupted data. */
|
||||
bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes,
|
||||
acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED);
|
||||
acb->qiov, 0);
|
||||
|
||||
/* Wake up the caller after the last rewrite */
|
||||
acb->rewrite_count--;
|
||||
@@ -614,7 +608,7 @@ static void read_quorum_children_entry(void *opaque)
|
||||
static int read_quorum_children(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
acb->children_read = s->num_children;
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
@@ -649,7 +643,9 @@ static int read_quorum_children(QuorumAIOCB *acb)
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
return acb->vote_ret;
|
||||
ret = acb->vote_ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_fifo_child(QuorumAIOCB *acb)
|
||||
@@ -677,7 +673,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags);
|
||||
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes);
|
||||
int ret;
|
||||
|
||||
acb->is_read = true;
|
||||
@@ -703,7 +699,7 @@ static void write_quorum_entry(void *opaque)
|
||||
|
||||
sacb->bs = s->children[i]->bs;
|
||||
sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
|
||||
acb->qiov, acb->flags);
|
||||
acb->qiov, 0);
|
||||
if (sacb->ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
@@ -723,7 +719,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags);
|
||||
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
@@ -965,8 +961,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
s->next_child_index = s->num_children;
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
g_free(opened);
|
||||
goto exit;
|
||||
|
||||
@@ -1088,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
|
||||
children = qlist_new();
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
qlist_append(children,
|
||||
qobject_ref(s->children[i]->bs->full_open_options));
|
||||
QINCREF(s->children[i]->bs->full_open_options);
|
||||
qlist_append(children, s->children[i]->bs->full_open_options);
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
|
||||
@@ -167,37 +167,16 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
/* Check and adjust the offset, against 'offset' and 'size' options. */
|
||||
static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset,
|
||||
uint64_t bytes, bool is_write)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) {
|
||||
/* There's not enough space for the write, or the read request is
|
||||
* out-of-range. Don't read/write anything to prevent leaking out of
|
||||
* the size specified in options. */
|
||||
return is_write ? -ENOSPC : -EINVAL;;
|
||||
}
|
||||
|
||||
if (*offset > INT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
*offset += s->offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
int ret;
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
ret = raw_adjust_offset(bs, &offset, bytes, false);
|
||||
if (ret) {
|
||||
return ret;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
offset += s->offset;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
@@ -207,11 +186,23 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
void *buf = NULL;
|
||||
BlockDriver *drv;
|
||||
QEMUIOVector local_qiov;
|
||||
int ret;
|
||||
|
||||
if (s->has_size && (offset > s->size || bytes > (s->size - offset))) {
|
||||
/* There's not enough space for the data. Don't write anything and just
|
||||
* fail to prevent leaking out of the size specified in options. */
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
|
||||
/* Handling partial writes would be a pain - so we just
|
||||
* require that guests have 512-byte request alignment if
|
||||
@@ -246,10 +237,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
qiov = &local_qiov;
|
||||
}
|
||||
|
||||
ret = raw_adjust_offset(bs, &offset, bytes, true);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
offset += s->offset;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
@@ -279,24 +267,22 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||
if (ret) {
|
||||
return ret;
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
offset += s->offset;
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||
if (ret) {
|
||||
return ret;
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
offset += s->offset;
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
}
|
||||
|
||||
@@ -429,11 +415,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
bs->sg = bs->file->bs->sg;
|
||||
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) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||
bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||
bs->file->bs->supported_zero_flags;
|
||||
|
||||
if (bs->probed && !bdrv_is_read_only(bs)) {
|
||||
fprintf(stderr,
|
||||
@@ -497,36 +482,6 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
|
||||
return bdrv_probe_geometry(bs->file->bs, geo);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs,
|
||||
BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, &src_offset, bytes, false);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset,
|
||||
bytes, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
|
||||
BdrvChild *src, uint64_t src_offset,
|
||||
BdrvChild *dst, uint64_t dst_offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, &dst_offset, bytes, true);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes,
|
||||
flags);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_raw = {
|
||||
.format_name = "raw",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
@@ -543,8 +498,6 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = &raw_co_pdiscard,
|
||||
.bdrv_co_block_status = &raw_co_block_status,
|
||||
.bdrv_co_copy_range_from = &raw_co_copy_range_from,
|
||||
.bdrv_co_copy_range_to = &raw_co_copy_range_to,
|
||||
.bdrv_truncate = &raw_truncate,
|
||||
.bdrv_getlength = &raw_getlength,
|
||||
.has_variable_length = true,
|
||||
|
||||
152
block/rbd.c
152
block/rbd.c
@@ -18,7 +18,6 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "crypto/secret.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
@@ -227,57 +226,27 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
||||
|
||||
done:
|
||||
g_free(buf);
|
||||
qobject_unref(keypairs);
|
||||
QDECREF(keypairs);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
/* XXX Does RBD support AIO on less than 512-byte alignment? */
|
||||
bs->bl.request_alignment = 512;
|
||||
}
|
||||
|
||||
|
||||
static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
|
||||
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
|
||||
Error **errp)
|
||||
{
|
||||
char *key, *acr;
|
||||
int r;
|
||||
GString *accu;
|
||||
RbdAuthModeList *auth;
|
||||
|
||||
if (opts->key_secret) {
|
||||
key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp);
|
||||
if (!key) {
|
||||
return -EIO;
|
||||
}
|
||||
r = rados_conf_set(cluster, "key", key);
|
||||
g_free(key);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "Could not set 'key'");
|
||||
return r;
|
||||
}
|
||||
if (secretid == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts->has_auth_client_required) {
|
||||
accu = g_string_new("");
|
||||
for (auth = opts->auth_client_required; auth; auth = auth->next) {
|
||||
if (accu->str[0]) {
|
||||
g_string_append_c(accu, ';');
|
||||
}
|
||||
g_string_append(accu, RbdAuthMode_str(auth->value));
|
||||
}
|
||||
acr = g_string_free(accu, FALSE);
|
||||
r = rados_conf_set(cluster, "auth_client_required", acr);
|
||||
g_free(acr);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r,
|
||||
"Could not set 'auth_client_required'");
|
||||
return r;
|
||||
}
|
||||
gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
|
||||
errp);
|
||||
if (!secret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rados_conf_set(cluster, "key", secret);
|
||||
g_free(secret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -294,29 +263,29 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
|
||||
if (!keypairs_json) {
|
||||
return ret;
|
||||
}
|
||||
keypairs = qobject_to(QList,
|
||||
qobject_from_json(keypairs_json, &error_abort));
|
||||
keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
|
||||
&error_abort));
|
||||
remaining = qlist_size(keypairs) / 2;
|
||||
assert(remaining);
|
||||
|
||||
while (remaining--) {
|
||||
name = qobject_to(QString, qlist_pop(keypairs));
|
||||
value = qobject_to(QString, qlist_pop(keypairs));
|
||||
name = qobject_to_qstring(qlist_pop(keypairs));
|
||||
value = qobject_to_qstring(qlist_pop(keypairs));
|
||||
assert(name && value);
|
||||
key = qstring_get_str(name);
|
||||
|
||||
ret = rados_conf_set(cluster, key, qstring_get_str(value));
|
||||
qobject_unref(value);
|
||||
QDECREF(value);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "invalid conf option %s", key);
|
||||
qobject_unref(name);
|
||||
QDECREF(name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
qobject_unref(name);
|
||||
QDECREF(name);
|
||||
}
|
||||
|
||||
qobject_unref(keypairs);
|
||||
QDECREF(keypairs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -368,7 +337,9 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
/* FIXME Deprecate and remove keypairs or make it available in QMP. */
|
||||
/* FIXME Deprecate and remove keypairs or make it available in QMP.
|
||||
* password_secret should eventually be configurable in opts->location. Support
|
||||
* for it in .bdrv_open will make it work here as well. */
|
||||
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
|
||||
const char *keypairs, const char *password_secret,
|
||||
Error **errp)
|
||||
@@ -478,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
||||
}
|
||||
|
||||
exit:
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
@@ -574,16 +545,6 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
||||
Error *local_err = NULL;
|
||||
int r;
|
||||
|
||||
if (secretid) {
|
||||
if (opts->key_secret) {
|
||||
error_setg(errp,
|
||||
"Legacy 'password-secret' clashes with 'key-secret'");
|
||||
return -EINVAL;
|
||||
}
|
||||
opts->key_secret = g_strdup(secretid);
|
||||
opts->has_key_secret = true;
|
||||
}
|
||||
|
||||
mon_host = qemu_rbd_mon_host(opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -616,8 +577,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
r = qemu_rbd_set_auth(*cluster, opts, errp);
|
||||
if (r < 0) {
|
||||
if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
|
||||
r = -EIO;
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
@@ -661,11 +622,27 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
BlockdevOptionsRbd *opts = NULL;
|
||||
Visitor *v;
|
||||
const QDictEntry *e;
|
||||
QObject *crumpled = NULL;
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
char *keypairs, *secretid;
|
||||
int r;
|
||||
|
||||
/* If we are given a filename, parse the filename, with precedence given to
|
||||
* filename encoded options */
|
||||
filename = qdict_get_try_str(options, "filename");
|
||||
if (filename) {
|
||||
warn_report("'filename' option specified. "
|
||||
"This is an unsupported option, and may be deprecated "
|
||||
"in the future");
|
||||
qemu_rbd_parse_filename(filename, options, &local_err);
|
||||
qdict_del(options, "filename");
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
|
||||
if (keypairs) {
|
||||
qdict_del(options, "=keyvalue-pairs");
|
||||
@@ -677,14 +654,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Convert the remaining options into a QAPI object */
|
||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
||||
if (!v) {
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -692,12 +671,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Remove the processed options from the QDict (the visitor processes
|
||||
* _all_ options in the QDict) */
|
||||
while ((e = qdict_first(options))) {
|
||||
qdict_del(options, e->key);
|
||||
}
|
||||
|
||||
r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
|
||||
!(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
|
||||
if (r < 0) {
|
||||
@@ -919,23 +892,27 @@ failed:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
|
||||
return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
(int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
|
||||
RBD_AIO_READ);
|
||||
}
|
||||
|
||||
static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
|
||||
return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
(int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
|
||||
RBD_AIO_WRITE);
|
||||
}
|
||||
|
||||
@@ -1174,7 +1151,6 @@ static BlockDriver bdrv_rbd = {
|
||||
.format_name = "rbd",
|
||||
.instance_size = sizeof(BDRVRBDState),
|
||||
.bdrv_parse_filename = qemu_rbd_parse_filename,
|
||||
.bdrv_refresh_limits = qemu_rbd_refresh_limits,
|
||||
.bdrv_file_open = qemu_rbd_open,
|
||||
.bdrv_close = qemu_rbd_close,
|
||||
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
|
||||
@@ -1187,8 +1163,8 @@ static BlockDriver bdrv_rbd = {
|
||||
.bdrv_truncate = qemu_rbd_truncate,
|
||||
.protocol_name = "rbd",
|
||||
|
||||
.bdrv_aio_preadv = qemu_rbd_aio_preadv,
|
||||
.bdrv_aio_pwritev = qemu_rbd_aio_pwritev,
|
||||
.bdrv_aio_readv = qemu_rbd_aio_readv,
|
||||
.bdrv_aio_writev = qemu_rbd_aio_writev,
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
|
||||
.bdrv_aio_flush = qemu_rbd_aio_flush,
|
||||
|
||||
@@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs)
|
||||
replication_stop(s->rs, false, NULL);
|
||||
}
|
||||
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
|
||||
job_cancel_sync(&s->active_disk->bs->job->job);
|
||||
block_job_cancel_sync(s->active_disk->bs->job);
|
||||
}
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
@@ -260,8 +260,7 @@ out:
|
||||
static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int remaining_sectors,
|
||||
QEMUIOVector *qiov,
|
||||
int flags)
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
QEMUIOVector hd_qiov;
|
||||
@@ -272,7 +271,6 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
||||
int ret;
|
||||
int64_t n;
|
||||
|
||||
assert(!flags);
|
||||
ret = replication_get_io_status(s);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -568,7 +566,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
||||
0, MIRROR_SYNC_MODE_NONE, NULL, false,
|
||||
BLOCKDEV_ON_ERROR_REPORT,
|
||||
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
||||
BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
|
||||
backup_job_completed, bs, NULL, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -576,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
job_start(&job->job);
|
||||
block_job_start(job);
|
||||
break;
|
||||
default:
|
||||
aio_context_release(aio_context);
|
||||
@@ -681,7 +679,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||
* disk, secondary disk in backup_job_completed().
|
||||
*/
|
||||
if (s->secondary_disk->bs->job) {
|
||||
job_cancel_sync(&s->secondary_disk->bs->job->job);
|
||||
block_job_cancel_sync(s->secondary_disk->bs->job);
|
||||
}
|
||||
|
||||
if (!failover) {
|
||||
@@ -693,7 +691,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||
|
||||
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
|
||||
JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||
NULL, replication_done, bs, true, errp);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/cutils.h"
|
||||
@@ -539,17 +538,27 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
|
||||
static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
||||
{
|
||||
QDict *server = NULL;
|
||||
QObject *crumpled_server = NULL;
|
||||
Visitor *iv = NULL;
|
||||
SocketAddress *saddr = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
qdict_extract_subqdict(options, &server, "server.");
|
||||
|
||||
iv = qobject_input_visitor_new_flat_confused(server, errp);
|
||||
if (!iv) {
|
||||
crumpled_server = qdict_crumple(server, errp);
|
||||
if (!crumpled_server) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
|
||||
* server.type=inet. .to doesn't matter, it's ignored anyway.
|
||||
* That's because when @options come from -blockdev or
|
||||
* blockdev_add, members are typed according to the QAPI schema,
|
||||
* but when they come from -drive, they're all QString. The
|
||||
* visitor expects the former.
|
||||
*/
|
||||
iv = qobject_input_visitor_new(crumpled_server);
|
||||
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -558,7 +567,8 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
||||
|
||||
done:
|
||||
visit_free(iv);
|
||||
qobject_unref(server);
|
||||
qobject_decref(crumpled_server);
|
||||
QDECREF(server);
|
||||
return saddr;
|
||||
}
|
||||
|
||||
@@ -1026,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
|
||||
|
||||
cfg->uri = uri = uri_parse(filename);
|
||||
if (!uri) {
|
||||
error_setg(&err, "invalid URI '%s'", filename);
|
||||
error_setg(&err, "invalid URI");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1849,7 +1859,9 @@ out:
|
||||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||
}
|
||||
out_with_err_set:
|
||||
blk_unref(blk);
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
}
|
||||
g_free(buf);
|
||||
|
||||
return ret;
|
||||
@@ -1871,11 +1883,11 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
qobject_unref(obj);
|
||||
qobject_decref(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qdict = qobject_to(QDict, obj);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
qdict_flatten(qdict);
|
||||
|
||||
qdict_put_str(qdict, "driver", "sheepdog");
|
||||
@@ -1889,7 +1901,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
||||
ret = sd_prealloc(bs, 0, size, errp);
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
qobject_unref(qdict);
|
||||
QDECREF(qdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1975,7 +1987,6 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt)
|
||||
} else {
|
||||
ret = qemu_strtol(n2, NULL, 10, &parity);
|
||||
if (ret < 0) {
|
||||
g_free(redundancy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2170,8 +2181,9 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict, *location_qdict;
|
||||
QObject *crumpled;
|
||||
Visitor *v;
|
||||
char *redundancy;
|
||||
const char *redundancy;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
@@ -2205,14 +2217,16 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
}
|
||||
|
||||
/* Get the QAPI object */
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
crumpled = qdict_crumple(qdict, errp);
|
||||
if (crumpled == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -2238,8 +2252,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
ret = sd_co_create(create_options, errp);
|
||||
fail:
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
qobject_unref(qdict);
|
||||
g_free(redundancy);
|
||||
QDECREF(qdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2322,7 +2335,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
|
||||
/* we don't need to update entire object */
|
||||
datalen = SD_INODE_HEADER_SIZE;
|
||||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||
s->inode.vdi_size = offset;
|
||||
ret = write_object(fd, s->bs, (char *)&s->inode,
|
||||
vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
|
||||
@@ -2599,15 +2612,13 @@ static void sd_aio_complete(SheepdogAIOCB *acb)
|
||||
}
|
||||
|
||||
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
SheepdogAIOCB acb;
|
||||
int ret;
|
||||
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
assert(!flags);
|
||||
if (offset > s->inode.vdi_size) {
|
||||
ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
|
||||
if (ret < 0) {
|
||||
@@ -2690,7 +2701,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
*/
|
||||
strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
|
||||
/* we don't need to update entire object */
|
||||
datalen = SD_INODE_HEADER_SIZE;
|
||||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||
inode = g_malloc(datalen);
|
||||
|
||||
/* refresh inode. */
|
||||
@@ -2925,14 +2936,13 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
QEMUSnapshotInfo *sn_tab = NULL;
|
||||
unsigned wlen, rlen;
|
||||
int found = 0;
|
||||
SheepdogInode *inode;
|
||||
static SheepdogInode inode;
|
||||
unsigned long *vdi_inuse;
|
||||
unsigned int start_nr;
|
||||
uint64_t hval;
|
||||
uint32_t vid;
|
||||
|
||||
vdi_inuse = g_malloc(max);
|
||||
inode = g_malloc(SD_INODE_HEADER_SIZE);
|
||||
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
@@ -2975,26 +2985,26 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
}
|
||||
|
||||
/* we don't need to read entire object */
|
||||
ret = read_object(fd, s->bs, (char *)inode,
|
||||
ret = read_object(fd, s->bs, (char *)&inode,
|
||||
vid_to_vdi_oid(vid),
|
||||
0, SD_INODE_HEADER_SIZE, 0,
|
||||
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
|
||||
s->cache_flags);
|
||||
|
||||
if (ret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(inode->name, s->name) && is_snapshot(inode)) {
|
||||
sn_tab[found].date_sec = inode->snap_ctime >> 32;
|
||||
sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff;
|
||||
sn_tab[found].vm_state_size = inode->vm_state_size;
|
||||
sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec;
|
||||
if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) {
|
||||
sn_tab[found].date_sec = inode.snap_ctime >> 32;
|
||||
sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff;
|
||||
sn_tab[found].vm_state_size = inode.vm_state_size;
|
||||
sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec;
|
||||
|
||||
snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str),
|
||||
"%" PRIu32, inode->snap_id);
|
||||
"%" PRIu32, inode.snap_id);
|
||||
pstrcpy(sn_tab[found].name,
|
||||
MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)),
|
||||
inode->tag);
|
||||
MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)),
|
||||
inode.tag);
|
||||
found++;
|
||||
}
|
||||
}
|
||||
@@ -3004,7 +3014,6 @@ out:
|
||||
*psn_tab = sn_tab;
|
||||
|
||||
g_free(vdi_inuse);
|
||||
g_free(inode);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/snapshot.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -215,7 +214,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
bdrv_ref(file);
|
||||
|
||||
qdict_extract_subqdict(options, &file_options, "file.");
|
||||
qobject_unref(file_options);
|
||||
QDECREF(file_options);
|
||||
qdict_put_str(options, "file", bdrv_get_node_name(file));
|
||||
|
||||
drv->bdrv_close(bs);
|
||||
@@ -224,7 +223,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
|
||||
ret = bdrv_snapshot_goto(file, snapshot_id, errp);
|
||||
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
if (open_ret < 0) {
|
||||
bdrv_unref(file);
|
||||
bs->drv = NULL;
|
||||
|
||||
22
block/ssh.c
22
block/ssh.c
@@ -28,7 +28,6 @@
|
||||
#include <libssh2_sftp.h>
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -606,6 +605,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
||||
BlockdevOptionsSsh *result = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
QObject *crumpled;
|
||||
const QDictEntry *e;
|
||||
Visitor *v;
|
||||
|
||||
@@ -622,13 +622,23 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
||||
}
|
||||
|
||||
/* Create the QAPI object */
|
||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
||||
if (!v) {
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive.
|
||||
* .to doesn't matter, it's ignored anyway.
|
||||
* That's because when @options come from -blockdev or
|
||||
* blockdev_add, members are typed according to the QAPI schema,
|
||||
* but when they come from -drive, they're all QString. The
|
||||
* visitor expects the former.
|
||||
*/
|
||||
v = qobject_input_visitor_new(crumpled);
|
||||
visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -907,7 +917,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
ret = ssh_co_create(create_options, errp);
|
||||
|
||||
out:
|
||||
qobject_unref(uri_options);
|
||||
QDECREF(uri_options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
@@ -1154,13 +1164,11 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
||||
|
||||
static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
assert(!flags);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE, qiov);
|
||||
|
||||
@@ -29,8 +29,11 @@ enum {
|
||||
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct StreamBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *base;
|
||||
BlockdevOnError on_error;
|
||||
char *backing_file_str;
|
||||
@@ -58,16 +61,16 @@ typedef struct {
|
||||
int ret;
|
||||
} StreamCompleteData;
|
||||
|
||||
static void stream_complete(Job *job, void *opaque)
|
||||
static void stream_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
|
||||
StreamCompleteData *data = opaque;
|
||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
||||
BlockDriverState *bs = blk_bs(job->blk);
|
||||
BlockDriverState *base = s->base;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
|
||||
if (!block_job_is_cancelled(&s->common) && bs->backing &&
|
||||
data->ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
base_id = s->backing_file_str;
|
||||
@@ -88,12 +91,12 @@ out:
|
||||
/* Reopen the image back in read-only mode if necessary */
|
||||
if (s->bs_flags != bdrv_get_flags(bs)) {
|
||||
/* Give up write permissions before making it read-only */
|
||||
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
|
||||
blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
|
||||
bdrv_reopen(bs, s->bs_flags, NULL);
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
job_completed(job, data->ret, NULL);
|
||||
block_job_completed(&s->common, data->ret);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
@@ -104,7 +107,6 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
BlockBackend *blk = s->common.blk;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t len;
|
||||
int64_t offset = 0;
|
||||
uint64_t delay_ns = 0;
|
||||
int error = 0;
|
||||
@@ -116,12 +118,11 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = bdrv_getlength(bs);
|
||||
if (len < 0) {
|
||||
ret = len;
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
ret = s->common.len;
|
||||
goto out;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
|
||||
|
||||
@@ -134,14 +135,14 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
for ( ; offset < len; offset += n) {
|
||||
for ( ; offset < s->common.len; offset += n) {
|
||||
bool copy;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
job_sleep_ns(&s->common.job, delay_ns);
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -158,7 +159,7 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
|
||||
/* Finish early if end of backing file has been reached */
|
||||
if (ret == 0 && n == 0) {
|
||||
n = len - offset;
|
||||
n = s->common.len - offset;
|
||||
}
|
||||
|
||||
copy = (ret == 1);
|
||||
@@ -184,11 +185,9 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
ret = 0;
|
||||
|
||||
/* Publish progress */
|
||||
job_progress_update(&s->common.job, n);
|
||||
if (copy) {
|
||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
s->common.offset += n;
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,18 +204,25 @@ out:
|
||||
/* Modify backing chain and close BDSes in main loop */
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
job_defer_to_main_loop(&s->common.job, stream_complete, data);
|
||||
block_job_defer_to_main_loop(&s->common, stream_complete, data);
|
||||
}
|
||||
|
||||
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobDriver stream_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = JOB_TYPE_STREAM,
|
||||
.free = block_job_free,
|
||||
.start = stream_run,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
},
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_STREAM,
|
||||
.set_speed = stream_set_speed,
|
||||
.start = stream_run,
|
||||
};
|
||||
|
||||
void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
@@ -238,12 +244,12 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
/* Prevent concurrent jobs trying to modify the graph structure here, we
|
||||
* already have our own plans. Also don't allow resize as the image size is
|
||||
* queried only at the job start and then cached. */
|
||||
s = block_job_create(job_id, &stream_job_driver, NULL, bs,
|
||||
s = block_job_create(job_id, &stream_job_driver, bs,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_GRAPH_MOD,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_WRITE,
|
||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
||||
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -264,7 +270,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
|
||||
s->on_error = on_error;
|
||||
trace_stream_start(bs, base, s);
|
||||
job_start(&s->common.job);
|
||||
block_job_start(&s->common);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
||||
@@ -36,12 +36,9 @@ static QemuOptsList throttle_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* If this function succeeds then the throttle group name is stored in
|
||||
* @group and must be freed by the caller.
|
||||
* If there's an error then @group remains unmodified.
|
||||
*/
|
||||
static int throttle_parse_options(QDict *options, char **group, Error **errp)
|
||||
static int throttle_configure_tgm(BlockDriverState *bs,
|
||||
ThrottleGroupMember *tgm,
|
||||
QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
const char *group_name;
|
||||
@@ -66,7 +63,8 @@ static int throttle_parse_options(QDict *options, char **group, Error **errp)
|
||||
goto fin;
|
||||
}
|
||||
|
||||
*group = g_strdup(group_name);
|
||||
/* Register membership to group with name group_name */
|
||||
throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
|
||||
ret = 0;
|
||||
fin:
|
||||
qemu_opts_del(opts);
|
||||
@@ -77,27 +75,16 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
char *group;
|
||||
int ret;
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs,
|
||||
&child_file, false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
bs->supported_write_flags = bs->file->bs->supported_write_flags |
|
||||
BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = bs->file->bs->supported_zero_flags |
|
||||
BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_write_flags = bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
|
||||
|
||||
ret = throttle_parse_options(options, &group, errp);
|
||||
if (ret == 0) {
|
||||
/* Register membership to group with name group_name */
|
||||
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
||||
g_free(group);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return throttle_configure_tgm(bs, tgm, options, errp);
|
||||
}
|
||||
|
||||
static void throttle_close(BlockDriverState *bs)
|
||||
@@ -173,36 +160,35 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
|
||||
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char *group = NULL;
|
||||
ThrottleGroupMember *tgm;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
assert(reopen_state->bs != NULL);
|
||||
|
||||
ret = throttle_parse_options(reopen_state->options, &group, errp);
|
||||
reopen_state->opaque = group;
|
||||
return ret;
|
||||
reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
|
||||
tgm = reopen_state->opaque;
|
||||
|
||||
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
|
||||
errp);
|
||||
}
|
||||
|
||||
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
|
||||
{
|
||||
BlockDriverState *bs = reopen_state->bs;
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
char *group = reopen_state->opaque;
|
||||
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
|
||||
ThrottleGroupMember *new_tgm = reopen_state->opaque;
|
||||
|
||||
assert(group);
|
||||
|
||||
if (strcmp(group, throttle_group_get_name(tgm))) {
|
||||
throttle_group_unregister_tgm(tgm);
|
||||
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
||||
}
|
||||
g_free(reopen_state->opaque);
|
||||
throttle_group_unregister_tgm(old_tgm);
|
||||
g_free(old_tgm);
|
||||
reopen_state->bs->opaque = new_tgm;
|
||||
reopen_state->opaque = NULL;
|
||||
}
|
||||
|
||||
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
|
||||
{
|
||||
g_free(reopen_state->opaque);
|
||||
ThrottleGroupMember *tgm = reopen_state->opaque;
|
||||
|
||||
throttle_group_unregister_tgm(tgm);
|
||||
g_free(tgm);
|
||||
reopen_state->opaque = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
|
||||
# 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"
|
||||
|
||||
# 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"
|
||||
@@ -48,8 +46,6 @@ qmp_block_job_cancel(void *job) "job %p"
|
||||
qmp_block_job_pause(void *job) "job %p"
|
||||
qmp_block_job_resume(void *job) "job %p"
|
||||
qmp_block_job_complete(void *job) "job %p"
|
||||
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"
|
||||
|
||||
# block/file-win32.c
|
||||
|
||||
196
block/vdi.c
196
block/vdi.c
@@ -51,10 +51,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -143,8 +140,6 @@
|
||||
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
|
||||
(uint64_t)DEFAULT_CLUSTER_SIZE)
|
||||
|
||||
static QemuOptsList vdi_create_opts;
|
||||
|
||||
typedef struct {
|
||||
char text[0x40];
|
||||
uint32_t signature;
|
||||
@@ -235,6 +230,7 @@ static void vdi_header_to_le(VdiHeader *header)
|
||||
qemu_uuid_bswap(&header->uuid_parent);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_VDI_DEBUG)
|
||||
static void vdi_header_print(VdiHeader *header)
|
||||
{
|
||||
char uuid[37];
|
||||
@@ -256,15 +252,16 @@ static void vdi_header_print(VdiHeader *header)
|
||||
logout("block extra 0x%04x\n", header->block_extra);
|
||||
logout("blocks tot. 0x%04x\n", header->blocks_in_image);
|
||||
logout("blocks all. 0x%04x\n", header->blocks_allocated);
|
||||
qemu_uuid_unparse(&header->uuid_image, uuid);
|
||||
uuid_unparse(header->uuid_image, uuid);
|
||||
logout("uuid image %s\n", uuid);
|
||||
qemu_uuid_unparse(&header->uuid_last_snap, uuid);
|
||||
uuid_unparse(header->uuid_last_snap, uuid);
|
||||
logout("uuid snap %s\n", uuid);
|
||||
qemu_uuid_unparse(&header->uuid_link, uuid);
|
||||
uuid_unparse(header->uuid_link, uuid);
|
||||
logout("uuid link %s\n", uuid);
|
||||
qemu_uuid_unparse(&header->uuid_parent, uuid);
|
||||
uuid_unparse(header->uuid_parent, uuid);
|
||||
logout("uuid parent %s\n", uuid);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
@@ -385,9 +382,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
vdi_header_to_cpu(&header);
|
||||
if (VDI_DEBUG) {
|
||||
vdi_header_print(&header);
|
||||
}
|
||||
#if defined(CONFIG_VDI_DEBUG)
|
||||
vdi_header_print(&header);
|
||||
#endif
|
||||
|
||||
if (header.disk_size > VDI_DISK_SIZE_MAX) {
|
||||
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||
@@ -719,59 +716,36 @@ nonallocating_write:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
size_t block_size, Error **errp)
|
||||
static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsVdi *vdi_opts;
|
||||
int ret = 0;
|
||||
uint64_t bytes = 0;
|
||||
uint32_t blocks;
|
||||
uint32_t image_type;
|
||||
size_t block_size = DEFAULT_CLUSTER_SIZE;
|
||||
uint32_t image_type = VDI_TYPE_DYNAMIC;
|
||||
VdiHeader header;
|
||||
size_t i;
|
||||
size_t bmap_size;
|
||||
int64_t offset = 0;
|
||||
BlockDriverState *bs_file = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
uint32_t *bmap = NULL;
|
||||
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
|
||||
vdi_opts = &create_options->u.vdi;
|
||||
|
||||
logout("\n");
|
||||
|
||||
/* Validate options and set default values */
|
||||
bytes = vdi_opts->size;
|
||||
|
||||
if (!vdi_opts->has_preallocation) {
|
||||
vdi_opts->preallocation = PREALLOC_MODE_OFF;
|
||||
}
|
||||
switch (vdi_opts->preallocation) {
|
||||
case PREALLOC_MODE_OFF:
|
||||
image_type = VDI_TYPE_DYNAMIC;
|
||||
break;
|
||||
case PREALLOC_MODE_METADATA:
|
||||
image_type = VDI_TYPE_STATIC;
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Preallocation mode not supported for vdi");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_VDI_STATIC_IMAGE
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
ret = -ENOTSUP;
|
||||
error_setg(errp, "Statically allocated images cannot be created in "
|
||||
"this build");
|
||||
goto exit;
|
||||
}
|
||||
/* Read out options. */
|
||||
bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||
/* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
|
||||
block_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
#endif
|
||||
#ifndef CONFIG_VDI_BLOCK_SIZE
|
||||
if (block_size != DEFAULT_CLUSTER_SIZE) {
|
||||
ret = -ENOTSUP;
|
||||
error_setg(errp,
|
||||
"A non-default cluster size is not supported in this build");
|
||||
goto exit;
|
||||
#if defined(CONFIG_VDI_STATIC_IMAGE)
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
|
||||
image_type = VDI_TYPE_STATIC;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -783,16 +757,18 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp);
|
||||
if (!bs_file) {
|
||||
ret = -EIO;
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs_file, errp);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -823,13 +799,13 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
qemu_uuid_generate(&header.uuid_image);
|
||||
qemu_uuid_generate(&header.uuid_last_snap);
|
||||
/* There is no need to set header.uuid_link or header.uuid_parent here. */
|
||||
if (VDI_DEBUG) {
|
||||
vdi_header_print(&header);
|
||||
}
|
||||
#if defined(CONFIG_VDI_DEBUG)
|
||||
vdi_header_print(&header);
|
||||
#endif
|
||||
vdi_header_to_le(&header);
|
||||
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing header");
|
||||
error_setg(errp, "Error writing header to %s", filename);
|
||||
goto exit;
|
||||
}
|
||||
offset += sizeof(header);
|
||||
@@ -850,7 +826,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
}
|
||||
ret = blk_pwrite(blk, offset, bmap, bmap_size, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing bmap");
|
||||
error_setg(errp, "Error writing bmap to %s", filename);
|
||||
goto exit;
|
||||
}
|
||||
offset += bmap_size;
|
||||
@@ -860,108 +836,17 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
ret = blk_truncate(blk, offset + blocks * block_size,
|
||||
PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
error_prepend(errp, "Failed to statically allocate file");
|
||||
error_prepend(errp, "Failed to statically allocate %s", filename);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs_file);
|
||||
g_free(bmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options,
|
||||
Error **errp)
|
||||
{
|
||||
return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
QDict *qdict = NULL;
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
BlockDriverState *bs_file = NULL;
|
||||
uint64_t block_size = DEFAULT_CLUSTER_SIZE;
|
||||
bool is_static = false;
|
||||
Visitor *v;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/* Parse options and convert legacy syntax.
|
||||
*
|
||||
* Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
|
||||
* cluster-size is not part of the QAPI schema; therefore we have
|
||||
* to parse it before creating the QAPI object. */
|
||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||
block_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
if (block_size < BDRV_SECTOR_SIZE || block_size > UINT32_MAX ||
|
||||
!is_power_of_2(block_size))
|
||||
{
|
||||
error_setg(errp, "Invalid cluster size");
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
|
||||
is_static = true;
|
||||
}
|
||||
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
bs_file = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (!bs_file) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
qdict_put_str(qdict, "driver", "vdi");
|
||||
qdict_put_str(qdict, "file", bs_file->node_name);
|
||||
if (is_static) {
|
||||
qdict_put_str(qdict, "preallocation", "metadata");
|
||||
}
|
||||
|
||||
/* Get the QAPI object */
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
|
||||
create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size,
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the vdi image (format layer) */
|
||||
ret = vdi_co_do_create(create_options, block_size, errp);
|
||||
done:
|
||||
qobject_unref(qdict);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
bdrv_unref(bs_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vdi_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
@@ -1010,7 +895,6 @@ static BlockDriver bdrv_vdi = {
|
||||
.bdrv_close = vdi_close,
|
||||
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = vdi_co_create,
|
||||
.bdrv_co_create_opts = vdi_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = vdi_co_block_status,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "vhdx.h"
|
||||
#include "block/vhdx.h"
|
||||
|
||||
/*
|
||||
* All the VHDX formats on disk are little endian - the following
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "vhdx.h"
|
||||
#include "block/vhdx.h"
|
||||
|
||||
|
||||
typedef struct VHDXLogSequence {
|
||||
|
||||
230
block/vhdx.c
230
block/vhdx.c
@@ -18,18 +18,14 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "vhdx.h"
|
||||
#include "block/vhdx.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
/* Options for VHDX creation */
|
||||
|
||||
@@ -43,8 +39,6 @@ typedef enum VHDXImageType {
|
||||
VHDX_TYPE_DIFFERENCING, /* Currently unsupported */
|
||||
} VHDXImageType;
|
||||
|
||||
static QemuOptsList vhdx_create_opts;
|
||||
|
||||
/* Several metadata and region table data entries are identified by
|
||||
* guids in a MS-specific GUID format. */
|
||||
|
||||
@@ -1227,8 +1221,7 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
}
|
||||
|
||||
static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int ret = -ENOTSUP;
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
@@ -1244,7 +1237,6 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
uint64_t bat_prior_offset = 0;
|
||||
bool bat_update = false;
|
||||
|
||||
assert(!flags);
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
@@ -1800,75 +1792,59 @@ exit:
|
||||
* .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
|
||||
* 1MB
|
||||
*/
|
||||
static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsVhdx *vhdx_opts;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
int ret = 0;
|
||||
uint64_t image_size;
|
||||
uint32_t log_size;
|
||||
uint32_t block_size;
|
||||
uint64_t image_size = (uint64_t) 2 * GiB;
|
||||
uint32_t log_size = 1 * MiB;
|
||||
uint32_t block_size = 0;
|
||||
uint64_t signature;
|
||||
uint64_t metadata_offset;
|
||||
bool use_zero_blocks = false;
|
||||
|
||||
gunichar2 *creator = NULL;
|
||||
glong creator_items;
|
||||
BlockBackend *blk;
|
||||
char *type = NULL;
|
||||
VHDXImageType image_type;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_VHDX);
|
||||
vhdx_opts = &opts->u.vhdx;
|
||||
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0);
|
||||
block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0);
|
||||
type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true);
|
||||
|
||||
/* Validate options and set default values */
|
||||
image_size = vhdx_opts->size;
|
||||
if (image_size > VHDX_MAX_IMAGE_SIZE) {
|
||||
error_setg(errp, "Image size too large; max of 64TB");
|
||||
return -EINVAL;
|
||||
error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!vhdx_opts->has_log_size) {
|
||||
log_size = DEFAULT_LOG_SIZE;
|
||||
} else {
|
||||
if (vhdx_opts->log_size > UINT32_MAX) {
|
||||
error_setg(errp, "Log size must be smaller than 4 GB");
|
||||
return -EINVAL;
|
||||
}
|
||||
log_size = vhdx_opts->log_size;
|
||||
}
|
||||
if (log_size < MiB || (log_size % MiB) != 0) {
|
||||
error_setg(errp, "Log size must be a multiple of 1 MB");
|
||||
return -EINVAL;
|
||||
if (type == NULL) {
|
||||
type = g_strdup("dynamic");
|
||||
}
|
||||
|
||||
if (!vhdx_opts->has_block_state_zero) {
|
||||
use_zero_blocks = true;
|
||||
} else {
|
||||
use_zero_blocks = vhdx_opts->block_state_zero;
|
||||
}
|
||||
|
||||
if (!vhdx_opts->has_subformat) {
|
||||
vhdx_opts->subformat = BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC;
|
||||
}
|
||||
|
||||
switch (vhdx_opts->subformat) {
|
||||
case BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC:
|
||||
if (!strcmp(type, "dynamic")) {
|
||||
image_type = VHDX_TYPE_DYNAMIC;
|
||||
break;
|
||||
case BLOCKDEV_VHDX_SUBFORMAT_FIXED:
|
||||
} else if (!strcmp(type, "fixed")) {
|
||||
image_type = VHDX_TYPE_FIXED;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
} else if (!strcmp(type, "differencing")) {
|
||||
error_setg_errno(errp, ENOTSUP,
|
||||
"Differencing files not yet supported");
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
} else {
|
||||
error_setg(errp, "Invalid subformat '%s'", type);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* These are pretty arbitrary, and mainly designed to keep the BAT
|
||||
* size reasonable to load into RAM */
|
||||
if (vhdx_opts->has_block_size) {
|
||||
block_size = vhdx_opts->block_size;
|
||||
} else {
|
||||
if (block_size == 0) {
|
||||
if (image_size > 32 * TiB) {
|
||||
block_size = 64 * MiB;
|
||||
} else if (image_size > (uint64_t) 100 * GiB) {
|
||||
@@ -1880,30 +1856,30 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
}
|
||||
}
|
||||
|
||||
if (block_size < MiB || (block_size % MiB) != 0) {
|
||||
error_setg(errp, "Block size must be a multiple of 1 MB");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!is_power_of_2(block_size)) {
|
||||
error_setg(errp, "Block size must be a power of two");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||
error_setg(errp, "Block size must not exceed %d", VHDX_BLOCK_SIZE_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(vhdx_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
/* make the log size close to what was specified, but must be
|
||||
* min 1MB, and multiple of 1MB */
|
||||
log_size = ROUND_UP(log_size, MiB);
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
block_size = ROUND_UP(block_size, MiB);
|
||||
block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
|
||||
block_size;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Create (A) */
|
||||
@@ -1952,108 +1928,15 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
goto delete_and_exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
delete_and_exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
exit:
|
||||
g_free(type);
|
||||
g_free(creator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vhdx_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ VHDX_BLOCK_OPT_LOG_SIZE, "log-size" },
|
||||
{ VHDX_BLOCK_OPT_BLOCK_SIZE, "block-size" },
|
||||
{ VHDX_BLOCK_OPT_ZERO, "block-state-zero" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vhdx_create_opts, true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "vhdx");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Silently round up sizes:
|
||||
* The image size is rounded to 512 bytes. Make the block and log size
|
||||
* close to what was specified, but must be at least 1MB, and a multiple of
|
||||
* 1 MB. Also respect VHDX_BLOCK_SIZE_MAX for block sizes. block_size = 0
|
||||
* means auto, which is represented by a missing key in QAPI. */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VHDX);
|
||||
create_options->u.vhdx.size =
|
||||
ROUND_UP(create_options->u.vhdx.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (create_options->u.vhdx.has_log_size) {
|
||||
create_options->u.vhdx.log_size =
|
||||
ROUND_UP(create_options->u.vhdx.log_size, MiB);
|
||||
}
|
||||
if (create_options->u.vhdx.has_block_size) {
|
||||
create_options->u.vhdx.block_size =
|
||||
ROUND_UP(create_options->u.vhdx.block_size, MiB);
|
||||
|
||||
if (create_options->u.vhdx.block_size == 0) {
|
||||
create_options->u.vhdx.has_block_size = false;
|
||||
}
|
||||
if (create_options->u.vhdx.block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||
create_options->u.vhdx.block_size = VHDX_BLOCK_SIZE_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the vhdx image (format layer) */
|
||||
ret = vhdx_co_create(create_options, errp);
|
||||
|
||||
fail:
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If opened r/w, the VHDX driver will automatically replay the log,
|
||||
* if one is present, inside the vhdx_open() call.
|
||||
*
|
||||
@@ -2122,7 +2005,6 @@ static BlockDriver bdrv_vhdx = {
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_readv = vhdx_co_readv,
|
||||
.bdrv_co_writev = vhdx_co_writev,
|
||||
.bdrv_co_create = vhdx_co_create,
|
||||
.bdrv_co_create_opts = vhdx_co_create_opts,
|
||||
.bdrv_get_info = vhdx_get_info,
|
||||
.bdrv_co_check = vhdx_co_check,
|
||||
|
||||
@@ -47,8 +47,6 @@
|
||||
#define VMDK4_FLAG_MARKER (1 << 17)
|
||||
#define VMDK4_GD_AT_END 0xffffffffffffffffULL
|
||||
|
||||
#define VMDK_EXTENT_MAX_SECTORS (1ULL << 32)
|
||||
|
||||
#define VMDK_GTE_ZEROED 0x1
|
||||
|
||||
/* VMDK internal error codes */
|
||||
@@ -1252,10 +1250,6 @@ static int get_cluster_offset(BlockDriverState *bs,
|
||||
return zeroed ? VMDK_ZEROED : VMDK_UNALLOC;
|
||||
}
|
||||
|
||||
if (extent->next_cluster_sector >= VMDK_EXTENT_MAX_SECTORS) {
|
||||
return VMDK_ERROR;
|
||||
}
|
||||
|
||||
cluster_sector = extent->next_cluster_sector;
|
||||
extent->next_cluster_sector += extent->cluster_sectors;
|
||||
|
||||
|
||||
238
block/vpc.c
238
block/vpc.c
@@ -26,16 +26,12 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -170,8 +166,6 @@ static QemuOptsList vpc_runtime_opts = {
|
||||
}
|
||||
};
|
||||
|
||||
static QemuOptsList vpc_create_opts;
|
||||
|
||||
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
@@ -903,19 +897,60 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||
uint16_t *out_cyls,
|
||||
uint8_t *out_heads,
|
||||
uint8_t *out_secs_per_cyl,
|
||||
int64_t *out_total_sectors,
|
||||
Error **errp)
|
||||
static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t total_size = vpc_opts->size;
|
||||
uint8_t buf[1024];
|
||||
VHDFooter *footer = (VHDFooter *) buf;
|
||||
char *disk_type_param;
|
||||
int i;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
int64_t total_sectors;
|
||||
int i;
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
bool force_size;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
if (disk_type_param) {
|
||||
if (!strcmp(disk_type_param, "dynamic")) {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
} else if (!strcmp(disk_type_param, "fixed")) {
|
||||
disk_type = VHD_FIXED;
|
||||
} else {
|
||||
error_setg(errp, "Invalid disk type, %s", disk_type_param);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
}
|
||||
|
||||
force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/*
|
||||
* Calculate matching total_size and geometry. Increase the number of
|
||||
@@ -926,7 +961,7 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
||||
* the image size from the VHD footer to calculate total_sectors.
|
||||
*/
|
||||
if (vpc_opts->force_size) {
|
||||
if (force_size) {
|
||||
/* This will force the use of total_size for sector count, below */
|
||||
cyls = VHD_CHS_MAX_C;
|
||||
heads = VHD_CHS_MAX_H;
|
||||
@@ -943,95 +978,19 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||
/* Allow a maximum disk size of 2040 GiB */
|
||||
if (total_sectors > VHD_MAX_SECTORS) {
|
||||
error_setg(errp, "Disk size is too large, max size is 2040 GiB");
|
||||
return -EFBIG;
|
||||
ret = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||
}
|
||||
|
||||
*out_total_sectors = total_sectors;
|
||||
if (out_cyls) {
|
||||
*out_cyls = cyls;
|
||||
*out_heads = heads;
|
||||
*out_secs_per_cyl = secs_per_cyl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsVpc *vpc_opts;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
uint8_t buf[1024];
|
||||
VHDFooter *footer = (VHDFooter *) buf;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
int64_t total_sectors;
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_VPC);
|
||||
vpc_opts = &opts->u.vpc;
|
||||
|
||||
/* Validate options and set default values */
|
||||
total_size = vpc_opts->size;
|
||||
|
||||
if (!vpc_opts->has_subformat) {
|
||||
vpc_opts->subformat = BLOCKDEV_VPC_SUBFORMAT_DYNAMIC;
|
||||
}
|
||||
switch (vpc_opts->subformat) {
|
||||
case BLOCKDEV_VPC_SUBFORMAT_DYNAMIC:
|
||||
disk_type = VHD_DYNAMIC;
|
||||
break;
|
||||
case BLOCKDEV_VPC_SUBFORMAT_FIXED:
|
||||
disk_type = VHD_FIXED;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(vpc_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Get geometry and check that it matches the image size*/
|
||||
ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
|
||||
&total_sectors, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "The requested image size cannot be represented in "
|
||||
"CHS geometry");
|
||||
error_append_hint(errp, "Try size=%llu or force-size=on (the "
|
||||
"latter makes the image incompatible with "
|
||||
"Virtual PC)",
|
||||
total_sectors * BDRV_SECTOR_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
total_sectors = (int64_t)cyls * heads * secs_per_cyl;
|
||||
total_size = total_sectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Prepare the Hard Disk Footer */
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
if (vpc_opts->force_size) {
|
||||
if (force_size) {
|
||||
memcpy(footer->creator_app, "qem2", 4);
|
||||
} else {
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
@@ -1073,94 +1032,10 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||
|
||||
out:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
g_free(disk_type_param);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vpc_co_create_opts(const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ VPC_OPT_FORCE_SIZE, "force-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vpc_create_opts, true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "vpc");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VPC);
|
||||
create_options->u.vpc.size =
|
||||
ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (!create_options->u.vpc.force_size) {
|
||||
int64_t total_sectors;
|
||||
ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL,
|
||||
NULL, &total_sectors, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
|
||||
/* Create the vpc image (format layer) */
|
||||
ret = vpc_co_create(create_options, errp);
|
||||
|
||||
fail:
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int vpc_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
@@ -1221,7 +1096,6 @@ static BlockDriver bdrv_vpc = {
|
||||
.bdrv_close = vpc_close,
|
||||
.bdrv_reopen_prepare = vpc_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = vpc_co_create,
|
||||
.bdrv_co_create_opts = vpc_co_create_opts,
|
||||
|
||||
.bdrv_co_preadv = vpc_co_preadv,
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <dirent.h>
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/bswap.h"
|
||||
@@ -3130,7 +3129,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||
*child_flags = BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_vvfat_qcow = {
|
||||
@@ -3180,7 +3179,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||
qdict_put_str(options, "write-target.driver", "qcow");
|
||||
s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
|
||||
&child_vvfat_qcow, false, errp);
|
||||
qobject_unref(options);
|
||||
QDECREF(options);
|
||||
if (!s->qcow) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
|
||||
46
block/vxhs.c
46
block/vxhs.c
@@ -12,7 +12,6 @@
|
||||
#include <qnio/qnio_api.h>
|
||||
#include <sys/param.h>
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
@@ -217,12 +216,6 @@ static void vxhs_parse_filename(const char *filename, QDict *options,
|
||||
}
|
||||
}
|
||||
|
||||
static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
/* XXX Does VXHS support AIO on less than 512-byte alignment? */
|
||||
bs->bl.request_alignment = 512;
|
||||
}
|
||||
|
||||
static int vxhs_init_and_ref(void)
|
||||
{
|
||||
if (vxhs_ref++ == 0) {
|
||||
@@ -403,7 +396,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
out:
|
||||
g_free(of_vsa_addr);
|
||||
qobject_unref(backing_options);
|
||||
QDECREF(backing_options);
|
||||
qemu_opts_del(tcp_opts);
|
||||
qemu_opts_del(opts);
|
||||
g_free(cacert);
|
||||
@@ -431,17 +424,21 @@ static const AIOCBInfo vxhs_aiocb_info = {
|
||||
* and is passed to QNIO. When QNIO completes the work,
|
||||
* it will be passed back through the callback.
|
||||
*/
|
||||
static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset,
|
||||
QEMUIOVector *qiov, uint64_t size,
|
||||
static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
VDISKAIOCmd iodir)
|
||||
{
|
||||
VXHSAIOCB *acb = NULL;
|
||||
BDRVVXHSState *s = bs->opaque;
|
||||
size_t size;
|
||||
uint64_t offset;
|
||||
int iio_flags = 0;
|
||||
int ret = 0;
|
||||
void *dev_handle = s->vdisk_hostinfo.dev_handle;
|
||||
|
||||
offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque);
|
||||
|
||||
/*
|
||||
@@ -454,11 +451,11 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset,
|
||||
switch (iodir) {
|
||||
case VDISK_AIO_WRITE:
|
||||
ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
|
||||
offset, size, iio_flags);
|
||||
offset, (uint64_t)size, iio_flags);
|
||||
break;
|
||||
case VDISK_AIO_READ:
|
||||
ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
|
||||
offset, size, iio_flags);
|
||||
offset, (uint64_t)size, iio_flags);
|
||||
break;
|
||||
default:
|
||||
trace_vxhs_aio_rw_invalid(iodir);
|
||||
@@ -477,20 +474,22 @@ errout:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ);
|
||||
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb,
|
||||
opaque, VDISK_AIO_READ);
|
||||
}
|
||||
|
||||
static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE);
|
||||
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors,
|
||||
cb, opaque, VDISK_AIO_WRITE);
|
||||
}
|
||||
|
||||
static void vxhs_close(BlockDriverState *bs)
|
||||
@@ -562,11 +561,10 @@ static BlockDriver bdrv_vxhs = {
|
||||
.instance_size = sizeof(BDRVVXHSState),
|
||||
.bdrv_file_open = vxhs_open,
|
||||
.bdrv_parse_filename = vxhs_parse_filename,
|
||||
.bdrv_refresh_limits = vxhs_refresh_limits,
|
||||
.bdrv_close = vxhs_close,
|
||||
.bdrv_getlength = vxhs_getlength,
|
||||
.bdrv_aio_preadv = vxhs_aio_preadv,
|
||||
.bdrv_aio_pwritev = vxhs_aio_pwritev,
|
||||
.bdrv_aio_readv = vxhs_aio_readv,
|
||||
.bdrv_aio_writev = vxhs_aio_writev,
|
||||
};
|
||||
|
||||
static void bdrv_vxhs_init(void)
|
||||
|
||||
@@ -112,14 +112,15 @@ static const AIOCBInfo win32_aiocb_info = {
|
||||
|
||||
BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
QEMUWin32AIOState *aio, HANDLE hfile,
|
||||
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
struct QEMUWin32AIOCB *waiocb;
|
||||
uint64_t offset = sector_num * 512;
|
||||
DWORD rc;
|
||||
|
||||
waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque);
|
||||
waiocb->nbytes = bytes;
|
||||
waiocb->nbytes = nb_sectors * 512;
|
||||
waiocb->qiov = qiov;
|
||||
waiocb->is_read = (type == QEMU_AIO_READ);
|
||||
|
||||
|
||||
483
blockdev.c
483
blockdev.c
@@ -35,7 +35,6 @@
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/block/block.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/qdict.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -151,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk)
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (bs->job) {
|
||||
job_cancel(&bs->job->job, false);
|
||||
block_job_cancel(bs->job);
|
||||
}
|
||||
|
||||
aio_context_release(aio_context);
|
||||
@@ -335,8 +334,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
||||
|
||||
case QTYPE_QSTRING: {
|
||||
unsigned long long length;
|
||||
const char *str = qstring_get_str(qobject_to(QString,
|
||||
entry->value));
|
||||
const char *str = qstring_get_str(qobject_to_qstring(entry->value));
|
||||
if (parse_uint_full(str, &length, 10) == 0 &&
|
||||
length > 0 && length <= UINT_MAX) {
|
||||
block_acct_add_interval(stats, (unsigned) length);
|
||||
@@ -348,7 +346,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
||||
}
|
||||
|
||||
case QTYPE_QNUM: {
|
||||
int64_t length = qnum_get_int(qobject_to(QNum, entry->value));
|
||||
int64_t length = qnum_get_int(qobject_to_qnum(entry->value));
|
||||
|
||||
if (length > 0 && length <= UINT_MAX) {
|
||||
block_acct_add_interval(stats, (unsigned) length);
|
||||
@@ -577,7 +575,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
blk_rs->read_only = read_only;
|
||||
blk_rs->detect_zeroes = detect_zeroes;
|
||||
|
||||
qobject_unref(bs_opts);
|
||||
QDECREF(bs_opts);
|
||||
} else {
|
||||
if (file && !*file) {
|
||||
file = NULL;
|
||||
@@ -633,16 +631,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
|
||||
err_no_bs_opts:
|
||||
qemu_opts_del(opts);
|
||||
qobject_unref(interval_dict);
|
||||
qobject_unref(interval_list);
|
||||
QDECREF(interval_dict);
|
||||
QDECREF(interval_list);
|
||||
return blk;
|
||||
|
||||
early_err:
|
||||
qemu_opts_del(opts);
|
||||
qobject_unref(interval_dict);
|
||||
qobject_unref(interval_list);
|
||||
QDECREF(interval_dict);
|
||||
QDECREF(interval_list);
|
||||
err_no_opts:
|
||||
qobject_unref(bs_opts);
|
||||
QDECREF(bs_opts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -730,6 +728,30 @@ QemuOptsList qemu_legacy_drive_opts = {
|
||||
.name = "if",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
||||
},{
|
||||
.name = "cyls",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of cylinders (ide disk geometry)",
|
||||
},{
|
||||
.name = "heads",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of heads (ide disk geometry)",
|
||||
},{
|
||||
.name = "secs",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of sectors (ide disk geometry)",
|
||||
},{
|
||||
.name = "trans",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "chs translation (auto, lba, none)",
|
||||
},{
|
||||
.name = "addr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "pci address (virtio only)",
|
||||
},{
|
||||
.name = "serial",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "disk serial number",
|
||||
},{
|
||||
.name = "file",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -768,13 +790,19 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
QemuOpts *legacy_opts;
|
||||
DriveMediaType media = MEDIA_DISK;
|
||||
BlockInterfaceType type;
|
||||
int cyls, heads, secs, translation;
|
||||
int max_devs, bus_id, unit_id, index;
|
||||
const char *devaddr;
|
||||
const char *werror, *rerror;
|
||||
bool read_only = false;
|
||||
bool copy_on_read;
|
||||
const char *serial;
|
||||
const char *filename;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
const char *deprecated[] = {
|
||||
"serial", "trans", "secs", "heads", "cyls", "addr"
|
||||
};
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
static const struct {
|
||||
@@ -851,6 +879,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Other deprecated options */
|
||||
if (!qtest_enabled()) {
|
||||
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
|
||||
if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) {
|
||||
error_report("'%s' is deprecated, please use the corresponding "
|
||||
"option of '-device' instead", deprecated[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Media type */
|
||||
value = qemu_opt_get(legacy_opts, "media");
|
||||
if (value) {
|
||||
@@ -892,6 +930,57 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
type = block_default_type;
|
||||
}
|
||||
|
||||
/* Geometry */
|
||||
cyls = qemu_opt_get_number(legacy_opts, "cyls", 0);
|
||||
heads = qemu_opt_get_number(legacy_opts, "heads", 0);
|
||||
secs = qemu_opt_get_number(legacy_opts, "secs", 0);
|
||||
|
||||
if (cyls || heads || secs) {
|
||||
if (cyls < 1) {
|
||||
error_report("invalid physical cyls number");
|
||||
goto fail;
|
||||
}
|
||||
if (heads < 1) {
|
||||
error_report("invalid physical heads number");
|
||||
goto fail;
|
||||
}
|
||||
if (secs < 1) {
|
||||
error_report("invalid physical secs number");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
value = qemu_opt_get(legacy_opts, "trans");
|
||||
if (value != NULL) {
|
||||
if (!cyls) {
|
||||
error_report("'%s' trans must be used with cyls, heads and secs",
|
||||
value);
|
||||
goto fail;
|
||||
}
|
||||
if (!strcmp(value, "none")) {
|
||||
translation = BIOS_ATA_TRANSLATION_NONE;
|
||||
} else if (!strcmp(value, "lba")) {
|
||||
translation = BIOS_ATA_TRANSLATION_LBA;
|
||||
} else if (!strcmp(value, "large")) {
|
||||
translation = BIOS_ATA_TRANSLATION_LARGE;
|
||||
} else if (!strcmp(value, "rechs")) {
|
||||
translation = BIOS_ATA_TRANSLATION_RECHS;
|
||||
} else if (!strcmp(value, "auto")) {
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
} else {
|
||||
error_report("'%s' invalid translation type", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (media == MEDIA_CDROM) {
|
||||
if (cyls || secs || heads) {
|
||||
error_report("CHS can't be set with media=cdrom");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device address specified by bus/unit or index.
|
||||
* If none was specified, try to find the first free one. */
|
||||
bus_id = qemu_opt_get_number(legacy_opts, "bus", 0);
|
||||
@@ -931,6 +1020,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Serial number */
|
||||
serial = qemu_opt_get(legacy_opts, "serial");
|
||||
|
||||
/* no id supplied -> create one */
|
||||
if (qemu_opts_id(all_opts) == NULL) {
|
||||
char *new_id;
|
||||
@@ -950,6 +1042,12 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
}
|
||||
|
||||
/* Add virtio block device */
|
||||
devaddr = qemu_opt_get(legacy_opts, "addr");
|
||||
if (devaddr && type != IF_VIRTIO) {
|
||||
error_report("addr is not supported by this bus type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (type == IF_VIRTIO) {
|
||||
QemuOpts *devopts;
|
||||
devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
@@ -961,6 +1059,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
}
|
||||
qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"),
|
||||
&error_abort);
|
||||
if (devaddr) {
|
||||
qemu_opt_set(devopts, "addr", devaddr, &error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
filename = qemu_opt_get(legacy_opts, "file");
|
||||
@@ -1002,9 +1103,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
dinfo = g_malloc0(sizeof(*dinfo));
|
||||
dinfo->opts = all_opts;
|
||||
|
||||
dinfo->cyls = cyls;
|
||||
dinfo->heads = heads;
|
||||
dinfo->secs = secs;
|
||||
dinfo->trans = translation;
|
||||
|
||||
dinfo->type = type;
|
||||
dinfo->bus = bus_id;
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->devaddr = devaddr;
|
||||
dinfo->serial = g_strdup(serial);
|
||||
|
||||
blk_set_legacy_dinfo(blk, dinfo);
|
||||
|
||||
@@ -1021,7 +1129,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
|
||||
fail:
|
||||
qemu_opts_del(legacy_opts);
|
||||
qobject_unref(bs_opts);
|
||||
QDECREF(bs_opts);
|
||||
return dinfo;
|
||||
}
|
||||
|
||||
@@ -1337,7 +1445,7 @@ typedef struct BlkActionOps {
|
||||
struct BlkActionState {
|
||||
TransactionAction *action;
|
||||
const BlkActionOps *ops;
|
||||
JobTxn *block_job_txn;
|
||||
BlockJobTxn *block_job_txn;
|
||||
TransactionProperties *txn_props;
|
||||
QSIMPLEQ_ENTRY(BlkActionState) entry;
|
||||
};
|
||||
@@ -1755,7 +1863,7 @@ typedef struct DriveBackupState {
|
||||
BlockJob *job;
|
||||
} DriveBackupState;
|
||||
|
||||
static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
Error **errp);
|
||||
|
||||
static void drive_backup_prepare(BlkActionState *common, Error **errp)
|
||||
@@ -1801,7 +1909,7 @@ static void drive_backup_commit(BlkActionState *common)
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
assert(state->job);
|
||||
job_start(&state->job->job);
|
||||
block_job_start(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -1816,7 +1924,7 @@ static void drive_backup_abort(BlkActionState *common)
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
job_cancel_sync(&state->job->job);
|
||||
block_job_cancel_sync(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -1845,7 +1953,7 @@ typedef struct BlockdevBackupState {
|
||||
BlockJob *job;
|
||||
} BlockdevBackupState;
|
||||
|
||||
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
Error **errp);
|
||||
|
||||
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
|
||||
@@ -1899,7 +2007,7 @@ static void blockdev_backup_commit(BlkActionState *common)
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
assert(state->job);
|
||||
job_start(&state->job->job);
|
||||
block_job_start(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -1914,7 +2022,7 @@ static void blockdev_backup_abort(BlkActionState *common)
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
job_cancel_sync(&state->job->job);
|
||||
block_job_cancel_sync(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -1943,7 +2051,6 @@ typedef struct BlockDirtyBitmapState {
|
||||
BlockDriverState *bs;
|
||||
HBitmap *backup;
|
||||
bool prepared;
|
||||
bool was_enabled;
|
||||
} BlockDirtyBitmapState;
|
||||
|
||||
static void block_dirty_bitmap_add_prepare(BlkActionState *common,
|
||||
@@ -1964,7 +2071,6 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
|
||||
action->has_granularity, action->granularity,
|
||||
action->has_persistent, action->persistent,
|
||||
action->has_autoload, action->autoload,
|
||||
action->has_x_disabled, action->x_disabled,
|
||||
&local_err);
|
||||
|
||||
if (!local_err) {
|
||||
@@ -2012,9 +2118,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
if (bdrv_dirty_bitmap_frozen(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a frozen bitmap");
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a locked bitmap");
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
|
||||
error_setg(errp, "Cannot clear a disabled bitmap");
|
||||
return;
|
||||
@@ -2044,74 +2147,6 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common)
|
||||
hbitmap_free(state->backup);
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDirtyBitmap *action;
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
|
||||
if (action_check_completion_mode(common, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
action = common->action->u.x_block_dirty_bitmap_enable.data;
|
||||
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
||||
action->name,
|
||||
NULL,
|
||||
errp);
|
||||
if (!state->bitmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
|
||||
bdrv_enable_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_enable_abort(BlkActionState *common)
|
||||
{
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
|
||||
if (!state->was_enabled) {
|
||||
bdrv_disable_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDirtyBitmap *action;
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
|
||||
if (action_check_completion_mode(common, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
action = common->action->u.x_block_dirty_bitmap_disable.data;
|
||||
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
||||
action->name,
|
||||
NULL,
|
||||
errp);
|
||||
if (!state->bitmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
|
||||
bdrv_disable_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_disable_abort(BlkActionState *common)
|
||||
{
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
|
||||
if (state->was_enabled) {
|
||||
bdrv_enable_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
static void abort_prepare(BlkActionState *common, Error **errp)
|
||||
{
|
||||
error_setg(errp, "Transaction aborted using Abort action");
|
||||
@@ -2172,17 +2207,7 @@ static const BlkActionOps actions[] = {
|
||||
.prepare = block_dirty_bitmap_clear_prepare,
|
||||
.commit = block_dirty_bitmap_clear_commit,
|
||||
.abort = block_dirty_bitmap_clear_abort,
|
||||
},
|
||||
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = {
|
||||
.instance_size = sizeof(BlockDirtyBitmapState),
|
||||
.prepare = block_dirty_bitmap_enable_prepare,
|
||||
.abort = block_dirty_bitmap_enable_abort,
|
||||
},
|
||||
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = {
|
||||
.instance_size = sizeof(BlockDirtyBitmapState),
|
||||
.prepare = block_dirty_bitmap_disable_prepare,
|
||||
.abort = block_dirty_bitmap_disable_abort,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2214,7 +2239,7 @@ void qmp_transaction(TransactionActionList *dev_list,
|
||||
Error **errp)
|
||||
{
|
||||
TransactionActionList *dev_entry = dev_list;
|
||||
JobTxn *block_job_txn = NULL;
|
||||
BlockJobTxn *block_job_txn = NULL;
|
||||
BlkActionState *state, *next;
|
||||
Error *local_err = NULL;
|
||||
|
||||
@@ -2222,11 +2247,11 @@ void qmp_transaction(TransactionActionList *dev_list,
|
||||
QSIMPLEQ_INIT(&snap_bdrv_states);
|
||||
|
||||
/* Does this transaction get canceled as a group on failure?
|
||||
* If not, we don't really need to make a JobTxn.
|
||||
* If not, we don't really need to make a BlockJobTxn.
|
||||
*/
|
||||
props = get_transaction_properties(props);
|
||||
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
|
||||
block_job_txn = job_txn_new();
|
||||
block_job_txn = block_job_txn_new();
|
||||
}
|
||||
|
||||
/* drain all i/o before any operations */
|
||||
@@ -2285,7 +2310,7 @@ exit:
|
||||
if (!has_props) {
|
||||
qapi_free_TransactionProperties(props);
|
||||
}
|
||||
job_txn_unref(block_job_txn);
|
||||
block_job_txn_unref(block_job_txn);
|
||||
}
|
||||
|
||||
void qmp_eject(bool has_device, const char *device,
|
||||
@@ -2772,7 +2797,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_persistent, bool persistent,
|
||||
bool has_autoload, bool autoload,
|
||||
bool has_disabled, bool disabled,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -2807,10 +2831,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
warn_report("Autoload option is deprecated and its value is ignored");
|
||||
}
|
||||
|
||||
if (!has_disabled) {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
if (persistent &&
|
||||
!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
|
||||
{
|
||||
@@ -2822,10 +2842,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
|
||||
}
|
||||
|
||||
@@ -2846,11 +2862,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
"Bitmap '%s' is currently frozen and cannot be removed",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be removed",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
||||
@@ -2861,6 +2872,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_dirty_bitmap_make_anon(bitmap);
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
}
|
||||
|
||||
@@ -2884,11 +2896,6 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
"Bitmap '%s' is currently frozen and cannot be modified",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be modified",
|
||||
name);
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently disabled and cannot be cleared",
|
||||
@@ -2902,78 +2909,6 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
bdrv_clear_dirty_bitmap(bitmap, NULL);
|
||||
}
|
||||
|
||||
void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
|
||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
||||
if (!bitmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently frozen and cannot be enabled",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_enable_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
|
||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
||||
if (!bitmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently frozen and cannot be disabled",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name,
|
||||
const char *src_name, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BdrvDirtyBitmap *dst, *src;
|
||||
|
||||
dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp);
|
||||
if (!dst) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(dst)) {
|
||||
error_setg(errp, "Bitmap '%s' is frozen and cannot be modified",
|
||||
dst_name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_readonly(dst)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
||||
dst_name);
|
||||
return;
|
||||
}
|
||||
|
||||
src = bdrv_find_dirty_bitmap(bs, src_name);
|
||||
if (!src) {
|
||||
error_setg(errp, "Dirty bitmap '%s' not found", src_name);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_merge_dirty_bitmap(dst, src, errp);
|
||||
}
|
||||
|
||||
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
@@ -3295,7 +3230,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
goto out;
|
||||
}
|
||||
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
|
||||
JOB_DEFAULT, speed, on_error,
|
||||
BLOCK_JOB_DEFAULT, speed, on_error,
|
||||
filter_node_name, NULL, NULL, false, &local_err);
|
||||
} else {
|
||||
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
|
||||
@@ -3315,7 +3250,7 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -3326,7 +3261,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
AioContext *aio_context;
|
||||
QDict *options = NULL;
|
||||
Error *local_err = NULL;
|
||||
int flags, job_flags = JOB_DEFAULT;
|
||||
int flags;
|
||||
int64_t size;
|
||||
bool set_backing_hd = false;
|
||||
|
||||
@@ -3345,12 +3280,6 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
if (!backup->has_job_id) {
|
||||
backup->job_id = NULL;
|
||||
}
|
||||
if (!backup->has_auto_finalize) {
|
||||
backup->auto_finalize = true;
|
||||
}
|
||||
if (!backup->has_auto_dismiss) {
|
||||
backup->auto_dismiss = true;
|
||||
}
|
||||
if (!backup->has_compress) {
|
||||
backup->compress = false;
|
||||
}
|
||||
@@ -3441,24 +3370,12 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
bdrv_unref(target_bs);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_qmp_locked(bmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be used for "
|
||||
"backup", backup->bitmap);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (!backup->auto_dismiss) {
|
||||
job_flags |= JOB_MANUAL_DISMISS;
|
||||
}
|
||||
|
||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||
backup->sync, bmap, backup->compress,
|
||||
backup->on_source_error, backup->on_target_error,
|
||||
job_flags, NULL, NULL, txn, &local_err);
|
||||
BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
|
||||
bdrv_unref(target_bs);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -3476,7 +3393,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp)
|
||||
BlockJob *job;
|
||||
job = do_drive_backup(arg, NULL, errp);
|
||||
if (job) {
|
||||
job_start(&job->job);
|
||||
block_job_start(job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3485,7 +3402,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
|
||||
return bdrv_named_nodes_list(errp);
|
||||
}
|
||||
|
||||
BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -3493,7 +3410,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
Error *local_err = NULL;
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = NULL;
|
||||
int job_flags = JOB_DEFAULT;
|
||||
|
||||
if (!backup->has_speed) {
|
||||
backup->speed = 0;
|
||||
@@ -3507,12 +3423,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
if (!backup->has_job_id) {
|
||||
backup->job_id = NULL;
|
||||
}
|
||||
if (!backup->has_auto_finalize) {
|
||||
backup->auto_finalize = true;
|
||||
}
|
||||
if (!backup->has_auto_dismiss) {
|
||||
backup->auto_dismiss = true;
|
||||
}
|
||||
if (!backup->has_compress) {
|
||||
backup->compress = false;
|
||||
}
|
||||
@@ -3541,16 +3451,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (!backup->auto_dismiss) {
|
||||
job_flags |= JOB_MANUAL_DISMISS;
|
||||
}
|
||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||
backup->sync, NULL, backup->compress,
|
||||
backup->on_source_error, backup->on_target_error,
|
||||
job_flags, NULL, NULL, txn, &local_err);
|
||||
BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
@@ -3564,7 +3468,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
|
||||
BlockJob *job;
|
||||
job = do_blockdev_backup(arg, NULL, errp);
|
||||
if (job) {
|
||||
job_start(&job->job);
|
||||
block_job_start(job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3895,14 +3799,14 @@ void qmp_block_job_cancel(const char *device,
|
||||
force = false;
|
||||
}
|
||||
|
||||
if (job_user_paused(&job->job) && !force) {
|
||||
if (block_job_user_paused(job) && !force) {
|
||||
error_setg(errp, "The block job for device '%s' is currently paused",
|
||||
device);
|
||||
goto out;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_cancel(job);
|
||||
job_user_cancel(&job->job, force, errp);
|
||||
block_job_cancel(job);
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -3912,12 +3816,12 @@ void qmp_block_job_pause(const char *device, Error **errp)
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(device, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
if (!job || block_job_user_paused(job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_pause(job);
|
||||
job_user_pause(&job->job, errp);
|
||||
block_job_user_pause(job);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
@@ -3926,12 +3830,12 @@ void qmp_block_job_resume(const char *device, Error **errp)
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(device, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
if (!job || !block_job_user_paused(job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_resume(job);
|
||||
job_user_resume(&job->job, errp);
|
||||
block_job_user_resume(job);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
@@ -3945,37 +3849,7 @@ void qmp_block_job_complete(const char *device, Error **errp)
|
||||
}
|
||||
|
||||
trace_qmp_block_job_complete(job);
|
||||
job_complete(&job->job, errp);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_job_finalize(const char *id, Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(id, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_finalize(job);
|
||||
job_finalize(&job->job, errp);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_job_dismiss(const char *id, Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BlockJob *bjob = find_block_job(id, &aio_context, errp);
|
||||
Job *job;
|
||||
|
||||
if (!bjob) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_dismiss(bjob);
|
||||
job = &bjob->job;
|
||||
job_dismiss(&job, errp);
|
||||
block_job_complete(job, errp);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
@@ -4075,7 +3949,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr)
|
||||
qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
|
||||
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||
qobject_unref(qdict);
|
||||
QDECREF(qdict);
|
||||
error_report("'node-name' needs to be specified");
|
||||
goto out;
|
||||
}
|
||||
@@ -4098,6 +3972,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
QObject *obj;
|
||||
Visitor *v = qobject_output_visitor_new(&obj);
|
||||
QDict *qdict;
|
||||
const QDictEntry *ent;
|
||||
Error *local_err = NULL;
|
||||
|
||||
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
|
||||
@@ -4107,10 +3982,23 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
}
|
||||
|
||||
visit_complete(v, &obj);
|
||||
qdict = qobject_to(QDict, obj);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
|
||||
qdict_flatten(qdict);
|
||||
|
||||
/*
|
||||
* Rewrite "backing": null to "backing": ""
|
||||
* TODO Rewrite "" to null instead, and perhaps not even here
|
||||
*/
|
||||
for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
|
||||
char *dot = strrchr(ent->key, '.');
|
||||
|
||||
if (!strcmp(dot ? dot + 1 : ent->key, "backing")
|
||||
&& qobject_type(ent->value) == QTYPE_QNULL) {
|
||||
qdict_put(qdict, ent->key, qstring_new());
|
||||
}
|
||||
}
|
||||
|
||||
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||
error_setg(errp, "'node-name' must be specified for the root node");
|
||||
goto fail;
|
||||
@@ -4292,49 +4180,6 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
||||
aio_context_release(old_context);
|
||||
}
|
||||
|
||||
void qmp_x_block_latency_histogram_set(
|
||||
const char *device,
|
||||
bool has_boundaries, uint64List *boundaries,
|
||||
bool has_boundaries_read, uint64List *boundaries_read,
|
||||
bool has_boundaries_write, uint64List *boundaries_write,
|
||||
bool has_boundaries_flush, uint64List *boundaries_flush,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk = blk_by_name(device);
|
||||
BlockAcctStats *stats;
|
||||
|
||||
if (!blk) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return;
|
||||
}
|
||||
stats = blk_get_stats(blk);
|
||||
|
||||
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
|
||||
!has_boundaries_flush)
|
||||
{
|
||||
block_latency_histograms_clear(stats);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_read) {
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_READ,
|
||||
has_boundaries_read ? boundaries_read : boundaries);
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_write) {
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_WRITE,
|
||||
has_boundaries_write ? boundaries_write : boundaries);
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_flush) {
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_FLUSH,
|
||||
has_boundaries_flush ? boundaries_flush : boundaries);
|
||||
}
|
||||
}
|
||||
|
||||
QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
||||
|
||||
865
blockjob.c
865
blockjob.c
File diff suppressed because it is too large
Load Diff
@@ -649,7 +649,7 @@ void cpu_loop(CPUSPARCState *env)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
|
||||
printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
|
||||
"\n" QEMU_COPYRIGHT "\n"
|
||||
"usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
|
||||
"BSD CPU emulator (compiled for %s emulation)\n"
|
||||
@@ -723,7 +723,6 @@ int main(int argc, char **argv)
|
||||
{
|
||||
const char *filename;
|
||||
const char *cpu_model;
|
||||
const char *cpu_type;
|
||||
const char *log_file = NULL;
|
||||
const char *log_mask = NULL;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
@@ -898,12 +897,10 @@ int main(int argc, char **argv)
|
||||
cpu_model = "any";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* init tcg before creating CPUs and to get qemu_host_page_size */
|
||||
tcg_exec_init(0);
|
||||
|
||||
cpu_type = parse_cpu_model(cpu_model);
|
||||
cpu = cpu_create(cpu_type);
|
||||
/* NOTE: we need to init the CPU at this stage to get
|
||||
qemu_host_page_size */
|
||||
cpu = cpu_init(cpu_model);
|
||||
env = cpu->env_ptr;
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
cpu_reset(cpu);
|
||||
@@ -918,7 +915,7 @@ int main(int argc, char **argv)
|
||||
envlist_free(envlist);
|
||||
|
||||
/*
|
||||
* Now that page sizes are configured in tcg_exec_init() we can do
|
||||
* Now that page sizes are configured in cpu_init() we can do
|
||||
* proper page alignment for guest_base.
|
||||
*/
|
||||
guest_base = HOST_PAGE_ALIGN(guest_base);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "qemu.h"
|
||||
#include "qemu-common.h"
|
||||
#include "bsd-mman.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
//#define DEBUG_MMAP
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#undef DEBUG_REMAP
|
||||
|
||||
@@ -198,21 +198,19 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
|
||||
{
|
||||
int tag = 0;
|
||||
|
||||
if (s) {
|
||||
if (CHARDEV_IS_MUX(s)) {
|
||||
MuxChardev *d = MUX_CHARDEV(s);
|
||||
if (CHARDEV_IS_MUX(s)) {
|
||||
MuxChardev *d = MUX_CHARDEV(s);
|
||||
|
||||
if (d->mux_cnt >= MAX_MUX) {
|
||||
goto unavailable;
|
||||
}
|
||||
|
||||
d->backends[d->mux_cnt] = b;
|
||||
tag = d->mux_cnt++;
|
||||
} else if (s->be) {
|
||||
if (d->mux_cnt >= MAX_MUX) {
|
||||
goto unavailable;
|
||||
} else {
|
||||
s->be = b;
|
||||
}
|
||||
|
||||
d->backends[d->mux_cnt] = b;
|
||||
tag = d->mux_cnt++;
|
||||
} else if (s->be) {
|
||||
goto unavailable;
|
||||
} else {
|
||||
s->be = b;
|
||||
}
|
||||
|
||||
b->fe_open = false;
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "qemu/option.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "chardev/char-mux.h"
|
||||
|
||||
/* MUX driver for serial I/O splitting */
|
||||
@@ -231,12 +230,14 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
}
|
||||
}
|
||||
|
||||
bool muxes_realized;
|
||||
|
||||
void mux_chr_send_all_event(Chardev *chr, int event)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
int i;
|
||||
|
||||
if (!machine_init_done) {
|
||||
if (!muxes_realized) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -304,7 +305,6 @@ void mux_set_focus(Chardev *chr, int focus)
|
||||
}
|
||||
|
||||
d->focus = focus;
|
||||
chr->be = d->backends[focus];
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ static void qemu_chr_open_mux(Chardev *chr,
|
||||
/* only default to opened state if we've realized the initial
|
||||
* set of muxes
|
||||
*/
|
||||
*be_opened = machine_init_done;
|
||||
*be_opened = muxes_realized;
|
||||
qemu_chr_fe_init(&d->chr, drv, errp);
|
||||
}
|
||||
|
||||
@@ -347,31 +347,6 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
|
||||
mux->chardev = g_strdup(chardev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after processing of default and command-line-specified
|
||||
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
|
||||
* to a mux chardev. This is done here to ensure that
|
||||
* output/prompts/banners are only displayed for the FE that has
|
||||
* focus when initial command-line processing/machine init is
|
||||
* completed.
|
||||
*
|
||||
* After this point, any new FE attached to any new or existing
|
||||
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
||||
* immediately.
|
||||
*/
|
||||
static int open_muxes(Chardev *chr)
|
||||
{
|
||||
/* send OPENED to all already-attached FEs */
|
||||
mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
|
||||
/*
|
||||
* mark mux as OPENED so any new FEs will immediately receive
|
||||
* OPENED event
|
||||
*/
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@@ -382,7 +357,6 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_accept_input = mux_chr_accept_input;
|
||||
cc->chr_add_watch = mux_chr_add_watch;
|
||||
cc->chr_be_event = mux_chr_be_event;
|
||||
cc->chr_machine_done = open_muxes;
|
||||
}
|
||||
|
||||
static const TypeInfo char_mux_type_info = {
|
||||
|
||||
@@ -139,7 +139,7 @@ static void tty_serial_init(int fd, int speed,
|
||||
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_oflag &= ~OPOST;
|
||||
tty.c_oflag |= OPOST;
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
||||
tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
|
||||
switch (data_bits) {
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
@@ -41,11 +40,6 @@
|
||||
|
||||
#define TCP_MAX_FDS 16
|
||||
|
||||
typedef struct {
|
||||
char buf[21];
|
||||
size_t buflen;
|
||||
} TCPChardevTelnetInit;
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc; /* Client I/O channel */
|
||||
@@ -66,8 +60,6 @@ typedef struct {
|
||||
bool is_listen;
|
||||
bool is_telnet;
|
||||
bool is_tn3270;
|
||||
GSource *telnet_source;
|
||||
TCPChardevTelnetInit *telnet_init;
|
||||
|
||||
GSource *reconnect_timer;
|
||||
int64_t reconnect_time;
|
||||
@@ -78,7 +70,6 @@ typedef struct {
|
||||
OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
|
||||
|
||||
static gboolean socket_reconnect_timeout(gpointer opaque);
|
||||
static void tcp_chr_telnet_init(Chardev *chr);
|
||||
|
||||
static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
|
||||
{
|
||||
@@ -432,8 +423,8 @@ static void tcp_chr_disconnect(Chardev *chr)
|
||||
tcp_chr_free_connection(chr);
|
||||
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
|
||||
chr, NULL, chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
|
||||
chr, NULL);
|
||||
}
|
||||
update_disconnected_filename(s);
|
||||
if (emit_close) {
|
||||
@@ -459,7 +450,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
len = s->max_size;
|
||||
}
|
||||
size = tcp_chr_recv(chr, (void *)buf, len);
|
||||
if (size == 0 || (size == -1 && errno != EAGAIN)) {
|
||||
if (size == 0 || size == -1) {
|
||||
/* connection closed */
|
||||
tcp_chr_disconnect(chr);
|
||||
} else if (size > 0) {
|
||||
@@ -550,10 +541,12 @@ static void tcp_chr_connect(void *opaque)
|
||||
s->is_listen, s->is_telnet);
|
||||
|
||||
s->connected = 1;
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read,
|
||||
chr, chr->gcontext);
|
||||
if (s->ioc) {
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read,
|
||||
chr, chr->gcontext);
|
||||
}
|
||||
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
@@ -563,33 +556,10 @@ static void tcp_chr_connect(void *opaque)
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
static void tcp_chr_telnet_destroy(SocketChardev *s)
|
||||
{
|
||||
if (s->telnet_source) {
|
||||
g_source_destroy(s->telnet_source);
|
||||
g_source_unref(s->telnet_source);
|
||||
s->telnet_source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->listener) {
|
||||
/*
|
||||
* It's possible that chardev context is changed in
|
||||
* qemu_chr_be_update_read_handlers(). Reset it for QIO net
|
||||
* listener if there is.
|
||||
*/
|
||||
qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
|
||||
chr, NULL, chr->gcontext);
|
||||
}
|
||||
|
||||
if (s->telnet_source) {
|
||||
tcp_chr_telnet_init(CHARDEV(s));
|
||||
}
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
@@ -603,30 +573,32 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Chardev *chr;
|
||||
char buf[21];
|
||||
size_t buflen;
|
||||
} TCPChardevTelnetInit;
|
||||
|
||||
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
GIOCondition cond G_GNUC_UNUSED,
|
||||
gpointer user_data)
|
||||
{
|
||||
SocketChardev *s = user_data;
|
||||
Chardev *chr = CHARDEV(s);
|
||||
TCPChardevTelnetInit *init = s->telnet_init;
|
||||
TCPChardevTelnetInit *init = user_data;
|
||||
ssize_t ret;
|
||||
|
||||
assert(init);
|
||||
|
||||
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
|
||||
if (ret < 0) {
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
ret = 0;
|
||||
} else {
|
||||
tcp_chr_disconnect(chr);
|
||||
tcp_chr_disconnect(init->chr);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
init->buflen -= ret;
|
||||
|
||||
if (init->buflen == 0) {
|
||||
tcp_chr_connect(chr);
|
||||
tcp_chr_connect(init->chr);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -635,30 +607,16 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
end:
|
||||
g_free(s->telnet_init);
|
||||
s->telnet_init = NULL;
|
||||
g_source_unref(s->telnet_source);
|
||||
s->telnet_source = NULL;
|
||||
g_free(init);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void tcp_chr_telnet_init(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
TCPChardevTelnetInit *init;
|
||||
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
|
||||
size_t n = 0;
|
||||
|
||||
/* Destroy existing task */
|
||||
tcp_chr_telnet_destroy(s);
|
||||
|
||||
if (s->telnet_init) {
|
||||
/* We are possibly during a handshake already */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
s->telnet_init = g_new0(TCPChardevTelnetInit, 1);
|
||||
init = s->telnet_init;
|
||||
|
||||
#define IACSET(x, a, b, c) \
|
||||
do { \
|
||||
x[n++] = a; \
|
||||
@@ -666,6 +624,7 @@ static void tcp_chr_telnet_init(Chardev *chr)
|
||||
x[n++] = c; \
|
||||
} while (0)
|
||||
|
||||
init->chr = chr;
|
||||
if (!s->is_tn3270) {
|
||||
init->buflen = 12;
|
||||
/* Prep the telnet negotion to put telnet in binary,
|
||||
@@ -688,11 +647,10 @@ static void tcp_chr_telnet_init(Chardev *chr)
|
||||
|
||||
#undef IACSET
|
||||
|
||||
cont:
|
||||
s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
|
||||
tcp_chr_telnet_init_io,
|
||||
s, NULL,
|
||||
chr->gcontext);
|
||||
qio_channel_add_watch(
|
||||
s->ioc, G_IO_OUT,
|
||||
tcp_chr_telnet_init_io,
|
||||
init, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -705,7 +663,8 @@ static void tcp_chr_tls_handshake(QIOTask *task,
|
||||
if (qio_task_propagate_error(task, NULL)) {
|
||||
tcp_chr_disconnect(chr);
|
||||
} else {
|
||||
if (s->do_telnetopt) {
|
||||
/* tn3270 does not support TLS yet */
|
||||
if (s->do_telnetopt && !s->is_tn3270) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
@@ -721,11 +680,6 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
Error *err = NULL;
|
||||
gchar *name;
|
||||
|
||||
if (!machine_init_done) {
|
||||
/* This will be postponed to machine_done notifier */
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->is_listen) {
|
||||
tioc = qio_channel_tls_new_server(
|
||||
s->ioc, s->tls_creds,
|
||||
@@ -754,7 +708,7 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
tcp_chr_tls_handshake,
|
||||
chr,
|
||||
NULL,
|
||||
chr->gcontext);
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -790,8 +744,7 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
|
||||
qio_channel_set_delay(s->ioc, false);
|
||||
}
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
|
||||
NULL, chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (s->tls_creds) {
|
||||
@@ -871,11 +824,8 @@ static void char_socket_finalize(Object *obj)
|
||||
tcp_chr_free_connection(chr);
|
||||
tcp_chr_reconn_timer_cancel(s);
|
||||
qapi_free_SocketAddress(s->addr);
|
||||
tcp_chr_telnet_destroy(s);
|
||||
g_free(s->telnet_init);
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
|
||||
NULL, chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
object_unref(OBJECT(s->listener));
|
||||
}
|
||||
if (s->tls_creds) {
|
||||
@@ -905,22 +855,11 @@ cleanup:
|
||||
object_unref(OBJECT(sioc));
|
||||
}
|
||||
|
||||
static void tcp_chr_connect_async(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL, chr->gcontext);
|
||||
}
|
||||
|
||||
static gboolean socket_reconnect_timeout(gpointer opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
SocketChardev *s = SOCKET_CHARDEV(opaque);
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
g_source_unref(s->reconnect_timer);
|
||||
s->reconnect_timer = NULL;
|
||||
@@ -929,7 +868,11 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
|
||||
return false;
|
||||
}
|
||||
|
||||
tcp_chr_connect_async(chr);
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL, NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1008,8 +951,13 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
s->reconnect_time = reconnect;
|
||||
}
|
||||
|
||||
/* If reconnect_time is set, will do that in chr_machine_done. */
|
||||
if (!s->reconnect_time) {
|
||||
if (s->reconnect_time) {
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL, NULL);
|
||||
} else {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
s->listener = qio_net_listener_new();
|
||||
@@ -1033,10 +981,8 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
return;
|
||||
}
|
||||
if (!s->ioc) {
|
||||
qio_net_listener_set_client_func_full(s->listener,
|
||||
tcp_chr_accept,
|
||||
chr, NULL,
|
||||
chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
|
||||
chr, NULL);
|
||||
}
|
||||
} else if (qemu_chr_wait_connected(chr, errp) < 0) {
|
||||
goto error;
|
||||
@@ -1063,36 +1009,25 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
const char *host = qemu_opt_get(opts, "host");
|
||||
const char *port = qemu_opt_get(opts, "port");
|
||||
const char *fd = qemu_opt_get(opts, "fd");
|
||||
const char *tls_creds = qemu_opt_get(opts, "tls-creds");
|
||||
SocketAddressLegacy *addr;
|
||||
ChardevSocket *sock;
|
||||
|
||||
if ((!!path + !!fd + !!host) != 1) {
|
||||
error_setg(errp,
|
||||
"Exactly one of 'path', 'fd' or 'host' required");
|
||||
return;
|
||||
}
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_SOCKET;
|
||||
if (path) {
|
||||
if (tls_creds) {
|
||||
error_setg(errp, "TLS can only be used over TCP socket");
|
||||
if (!path) {
|
||||
if (!host) {
|
||||
error_setg(errp, "chardev: socket: no host given");
|
||||
return;
|
||||
}
|
||||
} else if (host) {
|
||||
if (!port) {
|
||||
error_setg(errp, "chardev: socket: no port given");
|
||||
return;
|
||||
}
|
||||
} else if (fd) {
|
||||
/* We don't know what host to validate against when in client mode */
|
||||
if (tls_creds && !is_listen) {
|
||||
error_setg(errp, "TLS can not be used with pre-opened client FD");
|
||||
} else {
|
||||
if (tls_creds) {
|
||||
error_setg(errp, "TLS can only be used over TCP socket");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
|
||||
@@ -1118,7 +1053,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
|
||||
q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
||||
q_unix->path = g_strdup(path);
|
||||
} else if (host) {
|
||||
} else {
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
@@ -1131,12 +1066,6 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
|
||||
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
|
||||
};
|
||||
} else if (fd) {
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
|
||||
addr->u.fd.data = g_new(String, 1);
|
||||
addr->u.fd.data->str = g_strdup(fd);
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
sock->addr = addr;
|
||||
}
|
||||
@@ -1158,21 +1087,6 @@ char_socket_get_connected(Object *obj, Error **errp)
|
||||
return s->connected;
|
||||
}
|
||||
|
||||
static int tcp_chr_machine_done_hook(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
}
|
||||
|
||||
if (s->ioc && s->tls_creds) {
|
||||
tcp_chr_tls_init(chr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@@ -1188,7 +1102,6 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_add_client = tcp_chr_add_client;
|
||||
cc->chr_add_watch = tcp_chr_add_watch;
|
||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
||||
cc->chr_machine_done = tcp_chr_machine_done_hook;
|
||||
|
||||
object_class_property_add(oc, "addr", "SocketAddress",
|
||||
char_socket_get_addr, NULL,
|
||||
|
||||
@@ -281,31 +281,40 @@ static const TypeInfo char_type_info = {
|
||||
.class_init = char_class_init,
|
||||
};
|
||||
|
||||
static int chardev_machine_done_notify_one(Object *child, void *opaque)
|
||||
/**
|
||||
* Called after processing of default and command-line-specified
|
||||
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
|
||||
* to a mux chardev. This is done here to ensure that
|
||||
* output/prompts/banners are only displayed for the FE that has
|
||||
* focus when initial command-line processing/machine init is
|
||||
* completed.
|
||||
*
|
||||
* After this point, any new FE attached to any new or existing
|
||||
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
||||
* immediately.
|
||||
*/
|
||||
static int open_muxes(Object *child, void *opaque)
|
||||
{
|
||||
Chardev *chr = (Chardev *)child;
|
||||
ChardevClass *class = CHARDEV_GET_CLASS(chr);
|
||||
|
||||
if (class->chr_machine_done) {
|
||||
return class->chr_machine_done(chr);
|
||||
if (CHARDEV_IS_MUX(child)) {
|
||||
/* send OPENED to all already-attached FEs */
|
||||
mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
|
||||
/* mark mux as OPENED so any new FEs will immediately receive
|
||||
* OPENED event
|
||||
*/
|
||||
qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chardev_machine_done_hook(Notifier *notifier, void *unused)
|
||||
static void muxes_realize_done(Notifier *notifier, void *unused)
|
||||
{
|
||||
int ret = object_child_foreach(get_chardevs_root(),
|
||||
chardev_machine_done_notify_one, NULL);
|
||||
|
||||
if (ret) {
|
||||
error_report("Failed to call chardev machine_done hooks");
|
||||
exit(1);
|
||||
}
|
||||
muxes_realized = true;
|
||||
object_child_foreach(get_chardevs_root(), open_muxes, NULL);
|
||||
}
|
||||
|
||||
static Notifier chardev_machine_done_notify = {
|
||||
.notify = chardev_machine_done_hook,
|
||||
static Notifier muxes_realize_notify = {
|
||||
.notify = muxes_realize_done,
|
||||
};
|
||||
|
||||
static bool qemu_chr_is_busy(Chardev *s)
|
||||
@@ -798,9 +807,6 @@ QemuOptsList qemu_chardev_opts = {
|
||||
},{
|
||||
.name = "port",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "fd",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "localaddr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -1112,7 +1118,7 @@ static void register_types(void)
|
||||
* as part of realize functions like serial_isa_realizefn when -nographic
|
||||
* is specified
|
||||
*/
|
||||
qemu_add_machine_init_done_notifier(&chardev_machine_done_notify);
|
||||
qemu_add_machine_init_done_notifier(&muxes_realize_notify);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
299
configure
vendored
299
configure
vendored
@@ -60,11 +60,6 @@ do_compiler() {
|
||||
# is compiler binary to execute.
|
||||
local compiler="$1"
|
||||
shift
|
||||
if test -n "$BASH_VERSION"; then eval '
|
||||
echo >>config.log "
|
||||
funcs: ${FUNCNAME[*]}
|
||||
lines: ${BASH_LINENO[*]}"
|
||||
'; fi
|
||||
echo $compiler "$@" >> config.log
|
||||
$compiler "$@" >> config.log 2>&1 || return $?
|
||||
# Test passed. If this is an --enable-werror build, rerun
|
||||
@@ -347,7 +342,7 @@ attr=""
|
||||
libattr=""
|
||||
xfs=""
|
||||
tcg="yes"
|
||||
membarrier=""
|
||||
|
||||
vhost_net="no"
|
||||
vhost_crypto="no"
|
||||
vhost_scsi="no"
|
||||
@@ -456,7 +451,6 @@ jemalloc="no"
|
||||
replication="yes"
|
||||
vxhs=""
|
||||
libxml2=""
|
||||
docker="no"
|
||||
|
||||
supported_cpu="no"
|
||||
supported_os="no"
|
||||
@@ -540,7 +534,7 @@ QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
|
||||
QEMU_INCLUDES="-iquote . -iquote \$(SRC_PATH) -iquote \$(SRC_PATH)/accel/tcg -iquote \$(SRC_PATH)/include"
|
||||
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/accel/tcg -I\$(SRC_PATH)/include"
|
||||
if test "$debug_info" = "yes"; then
|
||||
CFLAGS="-g $CFLAGS"
|
||||
LDFLAGS="-g $LDFLAGS"
|
||||
@@ -965,8 +959,6 @@ for opt do
|
||||
;;
|
||||
--firmwarepath=*) firmwarepath="$optarg"
|
||||
;;
|
||||
--host=*|--build=*|\
|
||||
--disable-dependency-tracking|\
|
||||
--sbindir=*|--sharedstatedir=*|\
|
||||
--oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\
|
||||
--htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*)
|
||||
@@ -1169,13 +1161,9 @@ for opt do
|
||||
;;
|
||||
--enable-attr) attr="yes"
|
||||
;;
|
||||
--disable-membarrier) membarrier="no"
|
||||
;;
|
||||
--enable-membarrier) membarrier="yes"
|
||||
;;
|
||||
--disable-blobs) blobs="no"
|
||||
;;
|
||||
--with-pkgversion=*) pkgversion="$optarg"
|
||||
--with-pkgversion=*) pkgversion=" ($optarg)"
|
||||
;;
|
||||
--with-coroutine=*) coroutine="$optarg"
|
||||
;;
|
||||
@@ -1505,19 +1493,16 @@ Advanced options (experts only):
|
||||
--install=INSTALL use specified install [$install]
|
||||
--python=PYTHON use specified python [$python]
|
||||
--smbd=SMBD use specified smbd [$smbd]
|
||||
--with-git=GIT use specified git [$git]
|
||||
--static enable static build [$static]
|
||||
--mandir=PATH install man pages in PATH
|
||||
--datadir=PATH install firmware in PATH$confsuffix
|
||||
--docdir=PATH install documentation in PATH$confsuffix
|
||||
--bindir=PATH install binaries in PATH
|
||||
--libdir=PATH install libraries in PATH
|
||||
--libexecdir=PATH install helper binaries in PATH
|
||||
--sysconfdir=PATH install config in PATH$confsuffix
|
||||
--localstatedir=PATH install local state in PATH (set at runtime on win32)
|
||||
--firmwarepath=PATH search PATH for firmware files
|
||||
--with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix]
|
||||
--with-pkgversion=VERS use specified string as sub-version of the package
|
||||
--enable-debug enable common debug build options
|
||||
--enable-sanitizers enable default sanitizers
|
||||
--disable-strip disable stripping binaries
|
||||
@@ -1589,10 +1574,9 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
virtfs VirtFS
|
||||
mpath Multipath persistent reservation passthrough
|
||||
xen xen backend driver support
|
||||
xen-pci-passthrough PCI passthrough support for Xen
|
||||
xen-pci-passthrough
|
||||
brlapi BrlAPI (Braile)
|
||||
curl curl connectivity
|
||||
membarrier membarrier system call (for Linux 4.14+ or Windows)
|
||||
fdt fdt device tree
|
||||
bluez bluez stack connectivity
|
||||
kvm KVM acceleration support
|
||||
@@ -1651,8 +1635,8 @@ fi
|
||||
|
||||
# Note that if the Python conditional here evaluates True we will exit
|
||||
# with status 1 which is a shell 'false' value.
|
||||
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then
|
||||
error_exit "Cannot use '$python', Python 2 >= 2.7 or Python 3 is required." \
|
||||
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6))'; then
|
||||
error_exit "Cannot use '$python', Python 2 >= 2.6 or Python 3 is required." \
|
||||
"Use --python=/path/to/python to specify a supported Python."
|
||||
fi
|
||||
|
||||
@@ -2197,9 +2181,6 @@ if test "$xen" != "no" ; then
|
||||
xen=yes
|
||||
xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab"
|
||||
xen_pc="$xen_pc xenevtchn xendevicemodel"
|
||||
if $pkg_config --exists xentoolcore; then
|
||||
xen_pc="$xen_pc xentoolcore"
|
||||
fi
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)"
|
||||
libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu"
|
||||
LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS"
|
||||
@@ -2229,46 +2210,20 @@ EOF
|
||||
# Xen unstable
|
||||
elif
|
||||
cat > $TMPC <<EOF &&
|
||||
#undef XC_WANT_COMPAT_DEVICEMODEL_API
|
||||
#define __XEN_TOOLS__
|
||||
#include <xendevicemodel.h>
|
||||
#include <xenforeignmemory.h>
|
||||
int main(void) {
|
||||
xendevicemodel_handle *xd;
|
||||
xenforeignmemory_handle *xfmem;
|
||||
|
||||
xd = xendevicemodel_open(0, 0);
|
||||
xendevicemodel_pin_memory_cacheattr(xd, 0, 0, 0, 0);
|
||||
|
||||
xfmem = xenforeignmemory_open(0, 0);
|
||||
xenforeignmemory_map_resource(xfmem, 0, 0, 0, 0, 0, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore"
|
||||
then
|
||||
xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore"
|
||||
xen_ctrl_version=41100
|
||||
xen=yes
|
||||
elif
|
||||
cat > $TMPC <<EOF &&
|
||||
#undef XC_WANT_COMPAT_MAP_FOREIGN_API
|
||||
#include <xenforeignmemory.h>
|
||||
#include <xentoolcore.h>
|
||||
int main(void) {
|
||||
xenforeignmemory_handle *xfmem;
|
||||
|
||||
xfmem = xenforeignmemory_open(0, 0);
|
||||
xenforeignmemory_map2(xfmem, 0, 0, 0, 0, 0, 0, 0);
|
||||
xentoolcore_restrict_all(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore"
|
||||
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs"
|
||||
then
|
||||
xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore"
|
||||
xen_stable_libs="-lxendevicemodel $xen_stable_libs"
|
||||
xen_ctrl_version=41000
|
||||
xen=yes
|
||||
elif
|
||||
@@ -2530,7 +2485,18 @@ fi
|
||||
##########################################
|
||||
# Windows Hypervisor Platform accelerator (WHPX) check
|
||||
if test "$whpx" != "no" ; then
|
||||
if check_include "WinHvPlatform.h" && check_include "WinHvEmulation.h"; then
|
||||
cat > $TMPC << EOF
|
||||
#include <windows.h>
|
||||
#include <WinHvPlatform.h>
|
||||
#include <WinHvEmulation.h>
|
||||
int main(void) {
|
||||
WHV_CAPABILITY whpx_cap;
|
||||
WHvGetCapability(WHvCapabilityCodeFeatures, &whpx_cap, sizeof(whpx_cap));
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "-lWinHvPlatform -lWinHvEmulation" ; then
|
||||
libs_softmmu="$libs_softmmu -lWinHvPlatform -lWinHvEmulation"
|
||||
whpx="yes"
|
||||
else
|
||||
if test "$whpx" = "yes"; then
|
||||
@@ -2564,18 +2530,19 @@ fi
|
||||
##########################################
|
||||
# GTK probe
|
||||
|
||||
if test "$gtk" != "no"; then
|
||||
if test "$gtkabi" = ""; then
|
||||
# The GTK ABI was not specified explicitly, so try whether 3.0 is available.
|
||||
# Use 2.0 as a fallback if that is available.
|
||||
if $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
|
||||
gtkabi=3.0
|
||||
elif $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
|
||||
gtkabi=2.0
|
||||
else
|
||||
gtkabi=3.0
|
||||
fi
|
||||
if test "$gtkabi" = ""; then
|
||||
# The GTK ABI was not specified explicitly, so try whether 3.0 is available.
|
||||
# Use 2.0 as a fallback if that is available.
|
||||
if $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
|
||||
gtkabi=3.0
|
||||
elif $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
|
||||
gtkabi=2.0
|
||||
else
|
||||
gtkabi=3.0
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$gtk" != "no"; then
|
||||
gtkpackage="gtk+-$gtkabi"
|
||||
gtkx11package="gtk+-x11-$gtkabi"
|
||||
if test "$gtkabi" = "3.0" ; then
|
||||
@@ -2859,52 +2826,49 @@ fi
|
||||
# Look for sdl configuration program (pkg-config or sdl-config). Try
|
||||
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
|
||||
|
||||
sdl_probe ()
|
||||
{
|
||||
sdl_too_old=no
|
||||
if test "$sdlabi" = ""; then
|
||||
if $pkg_config --exists "sdl2"; then
|
||||
sdlabi=2.0
|
||||
elif $pkg_config --exists "sdl"; then
|
||||
sdlabi=1.2
|
||||
else
|
||||
sdlabi=2.0
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $sdlabi = "2.0"; then
|
||||
sdl_config=$sdl2_config
|
||||
sdlname=sdl2
|
||||
sdlconfigname=sdl2_config
|
||||
elif test $sdlabi = "1.2"; then
|
||||
sdlname=sdl
|
||||
sdlconfigname=sdl_config
|
||||
else
|
||||
error_exit "Unknown sdlabi $sdlabi, must be 1.2 or 2.0"
|
||||
fi
|
||||
|
||||
if test "$(basename $sdl_config)" != $sdlconfigname && ! has ${sdl_config}; then
|
||||
sdl_config=$sdlconfigname
|
||||
fi
|
||||
|
||||
if $pkg_config $sdlname --exists; then
|
||||
sdlconfig="$pkg_config $sdlname"
|
||||
sdlversion=$($sdlconfig --modversion 2>/dev/null)
|
||||
elif has ${sdl_config}; then
|
||||
sdlconfig="$sdl_config"
|
||||
sdlversion=$($sdlconfig --version)
|
||||
else
|
||||
if test "$sdl" = "yes" ; then
|
||||
feature_not_found "sdl" "Install SDL2-devel"
|
||||
if test "$sdlabi" = ""; then
|
||||
if $pkg_config --exists "sdl2"; then
|
||||
sdlabi=2.0
|
||||
elif $pkg_config --exists "sdl"; then
|
||||
sdlabi=1.2
|
||||
else
|
||||
sdlabi=2.0
|
||||
fi
|
||||
sdl=no
|
||||
# no need to do the rest
|
||||
return
|
||||
fi
|
||||
if test -n "$cross_prefix" && test "$(basename "$sdlconfig")" = sdl-config; then
|
||||
echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $sdlabi = "2.0"; then
|
||||
sdl_config=$sdl2_config
|
||||
sdlname=sdl2
|
||||
sdlconfigname=sdl2_config
|
||||
elif test $sdlabi = "1.2"; then
|
||||
sdlname=sdl
|
||||
sdlconfigname=sdl_config
|
||||
else
|
||||
error_exit "Unknown sdlabi $sdlabi, must be 1.2 or 2.0"
|
||||
fi
|
||||
|
||||
if test "$(basename $sdl_config)" != $sdlconfigname && ! has ${sdl_config}; then
|
||||
sdl_config=$sdlconfigname
|
||||
fi
|
||||
|
||||
if $pkg_config $sdlname --exists; then
|
||||
sdlconfig="$pkg_config $sdlname"
|
||||
sdlversion=$($sdlconfig --modversion 2>/dev/null)
|
||||
elif has ${sdl_config}; then
|
||||
sdlconfig="$sdl_config"
|
||||
sdlversion=$($sdlconfig --version)
|
||||
else
|
||||
if test "$sdl" = "yes" ; then
|
||||
feature_not_found "sdl" "Install SDL2-devel"
|
||||
fi
|
||||
sdl=no
|
||||
fi
|
||||
if test -n "$cross_prefix" && test "$(basename "$sdlconfig")" = sdl-config; then
|
||||
echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2
|
||||
fi
|
||||
|
||||
sdl_too_old=no
|
||||
if test "$sdl" != "no" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <SDL.h>
|
||||
#undef main /* We don't want SDL to override our main() */
|
||||
@@ -2946,10 +2910,6 @@ EOF
|
||||
fi
|
||||
sdl=no
|
||||
fi # sdl compile test
|
||||
}
|
||||
|
||||
if test "$sdl" != "no" ; then
|
||||
sdl_probe
|
||||
fi
|
||||
|
||||
if test "$sdl" = "yes" ; then
|
||||
@@ -3756,7 +3716,7 @@ fi
|
||||
fdt_required=no
|
||||
for target in $target_list; do
|
||||
case $target in
|
||||
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu)
|
||||
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
|
||||
fdt_required=yes
|
||||
;;
|
||||
esac
|
||||
@@ -3782,22 +3742,22 @@ int main(void) { fdt_first_subnode(0, 0); return 0; }
|
||||
EOF
|
||||
if compile_prog "" "$fdt_libs" ; then
|
||||
# system DTC is good - use it
|
||||
fdt=system
|
||||
fdt=yes
|
||||
else
|
||||
# have GIT checkout, so activate dtc submodule
|
||||
if test -e "${source_path}/.git" ; then
|
||||
git_submodules="${git_submodules} dtc"
|
||||
fi
|
||||
if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then
|
||||
fdt=git
|
||||
fdt=yes
|
||||
dtc_internal="yes"
|
||||
mkdir -p dtc
|
||||
if [ "$pwd_is_source_path" != "y" ] ; then
|
||||
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
|
||||
symlink "$source_path/dtc/scripts" "dtc/scripts"
|
||||
fi
|
||||
fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt"
|
||||
fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt"
|
||||
fdt_libs="$fdt_libs"
|
||||
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
|
||||
elif test "$fdt" = "yes" ; then
|
||||
# Not a git build & no libfdt found, prompt for system install
|
||||
error_exit "DTC (libfdt) version >= 1.4.2 not present." \
|
||||
@@ -4473,7 +4433,7 @@ fi
|
||||
|
||||
# check for smartcard support
|
||||
if test "$smartcard" != "no"; then
|
||||
if $pkg_config --atleast-version=2.5.1 libcacard; then
|
||||
if $pkg_config libcacard; then
|
||||
libcacard_cflags=$($pkg_config --cflags libcacard)
|
||||
libcacard_libs=$($pkg_config --libs libcacard)
|
||||
smartcard="yes"
|
||||
@@ -5087,14 +5047,6 @@ static S2 c2;
|
||||
static S4 c4;
|
||||
static S8 c8;
|
||||
static int i;
|
||||
void helper(void *d, void *a, int shift, int i);
|
||||
void helper(void *d, void *a, int shift, int i)
|
||||
{
|
||||
*(U1 *)(d + i) = *(U1 *)(a + i) << shift;
|
||||
*(U2 *)(d + i) = *(U2 *)(a + i) << shift;
|
||||
*(U4 *)(d + i) = *(U4 *)(a + i) << shift;
|
||||
*(U8 *)(d + i) = *(U8 *)(a + i) << shift;
|
||||
}
|
||||
int main(void)
|
||||
{
|
||||
a1 += b1; a2 += b2; a4 += b4; a8 += b8;
|
||||
@@ -5171,20 +5123,6 @@ if test "$fortify_source" != "no"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
###############################################
|
||||
# Check if copy_file_range is provided by glibc
|
||||
have_copy_file_range=no
|
||||
cat > $TMPC << EOF
|
||||
#include <unistd.h>
|
||||
int main(void) {
|
||||
copy_file_range(0, NULL, 0, NULL, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
have_copy_file_range=yes
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check if struct fsxattr is available via linux/fs.h
|
||||
|
||||
@@ -5200,37 +5138,6 @@ if compile_prog "" "" ; then
|
||||
have_fsxattr=yes
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check for usable membarrier system call
|
||||
if test "$membarrier" = "yes"; then
|
||||
have_membarrier=no
|
||||
if test "$mingw32" = "yes" ; then
|
||||
have_membarrier=yes
|
||||
elif test "$linux" = "yes" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <linux/membarrier.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
int main(void) {
|
||||
syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0);
|
||||
syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0);
|
||||
exit(0);
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
have_membarrier=yes
|
||||
fi
|
||||
fi
|
||||
if test "$have_membarrier" = "no"; then
|
||||
feature_not_found "membarrier" "membarrier system call not available"
|
||||
fi
|
||||
else
|
||||
# Do not enable it by default even for Mingw32, because it doesn't
|
||||
# work on Wine.
|
||||
membarrier=no
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check if rtnetlink.h exists and is useful
|
||||
have_rtnetlink=no
|
||||
@@ -5451,17 +5358,6 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# Docker and cross-compiler support
|
||||
#
|
||||
# This is specifically for building test
|
||||
# cases for foreign architectures, not
|
||||
# cross-compiling QEMU itself.
|
||||
|
||||
if has "docker"; then
|
||||
docker=$($python $source_path/tests/docker/docker.py probe)
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# End of CC checks
|
||||
# After here, no more $cc or $ld runs
|
||||
@@ -5764,7 +5660,6 @@ echo_version() {
|
||||
|
||||
# prepend pixman and ftd flags after all config tests are done
|
||||
QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS"
|
||||
QEMU_LDFLAGS="$fdt_ldflags $QEMU_LDFLAGS"
|
||||
libs_softmmu="$pixman_libs $libs_softmmu"
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
@@ -5795,7 +5690,6 @@ echo "ARFLAGS $ARFLAGS"
|
||||
echo "CFLAGS $CFLAGS"
|
||||
echo "QEMU_CFLAGS $QEMU_CFLAGS"
|
||||
echo "LDFLAGS $LDFLAGS"
|
||||
echo "QEMU_LDFLAGS $QEMU_LDFLAGS"
|
||||
echo "make $make"
|
||||
echo "install $install"
|
||||
echo "python $python"
|
||||
@@ -5870,7 +5764,6 @@ fi
|
||||
echo "malloc trim support $malloc_trim"
|
||||
echo "RDMA support $rdma"
|
||||
echo "fdt support $fdt"
|
||||
echo "membarrier $membarrier"
|
||||
echo "preadv support $preadv"
|
||||
echo "fdatasync $fdatasync"
|
||||
echo "madvise $madvise"
|
||||
@@ -5925,7 +5818,6 @@ echo "avx2 optimization $avx2_opt"
|
||||
echo "replication support $replication"
|
||||
echo "VxHS block device $vxhs"
|
||||
echo "capstone $capstone"
|
||||
echo "docker $docker"
|
||||
|
||||
if test "$sdl_too_old" = "yes"; then
|
||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||
@@ -6128,7 +6020,7 @@ qemu_version=$(head $source_path/VERSION)
|
||||
echo "VERSION=$qemu_version" >>$config_host_mak
|
||||
echo "PKGVERSION=$pkgversion" >>$config_host_mak
|
||||
echo "SRC_PATH=$source_path" >> $config_host_mak
|
||||
echo "TARGET_LIST=$target_list" >> $config_host_mak
|
||||
echo "TARGET_DIRS=$target_list" >> $config_host_mak
|
||||
if [ "$docs" = "yes" ] ; then
|
||||
echo "BUILD_DOCS=yes" >> $config_host_mak
|
||||
fi
|
||||
@@ -6300,9 +6192,6 @@ fi
|
||||
if test "$have_fsxattr" = "yes" ; then
|
||||
echo "HAVE_FSXATTR=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$have_copy_file_range" = "yes" ; then
|
||||
echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vte" = "yes" ; then
|
||||
echo "CONFIG_VTE=y" >> $config_host_mak
|
||||
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
||||
@@ -6359,12 +6248,9 @@ fi
|
||||
if test "$preadv" = "yes" ; then
|
||||
echo "CONFIG_PREADV=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$fdt" != "no" ; then
|
||||
if test "$fdt" = "yes" ; then
|
||||
echo "CONFIG_FDT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$membarrier" = "yes" ; then
|
||||
echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$signalfd" = "yes" ; then
|
||||
echo "CONFIG_SIGNALFD=y" >> $config_host_mak
|
||||
fi
|
||||
@@ -6674,19 +6560,19 @@ if test "$vxhs" = "yes" ; then
|
||||
fi
|
||||
|
||||
if test "$tcg_interpreter" = "yes"; then
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
|
||||
elif test "$ARCH" = "sparc64" ; then
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/sparc $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/sparc $QEMU_INCLUDES"
|
||||
elif test "$ARCH" = "s390x" ; then
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/s390 $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/s390 $QEMU_INCLUDES"
|
||||
elif test "$ARCH" = "x86_64" -o "$ARCH" = "x32" ; then
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES"
|
||||
elif test "$ARCH" = "ppc64" ; then
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES"
|
||||
else
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES"
|
||||
fi
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg $QEMU_INCLUDES"
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg $QEMU_INCLUDES"
|
||||
|
||||
echo "TOOLS=$tools" >> $config_host_mak
|
||||
echo "ROMS=$roms" >> $config_host_mak
|
||||
@@ -6734,7 +6620,6 @@ else
|
||||
fi
|
||||
echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
|
||||
echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak
|
||||
echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak
|
||||
echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak
|
||||
echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak
|
||||
echo "LIBS+=$LIBS" >> $config_host_mak
|
||||
@@ -6753,10 +6638,6 @@ if test "$gcov" = "yes" ; then
|
||||
echo "GCOV=$gcov_tool" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$docker" != "no"; then
|
||||
echo "HAVE_USER_DOCKER=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
# use included Linux headers
|
||||
if test "$linux" = "yes" ; then
|
||||
mkdir -p linux-headers
|
||||
@@ -6870,7 +6751,6 @@ case "$target_name" in
|
||||
microblaze|microblazeel)
|
||||
TARGET_ARCH=microblaze
|
||||
bflt="yes"
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
;;
|
||||
mips|mipsel)
|
||||
TARGET_ARCH=mips
|
||||
@@ -6960,7 +6840,6 @@ case "$target_name" in
|
||||
;;
|
||||
xtensa|xtensaeb)
|
||||
TARGET_ARCH=xtensa
|
||||
mttcg="yes"
|
||||
;;
|
||||
*)
|
||||
error_exit "Unsupported target CPU"
|
||||
@@ -7166,7 +7045,7 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak
|
||||
|
||||
done # for target in $targets
|
||||
|
||||
if [ "$fdt" = "git" ]; then
|
||||
if [ "$dtc_internal" = "yes" ]; then
|
||||
echo "config-host.h: subdir-dtc" >> $config_host_mak
|
||||
fi
|
||||
if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then
|
||||
|
||||
@@ -26,20 +26,9 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/mman.h>
|
||||
#include "qemu/compiler.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/vhost.h>
|
||||
|
||||
#ifdef __NR_userfaultfd
|
||||
#include <linux/userfaultfd.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "qemu/compiler.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
#include "libvhost-user.h"
|
||||
@@ -97,9 +86,6 @@ vu_request_to_string(unsigned int req)
|
||||
REQ(VHOST_USER_SET_VRING_ENDIAN),
|
||||
REQ(VHOST_USER_GET_CONFIG),
|
||||
REQ(VHOST_USER_SET_CONFIG),
|
||||
REQ(VHOST_USER_POSTCOPY_ADVISE),
|
||||
REQ(VHOST_USER_POSTCOPY_LISTEN),
|
||||
REQ(VHOST_USER_POSTCOPY_END),
|
||||
REQ(VHOST_USER_MAX),
|
||||
};
|
||||
#undef REQ
|
||||
@@ -185,35 +171,6 @@ vmsg_close_fds(VhostUserMsg *vmsg)
|
||||
}
|
||||
}
|
||||
|
||||
/* A test to see if we have userfault available */
|
||||
static bool
|
||||
have_userfault(void)
|
||||
{
|
||||
#if defined(__linux__) && defined(__NR_userfaultfd) &&\
|
||||
defined(UFFD_FEATURE_MISSING_SHMEM) &&\
|
||||
defined(UFFD_FEATURE_MISSING_HUGETLBFS)
|
||||
/* Now test the kernel we're running on really has the features */
|
||||
int ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
struct uffdio_api api_struct;
|
||||
if (ufd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = UFFD_FEATURE_MISSING_SHMEM |
|
||||
UFFD_FEATURE_MISSING_HUGETLBFS;
|
||||
if (ioctl(ufd, UFFDIO_API, &api_struct)) {
|
||||
close(ufd);
|
||||
return false;
|
||||
}
|
||||
close(ufd);
|
||||
return true;
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_message_read(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -288,45 +245,23 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *p = (uint8_t *)vmsg;
|
||||
char control[CMSG_SPACE(VHOST_MEMORY_MAX_NREGIONS * sizeof(int))] = { };
|
||||
struct iovec iov = {
|
||||
.iov_base = (char *)vmsg,
|
||||
.iov_len = VHOST_USER_HDR_SIZE,
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = control,
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
memset(control, 0, sizeof(control));
|
||||
assert(vmsg->fd_num <= VHOST_MEMORY_MAX_NREGIONS);
|
||||
if (vmsg->fd_num > 0) {
|
||||
size_t fdsize = vmsg->fd_num * sizeof(int);
|
||||
msg.msg_controllen = CMSG_SPACE(fdsize);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(fdsize);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
memcpy(CMSG_DATA(cmsg), vmsg->fds, fdsize);
|
||||
} else {
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
/* Set the version in the flags when sending the reply */
|
||||
vmsg->flags &= ~VHOST_USER_VERSION_MASK;
|
||||
vmsg->flags |= VHOST_USER_VERSION;
|
||||
vmsg->flags |= VHOST_USER_REPLY_MASK;
|
||||
|
||||
do {
|
||||
rc = sendmsg(conn_fd, &msg, 0);
|
||||
rc = write(conn_fd, p, VHOST_USER_HDR_SIZE);
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
if (vmsg->size) {
|
||||
do {
|
||||
if (vmsg->data) {
|
||||
rc = write(conn_fd, vmsg->data, vmsg->size);
|
||||
} else {
|
||||
rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size);
|
||||
}
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
}
|
||||
do {
|
||||
if (vmsg->data) {
|
||||
rc = write(conn_fd, vmsg->data, vmsg->size);
|
||||
} else {
|
||||
rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size);
|
||||
}
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
if (rc <= 0) {
|
||||
vu_panic(dev, "Error while writing: %s", strerror(errno));
|
||||
@@ -336,39 +271,6 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
/* Set the version in the flags when sending the reply */
|
||||
vmsg->flags &= ~VHOST_USER_VERSION_MASK;
|
||||
vmsg->flags |= VHOST_USER_VERSION;
|
||||
vmsg->flags |= VHOST_USER_REPLY_MASK;
|
||||
|
||||
return vu_message_write(dev, conn_fd, vmsg);
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg)
|
||||
{
|
||||
VhostUserMsg msg_reply;
|
||||
|
||||
if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg_reply.request != vmsg->request) {
|
||||
DPRINT("Received unexpected msg type. Expected %d received %d",
|
||||
vmsg->request, msg_reply.request);
|
||||
return false;
|
||||
}
|
||||
|
||||
return msg_reply.payload.u64 == 0;
|
||||
}
|
||||
|
||||
/* Kick the log_call_fd if required. */
|
||||
static void
|
||||
vu_log_kick(VuDev *dev)
|
||||
@@ -443,7 +345,6 @@ vu_get_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
}
|
||||
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
DPRINT("Sending back to guest u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
||||
|
||||
@@ -508,148 +409,6 @@ vu_reset_device_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
int i;
|
||||
VhostUserMemory *memory = &vmsg->payload.memory;
|
||||
dev->nregions = memory->nregions;
|
||||
|
||||
DPRINT("Nregions: %d\n", memory->nregions);
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
void *mmap_addr;
|
||||
VhostUserMemoryRegion *msg_region = &memory->regions[i];
|
||||
VuDevRegion *dev_region = &dev->regions[i];
|
||||
|
||||
DPRINT("Region %d\n", i);
|
||||
DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n",
|
||||
msg_region->guest_phys_addr);
|
||||
DPRINT(" memory_size: 0x%016"PRIx64"\n",
|
||||
msg_region->memory_size);
|
||||
DPRINT(" userspace_addr 0x%016"PRIx64"\n",
|
||||
msg_region->userspace_addr);
|
||||
DPRINT(" mmap_offset 0x%016"PRIx64"\n",
|
||||
msg_region->mmap_offset);
|
||||
|
||||
dev_region->gpa = msg_region->guest_phys_addr;
|
||||
dev_region->size = msg_region->memory_size;
|
||||
dev_region->qva = msg_region->userspace_addr;
|
||||
dev_region->mmap_offset = msg_region->mmap_offset;
|
||||
|
||||
/* We don't use offset argument of mmap() since the
|
||||
* mapped address has to be page aligned, and we use huge
|
||||
* pages.
|
||||
* In postcopy we're using PROT_NONE here to catch anyone
|
||||
* accessing it before we userfault
|
||||
*/
|
||||
mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset,
|
||||
PROT_NONE, MAP_SHARED,
|
||||
vmsg->fds[i], 0);
|
||||
|
||||
if (mmap_addr == MAP_FAILED) {
|
||||
vu_panic(dev, "region mmap error: %s", strerror(errno));
|
||||
} else {
|
||||
dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr;
|
||||
DPRINT(" mmap_addr: 0x%016"PRIx64"\n",
|
||||
dev_region->mmap_addr);
|
||||
}
|
||||
|
||||
/* Return the address to QEMU so that it can translate the ufd
|
||||
* fault addresses back.
|
||||
*/
|
||||
msg_region->userspace_addr = (uintptr_t)(mmap_addr +
|
||||
dev_region->mmap_offset);
|
||||
close(vmsg->fds[i]);
|
||||
}
|
||||
|
||||
/* Send the message back to qemu with the addresses filled in */
|
||||
vmsg->fd_num = 0;
|
||||
if (!vu_send_reply(dev, dev->sock, vmsg)) {
|
||||
vu_panic(dev, "failed to respond to set-mem-table for postcopy");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait for QEMU to confirm that it's registered the handler for the
|
||||
* faults.
|
||||
*/
|
||||
if (!vu_message_read(dev, dev->sock, vmsg) ||
|
||||
vmsg->size != sizeof(vmsg->payload.u64) ||
|
||||
vmsg->payload.u64 != 0) {
|
||||
vu_panic(dev, "failed to receive valid ack for postcopy set-mem-table");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* OK, now we can go and register the memory and generate faults */
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
VuDevRegion *dev_region = &dev->regions[i];
|
||||
int ret;
|
||||
#ifdef UFFDIO_REGISTER
|
||||
/* We should already have an open ufd. Mark each memory
|
||||
* range as ufd.
|
||||
* Discard any mapping we have here; note I can't use MADV_REMOVE
|
||||
* or fallocate to make the hole since I don't want to lose
|
||||
* data that's already arrived in the shared process.
|
||||
* TODO: How to do hugepage
|
||||
*/
|
||||
ret = madvise((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
MADV_DONTNEED);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"%s: Failed to madvise(DONTNEED) region %d: %s\n",
|
||||
__func__, i, strerror(errno));
|
||||
}
|
||||
/* Turn off transparent hugepages so we dont get lose wakeups
|
||||
* in neighbouring pages.
|
||||
* TODO: Turn this backon later.
|
||||
*/
|
||||
ret = madvise((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
MADV_NOHUGEPAGE);
|
||||
if (ret) {
|
||||
/* Note: This can happen legally on kernels that are configured
|
||||
* without madvise'able hugepages
|
||||
*/
|
||||
fprintf(stderr,
|
||||
"%s: Failed to madvise(NOHUGEPAGE) region %d: %s\n",
|
||||
__func__, i, strerror(errno));
|
||||
}
|
||||
struct uffdio_register reg_struct;
|
||||
reg_struct.range.start = (uintptr_t)dev_region->mmap_addr;
|
||||
reg_struct.range.len = dev_region->size + dev_region->mmap_offset;
|
||||
reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;
|
||||
|
||||
if (ioctl(dev->postcopy_ufd, UFFDIO_REGISTER, ®_struct)) {
|
||||
vu_panic(dev, "%s: Failed to userfault region %d "
|
||||
"@%p + size:%zx offset: %zx: (ufd=%d)%s\n",
|
||||
__func__, i,
|
||||
dev_region->mmap_addr,
|
||||
dev_region->size, dev_region->mmap_offset,
|
||||
dev->postcopy_ufd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (!(reg_struct.ioctls & ((__u64)1 << _UFFDIO_COPY))) {
|
||||
vu_panic(dev, "%s Region (%d) doesn't support COPY",
|
||||
__func__, i);
|
||||
return false;
|
||||
}
|
||||
DPRINT("%s: region %d: Registered userfault for %llx + %llx\n",
|
||||
__func__, i, reg_struct.range.start, reg_struct.range.len);
|
||||
/* Now it's registered we can let the client at it */
|
||||
if (mprotect((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
PROT_READ | PROT_WRITE)) {
|
||||
vu_panic(dev, "failed to mprotect region %d for postcopy (%s)",
|
||||
i, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
/* TODO: Stash 'zero' support flags somewhere */
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -666,10 +425,6 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
}
|
||||
dev->nregions = memory->nregions;
|
||||
|
||||
if (dev->postcopy_listening) {
|
||||
return vu_set_mem_table_exec_postcopy(dev, vmsg);
|
||||
}
|
||||
|
||||
DPRINT("Nregions: %d\n", memory->nregions);
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
void *mmap_addr;
|
||||
@@ -745,7 +500,6 @@ vu_set_log_base_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
dev->log_size = log_mmap_size;
|
||||
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -944,41 +698,6 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq,
|
||||
}
|
||||
}
|
||||
|
||||
bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd,
|
||||
int size, int offset)
|
||||
{
|
||||
int qidx = vq - dev->vq;
|
||||
int fd_num = 0;
|
||||
VhostUserMsg vmsg = {
|
||||
.request = VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG,
|
||||
.flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK,
|
||||
.size = sizeof(vmsg.payload.area),
|
||||
.payload.area = {
|
||||
.u64 = qidx & VHOST_USER_VRING_IDX_MASK,
|
||||
.size = size,
|
||||
.offset = offset,
|
||||
},
|
||||
};
|
||||
|
||||
if (fd == -1) {
|
||||
vmsg.payload.area.u64 |= VHOST_USER_VRING_NOFD_MASK;
|
||||
} else {
|
||||
vmsg.fds[fd_num++] = fd;
|
||||
}
|
||||
|
||||
vmsg.fd_num = fd_num;
|
||||
|
||||
if ((dev->protocol_features & VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vu_message_write(dev, dev->slave_fd, &vmsg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return vu_process_message_reply(dev, &vmsg);
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -1031,13 +750,7 @@ static bool
|
||||
vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD |
|
||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ |
|
||||
1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER |
|
||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD;
|
||||
|
||||
if (have_userfault()) {
|
||||
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
|
||||
}
|
||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ;
|
||||
|
||||
if (dev->iface->get_protocol_features) {
|
||||
features |= dev->iface->get_protocol_features(dev);
|
||||
@@ -1045,7 +758,6 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
|
||||
vmsg->payload.u64 = features;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1144,77 +856,6 @@ vu_set_config(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_advise(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
dev->postcopy_ufd = -1;
|
||||
#ifdef UFFDIO_API
|
||||
struct uffdio_api api_struct;
|
||||
|
||||
dev->postcopy_ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
vmsg->size = 0;
|
||||
#endif
|
||||
|
||||
if (dev->postcopy_ufd == -1) {
|
||||
vu_panic(dev, "Userfaultfd not available: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef UFFDIO_API
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = 0;
|
||||
if (ioctl(dev->postcopy_ufd, UFFDIO_API, &api_struct)) {
|
||||
vu_panic(dev, "Failed UFFDIO_API: %s", strerror(errno));
|
||||
close(dev->postcopy_ufd);
|
||||
dev->postcopy_ufd = -1;
|
||||
goto out;
|
||||
}
|
||||
/* TODO: Stash feature flags somewhere */
|
||||
#endif
|
||||
|
||||
out:
|
||||
/* Return a ufd to the QEMU */
|
||||
vmsg->fd_num = 1;
|
||||
vmsg->fds[0] = dev->postcopy_ufd;
|
||||
return true; /* = send a reply */
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_listen(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
vmsg->payload.u64 = -1;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
|
||||
if (dev->nregions) {
|
||||
vu_panic(dev, "Regions already registered at postcopy-listen");
|
||||
return true;
|
||||
}
|
||||
dev->postcopy_listening = true;
|
||||
|
||||
vmsg->flags = VHOST_USER_VERSION | VHOST_USER_REPLY_MASK;
|
||||
vmsg->payload.u64 = 0; /* Success */
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_end(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
DPRINT("%s: Entry\n", __func__);
|
||||
dev->postcopy_listening = false;
|
||||
if (dev->postcopy_ufd > 0) {
|
||||
close(dev->postcopy_ufd);
|
||||
dev->postcopy_ufd = -1;
|
||||
DPRINT("%s: Done close\n", __func__);
|
||||
}
|
||||
|
||||
vmsg->fd_num = 0;
|
||||
vmsg->payload.u64 = 0;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->flags = VHOST_USER_VERSION | VHOST_USER_REPLY_MASK;
|
||||
DPRINT("%s: exit\n", __func__);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -1286,12 +927,6 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return vu_set_config(dev, vmsg);
|
||||
case VHOST_USER_NONE:
|
||||
break;
|
||||
case VHOST_USER_POSTCOPY_ADVISE:
|
||||
return vu_set_postcopy_advise(dev, vmsg);
|
||||
case VHOST_USER_POSTCOPY_LISTEN:
|
||||
return vu_set_postcopy_listen(dev, vmsg);
|
||||
case VHOST_USER_POSTCOPY_END:
|
||||
return vu_set_postcopy_end(dev, vmsg);
|
||||
default:
|
||||
vmsg_close_fds(vmsg);
|
||||
vu_panic(dev, "Unhandled request: %d", vmsg->request);
|
||||
@@ -1317,7 +952,7 @@ vu_dispatch(VuDev *dev)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!vu_send_reply(dev, dev->sock, &vmsg)) {
|
||||
if (!vu_message_write(dev, dev->sock, &vmsg)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,6 @@ enum VhostUserProtocolFeature {
|
||||
VHOST_USER_PROTOCOL_F_NET_MTU = 4,
|
||||
VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
|
||||
VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
|
||||
VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
||||
VHOST_USER_PROTOCOL_F_CONFIG = 9,
|
||||
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
|
||||
VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
|
||||
|
||||
VHOST_USER_PROTOCOL_F_MAX
|
||||
};
|
||||
@@ -86,22 +81,9 @@ typedef enum VhostUserRequest {
|
||||
VHOST_USER_SET_VRING_ENDIAN = 23,
|
||||
VHOST_USER_GET_CONFIG = 24,
|
||||
VHOST_USER_SET_CONFIG = 25,
|
||||
VHOST_USER_CREATE_CRYPTO_SESSION = 26,
|
||||
VHOST_USER_CLOSE_CRYPTO_SESSION = 27,
|
||||
VHOST_USER_POSTCOPY_ADVISE = 28,
|
||||
VHOST_USER_POSTCOPY_LISTEN = 29,
|
||||
VHOST_USER_POSTCOPY_END = 30,
|
||||
VHOST_USER_MAX
|
||||
} VhostUserRequest;
|
||||
|
||||
typedef enum VhostUserSlaveRequest {
|
||||
VHOST_USER_SLAVE_NONE = 0,
|
||||
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
||||
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
|
||||
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
|
||||
VHOST_USER_SLAVE_MAX
|
||||
} VhostUserSlaveRequest;
|
||||
|
||||
typedef struct VhostUserMemoryRegion {
|
||||
uint64_t guest_phys_addr;
|
||||
uint64_t memory_size;
|
||||
@@ -132,12 +114,6 @@ static VhostUserConfig c __attribute__ ((unused));
|
||||
+ sizeof(c.size) \
|
||||
+ sizeof(c.flags))
|
||||
|
||||
typedef struct VhostUserVringArea {
|
||||
uint64_t u64;
|
||||
uint64_t size;
|
||||
uint64_t offset;
|
||||
} VhostUserVringArea;
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define VU_PACKED __attribute__((gcc_struct, packed))
|
||||
#else
|
||||
@@ -149,7 +125,6 @@ typedef struct VhostUserMsg {
|
||||
|
||||
#define VHOST_USER_VERSION_MASK (0x3)
|
||||
#define VHOST_USER_REPLY_MASK (0x1 << 2)
|
||||
#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3)
|
||||
uint32_t flags;
|
||||
uint32_t size; /* the following payload size */
|
||||
|
||||
@@ -162,7 +137,6 @@ typedef struct VhostUserMsg {
|
||||
VhostUserMemory memory;
|
||||
VhostUserLog log;
|
||||
VhostUserConfig config;
|
||||
VhostUserVringArea area;
|
||||
} payload;
|
||||
|
||||
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
||||
@@ -303,10 +277,6 @@ struct VuDev {
|
||||
* re-initialize */
|
||||
vu_panic_cb panic;
|
||||
const VuDevIface *iface;
|
||||
|
||||
/* Postcopy data */
|
||||
int postcopy_ufd;
|
||||
bool postcopy_listening;
|
||||
};
|
||||
|
||||
typedef struct VuVirtqElement {
|
||||
@@ -386,20 +356,6 @@ VuVirtq *vu_get_queue(VuDev *dev, int qidx);
|
||||
void vu_set_queue_handler(VuDev *dev, VuVirtq *vq,
|
||||
vu_queue_handler_cb handler);
|
||||
|
||||
/**
|
||||
* vu_set_queue_host_notifier:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @fd: a file descriptor
|
||||
* @size: host page size
|
||||
* @offset: notifier offset in @fd file
|
||||
*
|
||||
* Set queue's host notifier. This function may be called several
|
||||
* times for the same queue. If called with -1 @fd, the notifier
|
||||
* is removed.
|
||||
*/
|
||||
bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd,
|
||||
int size, int offset);
|
||||
|
||||
/**
|
||||
* vu_queue_set_notification:
|
||||
|
||||
@@ -31,7 +31,6 @@ typedef struct VubDev {
|
||||
VugDev parent;
|
||||
int blk_fd;
|
||||
struct virtio_blk_config blkcfg;
|
||||
bool enable_ro;
|
||||
char *blk_name;
|
||||
GMainLoop *loop;
|
||||
} VubDev;
|
||||
@@ -302,33 +301,14 @@ static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
|
||||
static uint64_t
|
||||
vub_get_features(VuDev *dev)
|
||||
{
|
||||
uint64_t features;
|
||||
VugDev *gdev;
|
||||
VubDev *vdev_blk;
|
||||
|
||||
gdev = container_of(dev, VugDev, parent);
|
||||
vdev_blk = container_of(gdev, VubDev, parent);
|
||||
|
||||
features = 1ull << VIRTIO_BLK_F_SIZE_MAX |
|
||||
1ull << VIRTIO_BLK_F_SEG_MAX |
|
||||
1ull << VIRTIO_BLK_F_TOPOLOGY |
|
||||
1ull << VIRTIO_BLK_F_BLK_SIZE |
|
||||
1ull << VIRTIO_BLK_F_FLUSH |
|
||||
1ull << VIRTIO_BLK_F_CONFIG_WCE |
|
||||
1ull << VIRTIO_F_VERSION_1 |
|
||||
1ull << VHOST_USER_F_PROTOCOL_FEATURES;
|
||||
|
||||
if (vdev_blk->enable_ro) {
|
||||
features |= 1ull << VIRTIO_BLK_F_RO;
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
vub_get_protocol_features(VuDev *dev)
|
||||
{
|
||||
return 1ull << VHOST_USER_PROTOCOL_F_CONFIG;
|
||||
return 1ull << VIRTIO_BLK_F_SIZE_MAX |
|
||||
1ull << VIRTIO_BLK_F_SEG_MAX |
|
||||
1ull << VIRTIO_BLK_F_TOPOLOGY |
|
||||
1ull << VIRTIO_BLK_F_BLK_SIZE |
|
||||
1ull << VIRTIO_BLK_F_FLUSH |
|
||||
1ull << VIRTIO_BLK_F_CONFIG_WCE |
|
||||
1ull << VIRTIO_F_VERSION_1 |
|
||||
1ull << VHOST_USER_F_PROTOCOL_FEATURES;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -393,7 +373,6 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data,
|
||||
static const VuDevIface vub_iface = {
|
||||
.get_features = vub_get_features,
|
||||
.queue_set_started = vub_queue_set_started,
|
||||
.get_protocol_features = vub_get_protocol_features,
|
||||
.get_config = vub_get_config,
|
||||
.set_config = vub_set_config,
|
||||
};
|
||||
@@ -490,7 +469,6 @@ vub_new(char *blk_file)
|
||||
vub_free(vdev_blk);
|
||||
return NULL;
|
||||
}
|
||||
vdev_blk->enable_ro = false;
|
||||
vdev_blk->blkcfg.wce = 0;
|
||||
vdev_blk->blk_name = blk_file;
|
||||
|
||||
@@ -505,11 +483,10 @@ int main(int argc, char **argv)
|
||||
int opt;
|
||||
char *unix_socket = NULL;
|
||||
char *blk_file = NULL;
|
||||
bool enable_ro = false;
|
||||
int lsock = -1, csock = -1;
|
||||
VubDev *vdev_blk = NULL;
|
||||
|
||||
while ((opt = getopt(argc, argv, "b:rs:h")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "b:s:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
blk_file = g_strdup(optarg);
|
||||
@@ -517,20 +494,17 @@ int main(int argc, char **argv)
|
||||
case 's':
|
||||
unix_socket = g_strdup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
enable_ro = true;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
|
||||
" | -r Enable read-only ] | [ -h ]\n", argv[0]);
|
||||
printf("Usage: %s [-b block device or file, -s UNIX domain socket]"
|
||||
" | [ -h ]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unix_socket || !blk_file) {
|
||||
printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
|
||||
" | -r Enable read-only ] | [ -h ]\n", argv[0]);
|
||||
printf("Usage: %s [-b block device or file, -s UNIX domain socket] |"
|
||||
" [ -h ]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -549,9 +523,6 @@ int main(int argc, char **argv)
|
||||
if (!vdev_blk) {
|
||||
goto err;
|
||||
}
|
||||
if (enable_ro) {
|
||||
vdev_blk->enable_ro = true;
|
||||
}
|
||||
|
||||
vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);
|
||||
|
||||
|
||||
125
cpus.c
125
cpus.c
@@ -892,19 +892,11 @@ void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
|
||||
return;
|
||||
}
|
||||
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
/* A CPU is currently running; kick it back out to the
|
||||
* tcg_cpu_exec() loop so it will recalculate its
|
||||
* icount deadline immediately.
|
||||
*/
|
||||
qemu_cpu_kick(current_cpu);
|
||||
} else if (first_cpu) {
|
||||
if (!qemu_in_vcpu_thread() && first_cpu) {
|
||||
/* qemu_cpu_kick is not enough to kick a halted CPU out of
|
||||
* qemu_tcg_wait_io_event. async_run_on_cpu, instead,
|
||||
* causes cpu_thread_is_idle to return false. This way,
|
||||
* handle_icount_deadline can run.
|
||||
* If we have no CPUs at all for some reason, we don't
|
||||
* need to do anything.
|
||||
*/
|
||||
async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL);
|
||||
}
|
||||
@@ -1325,8 +1317,6 @@ static void prepare_icount_for_run(CPUState *cpu)
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_extra = cpu->icount_budget - insns_left;
|
||||
|
||||
replay_mutex_lock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,8 +1332,6 @@ static void process_icount_data(CPUState *cpu)
|
||||
cpu->icount_budget = 0;
|
||||
|
||||
replay_account_executed_instructions();
|
||||
|
||||
replay_mutex_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1358,9 +1346,11 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
#ifdef CONFIG_PROFILER
|
||||
ti = profile_getclock();
|
||||
#endif
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu_exec_start(cpu);
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
#ifdef CONFIG_PROFILER
|
||||
tcg_time += profile_getclock() - ti;
|
||||
#endif
|
||||
@@ -1427,9 +1417,6 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
cpu->exit_request = 1;
|
||||
|
||||
while (1) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
replay_mutex_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||
qemu_account_warp_timer();
|
||||
|
||||
@@ -1438,8 +1425,6 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
*/
|
||||
handle_icount_deadline();
|
||||
|
||||
replay_mutex_unlock();
|
||||
|
||||
if (!cpu) {
|
||||
cpu = first_cpu;
|
||||
}
|
||||
@@ -1455,13 +1440,11 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
prepare_icount_for_run(cpu);
|
||||
|
||||
r = tcg_cpu_exec(cpu);
|
||||
|
||||
process_icount_data(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
@@ -1648,12 +1631,10 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
/* process any pending work */
|
||||
cpu->exit_request = 1;
|
||||
|
||||
do {
|
||||
while (1) {
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
qemu_mutex_unlock_iothread();
|
||||
r = tcg_cpu_exec(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
switch (r) {
|
||||
case EXCP_DEBUG:
|
||||
cpu_handle_guest_debug(cpu);
|
||||
@@ -1800,21 +1781,12 @@ void pause_all_vcpus(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to drop the replay_lock so any vCPU threads woken up
|
||||
* can finish their replay tasks
|
||||
*/
|
||||
replay_mutex_unlock();
|
||||
|
||||
while (!all_vcpus_paused()) {
|
||||
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
|
||||
CPU_FOREACH(cpu) {
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
replay_mutex_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
void cpu_resume(CPUState *cpu)
|
||||
@@ -2043,6 +2015,7 @@ int vm_stop(RunState state)
|
||||
int vm_prepare_start(void)
|
||||
{
|
||||
RunState requested;
|
||||
int res = 0;
|
||||
|
||||
qemu_vmstop_requested(&requested);
|
||||
if (runstate_is_running() && requested == RUN_STATE__MAX) {
|
||||
@@ -2056,18 +2029,17 @@ int vm_prepare_start(void)
|
||||
*/
|
||||
if (runstate_is_running()) {
|
||||
qapi_event_send_stop(&error_abort);
|
||||
qapi_event_send_resume(&error_abort);
|
||||
return -1;
|
||||
res = -1;
|
||||
} else {
|
||||
replay_enable_events();
|
||||
cpu_enable_ticks();
|
||||
runstate_set(RUN_STATE_RUNNING);
|
||||
vm_state_notify(1, RUN_STATE_RUNNING);
|
||||
}
|
||||
|
||||
/* We are sending this now, but the CPUs will be resumed shortly later */
|
||||
qapi_event_send_resume(&error_abort);
|
||||
|
||||
replay_enable_events();
|
||||
cpu_enable_ticks();
|
||||
runstate_set(RUN_STATE_RUNNING);
|
||||
vm_state_notify(1, RUN_STATE_RUNNING);
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
void vm_start(void)
|
||||
@@ -2187,59 +2159,6 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
return head;
|
||||
}
|
||||
|
||||
static CpuInfoArch sysemu_target_to_cpuinfo_arch(SysEmuTarget target)
|
||||
{
|
||||
/*
|
||||
* The @SysEmuTarget -> @CpuInfoArch mapping below is based on the
|
||||
* TARGET_ARCH -> TARGET_BASE_ARCH mapping in the "configure" script.
|
||||
*/
|
||||
switch (target) {
|
||||
case SYS_EMU_TARGET_I386:
|
||||
case SYS_EMU_TARGET_X86_64:
|
||||
return CPU_INFO_ARCH_X86;
|
||||
|
||||
case SYS_EMU_TARGET_PPC:
|
||||
case SYS_EMU_TARGET_PPCEMB:
|
||||
case SYS_EMU_TARGET_PPC64:
|
||||
return CPU_INFO_ARCH_PPC;
|
||||
|
||||
case SYS_EMU_TARGET_SPARC:
|
||||
case SYS_EMU_TARGET_SPARC64:
|
||||
return CPU_INFO_ARCH_SPARC;
|
||||
|
||||
case SYS_EMU_TARGET_MIPS:
|
||||
case SYS_EMU_TARGET_MIPSEL:
|
||||
case SYS_EMU_TARGET_MIPS64:
|
||||
case SYS_EMU_TARGET_MIPS64EL:
|
||||
return CPU_INFO_ARCH_MIPS;
|
||||
|
||||
case SYS_EMU_TARGET_TRICORE:
|
||||
return CPU_INFO_ARCH_TRICORE;
|
||||
|
||||
case SYS_EMU_TARGET_S390X:
|
||||
return CPU_INFO_ARCH_S390;
|
||||
|
||||
case SYS_EMU_TARGET_RISCV32:
|
||||
case SYS_EMU_TARGET_RISCV64:
|
||||
return CPU_INFO_ARCH_RISCV;
|
||||
|
||||
default:
|
||||
return CPU_INFO_ARCH_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu)
|
||||
{
|
||||
#ifdef TARGET_S390X
|
||||
S390CPU *s390_cpu = S390_CPU(cpu);
|
||||
CPUS390XState *env = &s390_cpu->env;
|
||||
|
||||
info->cpu_state = env->cpu_state;
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* fast means: we NEVER interrupt vCPU threads to retrieve
|
||||
* information from KVM.
|
||||
@@ -2249,9 +2168,11 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
CpuInfoFastList *head = NULL, *cur_item = NULL;
|
||||
SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME,
|
||||
-1, &error_abort);
|
||||
CPUState *cpu;
|
||||
#if defined(TARGET_S390X)
|
||||
S390CPU *s390_cpu;
|
||||
CPUS390XState *env;
|
||||
#endif
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
CpuInfoFastList *info = g_malloc0(sizeof(*info));
|
||||
@@ -2269,14 +2190,12 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
|
||||
info->value->props = props;
|
||||
}
|
||||
|
||||
info->value->arch = sysemu_target_to_cpuinfo_arch(target);
|
||||
info->value->target = target;
|
||||
if (target == SYS_EMU_TARGET_S390X) {
|
||||
cpustate_to_cpuinfo_s390(&info->value->u.s390x, cpu);
|
||||
} else {
|
||||
/* do nothing for @CpuInfoOther */
|
||||
}
|
||||
|
||||
#if defined(TARGET_S390X)
|
||||
s390_cpu = S390_CPU(cpu);
|
||||
env = &s390_cpu->env;
|
||||
info->value->arch = CPU_INFO_ARCH_S390;
|
||||
info->value->u.s390.cpu_state = env->cpu_state;
|
||||
#endif
|
||||
if (!cur_item) {
|
||||
head = cur_item = info;
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
||||
#include "block-luks.h"
|
||||
#include "crypto/block-luks.h"
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "crypto/afsplit.h"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user