Compare commits
5 Commits
x86-for-3.
...
rm-protoco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6321d78dc5 | ||
|
|
a0974f99aa | ||
|
|
42883c01bf | ||
|
|
7966c2b312 | ||
|
|
5888011244 |
@@ -1,10 +1,4 @@
|
|||||||
# EditorConfig is a file format and collection of text editor plugins
|
# http://editorconfig.org
|
||||||
# for maintaining consistent coding styles between different editors
|
|
||||||
# and IDEs. Most popular editors support this either natively or via
|
|
||||||
# plugin.
|
|
||||||
#
|
|
||||||
# Check https://editorconfig.org for details.
|
|
||||||
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
@@ -12,23 +6,10 @@ end_of_line = lf
|
|||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
[*.mak]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 8
|
|
||||||
file_type_emacs = makefile
|
|
||||||
|
|
||||||
[Makefile*]
|
[Makefile*]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 8
|
indent_size = 8
|
||||||
file_type_emacs = makefile
|
|
||||||
|
|
||||||
[*.{c,h}]
|
[*.{c,h}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
[*.{vert,frag}]
|
|
||||||
file_type_emacs = glsl
|
|
||||||
|
|
||||||
[*.json]
|
|
||||||
indent_style = space
|
|
||||||
file_type_emacs = python
|
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -36,7 +36,6 @@
|
|||||||
/qapi/qapi-commands-common.[ch]
|
/qapi/qapi-commands-common.[ch]
|
||||||
/qapi/qapi-commands-crypto.[ch]
|
/qapi/qapi-commands-crypto.[ch]
|
||||||
/qapi/qapi-commands-introspect.[ch]
|
/qapi/qapi-commands-introspect.[ch]
|
||||||
/qapi/qapi-commands-job.[ch]
|
|
||||||
/qapi/qapi-commands-migration.[ch]
|
/qapi/qapi-commands-migration.[ch]
|
||||||
/qapi/qapi-commands-misc.[ch]
|
/qapi/qapi-commands-misc.[ch]
|
||||||
/qapi/qapi-commands-net.[ch]
|
/qapi/qapi-commands-net.[ch]
|
||||||
@@ -54,7 +53,6 @@
|
|||||||
/qapi/qapi-events-common.[ch]
|
/qapi/qapi-events-common.[ch]
|
||||||
/qapi/qapi-events-crypto.[ch]
|
/qapi/qapi-events-crypto.[ch]
|
||||||
/qapi/qapi-events-introspect.[ch]
|
/qapi/qapi-events-introspect.[ch]
|
||||||
/qapi/qapi-events-job.[ch]
|
|
||||||
/qapi/qapi-events-migration.[ch]
|
/qapi/qapi-events-migration.[ch]
|
||||||
/qapi/qapi-events-misc.[ch]
|
/qapi/qapi-events-misc.[ch]
|
||||||
/qapi/qapi-events-net.[ch]
|
/qapi/qapi-events-net.[ch]
|
||||||
@@ -73,7 +71,6 @@
|
|||||||
/qapi/qapi-types-common.[ch]
|
/qapi/qapi-types-common.[ch]
|
||||||
/qapi/qapi-types-crypto.[ch]
|
/qapi/qapi-types-crypto.[ch]
|
||||||
/qapi/qapi-types-introspect.[ch]
|
/qapi/qapi-types-introspect.[ch]
|
||||||
/qapi/qapi-types-job.[ch]
|
|
||||||
/qapi/qapi-types-migration.[ch]
|
/qapi/qapi-types-migration.[ch]
|
||||||
/qapi/qapi-types-misc.[ch]
|
/qapi/qapi-types-misc.[ch]
|
||||||
/qapi/qapi-types-net.[ch]
|
/qapi/qapi-types-net.[ch]
|
||||||
@@ -91,7 +88,6 @@
|
|||||||
/qapi/qapi-visit-common.[ch]
|
/qapi/qapi-visit-common.[ch]
|
||||||
/qapi/qapi-visit-crypto.[ch]
|
/qapi/qapi-visit-crypto.[ch]
|
||||||
/qapi/qapi-visit-introspect.[ch]
|
/qapi/qapi-visit-introspect.[ch]
|
||||||
/qapi/qapi-visit-job.[ch]
|
|
||||||
/qapi/qapi-visit-migration.[ch]
|
/qapi/qapi-visit-migration.[ch]
|
||||||
/qapi/qapi-visit-misc.[ch]
|
/qapi/qapi-visit-misc.[ch]
|
||||||
/qapi/qapi-visit-net.[ch]
|
/qapi/qapi-visit-net.[ch]
|
||||||
@@ -107,7 +103,6 @@
|
|||||||
/qemu-doc.html
|
/qemu-doc.html
|
||||||
/qemu-doc.info
|
/qemu-doc.info
|
||||||
/qemu-doc.txt
|
/qemu-doc.txt
|
||||||
/qemu-edid
|
|
||||||
/qemu-img
|
/qemu-img
|
||||||
/qemu-nbd
|
/qemu-nbd
|
||||||
/qemu-options.def
|
/qemu-options.def
|
||||||
@@ -156,7 +151,6 @@
|
|||||||
.sdk
|
.sdk
|
||||||
*.gcda
|
*.gcda
|
||||||
*.gcno
|
*.gcno
|
||||||
*.gcov
|
|
||||||
/pc-bios/bios-pq/status
|
/pc-bios/bios-pq/status
|
||||||
/pc-bios/vgabios-pq/status
|
/pc-bios/vgabios-pq/status
|
||||||
/pc-bios/optionrom/linuxboot.asm
|
/pc-bios/optionrom/linuxboot.asm
|
||||||
@@ -212,4 +206,3 @@ trace-dtrace-root.h
|
|||||||
trace-dtrace-root.dtrace
|
trace-dtrace-root.dtrace
|
||||||
trace-ust-all.h
|
trace-ust-all.h
|
||||||
trace-ust-all.c
|
trace-ust-all.c
|
||||||
/target/arm/decode-sve.inc.c
|
|
||||||
|
|||||||
39
.gitmodules
vendored
39
.gitmodules
vendored
@@ -1,51 +1,48 @@
|
|||||||
|
[submodule "roms/vgabios"]
|
||||||
|
path = roms/vgabios
|
||||||
|
url = git://git.qemu-project.org/vgabios.git/
|
||||||
[submodule "roms/seabios"]
|
[submodule "roms/seabios"]
|
||||||
path = roms/seabios
|
path = roms/seabios
|
||||||
url = https://git.qemu.org/git/seabios.git/
|
url = git://git.qemu-project.org/seabios.git/
|
||||||
[submodule "roms/SLOF"]
|
[submodule "roms/SLOF"]
|
||||||
path = roms/SLOF
|
path = roms/SLOF
|
||||||
url = https://git.qemu.org/git/SLOF.git
|
url = git://git.qemu-project.org/SLOF.git
|
||||||
[submodule "roms/ipxe"]
|
[submodule "roms/ipxe"]
|
||||||
path = roms/ipxe
|
path = roms/ipxe
|
||||||
url = https://git.qemu.org/git/ipxe.git
|
url = git://git.qemu-project.org/ipxe.git
|
||||||
[submodule "roms/openbios"]
|
[submodule "roms/openbios"]
|
||||||
path = roms/openbios
|
path = roms/openbios
|
||||||
url = https://git.qemu.org/git/openbios.git
|
url = git://git.qemu-project.org/openbios.git
|
||||||
[submodule "roms/openhackware"]
|
[submodule "roms/openhackware"]
|
||||||
path = roms/openhackware
|
path = roms/openhackware
|
||||||
url = https://git.qemu.org/git/openhackware.git
|
url = git://git.qemu-project.org/openhackware.git
|
||||||
[submodule "roms/qemu-palcode"]
|
[submodule "roms/qemu-palcode"]
|
||||||
path = roms/qemu-palcode
|
path = roms/qemu-palcode
|
||||||
url = https://git.qemu.org/git/qemu-palcode.git
|
url = git://github.com/rth7680/qemu-palcode.git
|
||||||
[submodule "roms/sgabios"]
|
[submodule "roms/sgabios"]
|
||||||
path = roms/sgabios
|
path = roms/sgabios
|
||||||
url = https://git.qemu.org/git/sgabios.git
|
url = git://git.qemu-project.org/sgabios.git
|
||||||
[submodule "dtc"]
|
[submodule "dtc"]
|
||||||
path = dtc
|
path = dtc
|
||||||
url = https://git.qemu.org/git/dtc.git
|
url = git://git.qemu-project.org/dtc.git
|
||||||
[submodule "roms/u-boot"]
|
[submodule "roms/u-boot"]
|
||||||
path = roms/u-boot
|
path = roms/u-boot
|
||||||
url = https://git.qemu.org/git/u-boot.git
|
url = git://git.qemu-project.org/u-boot.git
|
||||||
[submodule "roms/skiboot"]
|
[submodule "roms/skiboot"]
|
||||||
path = roms/skiboot
|
path = roms/skiboot
|
||||||
url = https://git.qemu.org/git/skiboot.git
|
url = git://git.qemu.org/skiboot.git
|
||||||
[submodule "roms/QemuMacDrivers"]
|
[submodule "roms/QemuMacDrivers"]
|
||||||
path = roms/QemuMacDrivers
|
path = roms/QemuMacDrivers
|
||||||
url = https://git.qemu.org/git/QemuMacDrivers.git
|
url = git://git.qemu.org/QemuMacDrivers.git
|
||||||
[submodule "ui/keycodemapdb"]
|
[submodule "ui/keycodemapdb"]
|
||||||
path = ui/keycodemapdb
|
path = ui/keycodemapdb
|
||||||
url = https://git.qemu.org/git/keycodemapdb.git
|
url = git://git.qemu.org/keycodemapdb.git
|
||||||
[submodule "capstone"]
|
[submodule "capstone"]
|
||||||
path = capstone
|
path = capstone
|
||||||
url = https://git.qemu.org/git/capstone.git
|
url = git://git.qemu.org/capstone.git
|
||||||
[submodule "roms/seabios-hppa"]
|
[submodule "roms/seabios-hppa"]
|
||||||
path = roms/seabios-hppa
|
path = roms/seabios-hppa
|
||||||
url = https://github.com/hdeller/seabios-hppa.git
|
url = git://github.com/hdeller/seabios-hppa.git
|
||||||
[submodule "roms/u-boot-sam460ex"]
|
[submodule "roms/u-boot-sam460ex"]
|
||||||
path = roms/u-boot-sam460ex
|
path = roms/u-boot-sam460ex
|
||||||
url = https://git.qemu.org/git/u-boot-sam460ex.git
|
url = git://github.com/zbalaton/u-boot-sam460ex
|
||||||
[submodule "tests/fp/berkeley-testfloat-3"]
|
|
||||||
path = tests/fp/berkeley-testfloat-3
|
|
||||||
url = https://github.com/cota/berkeley-testfloat-3
|
|
||||||
[submodule "tests/fp/berkeley-softfloat-3"]
|
|
||||||
path = tests/fp/berkeley-softfloat-3
|
|
||||||
url = https://github.com/cota/berkeley-softfloat-3
|
|
||||||
|
|||||||
29
.mailmap
29
.mailmap
@@ -1,7 +1,6 @@
|
|||||||
# This mailmap fixes up author names/addresses.
|
# 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.
|
||||||
# The first section translates weird addresses from the original git import
|
#
|
||||||
# into proper addresses so that they are counted properly by git shortlog.
|
|
||||||
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
|
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> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||||
@@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7
|
|||||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
|
Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
|
||||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
|
Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
|
||||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
|
|
||||||
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
|
|
||||||
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
|
|
||||||
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
|
|
||||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
|
|
||||||
# There is also a:
|
# There is also a:
|
||||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
# 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
|
# Also list preferred name forms where people have changed their
|
||||||
# git author config, or having utf8/latin1 encoding issues.
|
# git author config
|
||||||
Daniel P. Berrangé <berrange@redhat.com>
|
Daniel P. Berrangé <berrange@redhat.com>
|
||||||
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
|
||||||
|
|||||||
@@ -35,5 +35,13 @@ build:
|
|||||||
options: "-e HOME=/root"
|
options: "-e HOME=/root"
|
||||||
ci:
|
ci:
|
||||||
- unset CC
|
- 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}
|
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||||
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||||
|
|||||||
138
.travis.yml
138
.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
|
sudo: false
|
||||||
dist: trusty
|
|
||||||
language: c
|
language: c
|
||||||
python:
|
python:
|
||||||
- "2.6"
|
- "2.6"
|
||||||
@@ -38,7 +34,6 @@ addons:
|
|||||||
- libvte-2.90-dev
|
- libvte-2.90-dev
|
||||||
- sparse
|
- sparse
|
||||||
- uuid-dev
|
- uuid-dev
|
||||||
- gcovr
|
|
||||||
|
|
||||||
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
|
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
|
||||||
# to prevent IRC notifications from forks. This was created using:
|
# to prevent IRC notifications from forks. This was created using:
|
||||||
@@ -51,15 +46,12 @@ notifications:
|
|||||||
on_failure: always
|
on_failure: always
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- SRC_DIR="."
|
|
||||||
- BUILD_DIR="."
|
|
||||||
- TEST_CMD="make check"
|
- TEST_CMD="make check"
|
||||||
- MAKEFLAGS="-j3"
|
- MAKEFLAGS="-j3"
|
||||||
matrix:
|
matrix:
|
||||||
- CONFIG="--disable-system"
|
- CONFIG=""
|
||||||
- CONFIG="--disable-user"
|
- CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
|
||||||
- CONFIG="--enable-debug --enable-debug-tcg"
|
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
|
||||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
|
|
||||||
- CONFIG="--enable-modules --disable-linux-user"
|
- CONFIG="--enable-modules --disable-linux-user"
|
||||||
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
|
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
|
||||||
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
|
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
|
||||||
@@ -69,79 +61,113 @@ git:
|
|||||||
before_install:
|
before_install:
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
|
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
|
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
|
||||||
- git submodule update --init --recursive capstone dtc ui/keycodemapdb
|
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||||
|
- git submodule update --init --recursive
|
||||||
before_script:
|
before_script:
|
||||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
- ./configure ${CONFIG}
|
||||||
- ${SRC_DIR}/configure ${CONFIG} || { cat config.log && exit 1; }
|
|
||||||
script:
|
script:
|
||||||
- make ${MAKEFLAGS} && ${TEST_CMD}
|
- make ${MAKEFLAGS} && ${TEST_CMD}
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Test out-of-tree builds
|
# Test with CLang for compile portability
|
||||||
- env: CONFIG="--enable-debug --enable-debug-tcg"
|
- env: CONFIG=""
|
||||||
BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
|
|
||||||
# Test with Clang for compile portability (Travis uses clang-5.0)
|
|
||||||
- env: CONFIG="--disable-system"
|
|
||||||
compiler: clang
|
|
||||||
- env: CONFIG="--disable-user"
|
|
||||||
compiler: clang
|
compiler: clang
|
||||||
# gprof/gcov are GCC features
|
# 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"
|
||||||
after_success:
|
|
||||||
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
|
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
# We manually include builds which we disable "make check" for
|
# We manually include builds which we disable "make check" for
|
||||||
- env: CONFIG="--enable-debug --enable-tcg-interpreter"
|
- env: CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||||
TEST_CMD=""
|
TEST_CMD=""
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
# We don't need to exercise every backend with every front-end
|
- env: CONFIG="--enable-trace-backends=simple"
|
||||||
- env: CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
|
|
||||||
TEST_CMD=""
|
TEST_CMD=""
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
- env: CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
|
- env: CONFIG="--enable-trace-backends=ftrace"
|
||||||
TEST_CMD=""
|
TEST_CMD=""
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
- env: CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
|
- env: CONFIG="--enable-trace-backends=ust"
|
||||||
TEST_CMD=""
|
TEST_CMD=""
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
- env: CONFIG="--disable-tcg"
|
- env: CONFIG="--disable-tcg"
|
||||||
TEST_CMD=""
|
TEST_CMD=""
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
# MacOSX builds
|
- env: CONFIG=""
|
||||||
- env: CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode9.4
|
|
||||||
compiler: clang
|
compiler: clang
|
||||||
- env: CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
|
# Plain Trusty System Build
|
||||||
os: osx
|
- env: CONFIG="--disable-linux-user"
|
||||||
osx_image: xcode10
|
sudo: required
|
||||||
compiler: clang
|
addons:
|
||||||
# Python builds
|
dist: trusty
|
||||||
- env: CONFIG="--target-list=x86_64-softmmu"
|
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:
|
python:
|
||||||
- "3.0"
|
- "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:
|
python:
|
||||||
- "3.6"
|
- "3.6"
|
||||||
# Acceptance (Functional) tests
|
env:
|
||||||
- env: CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
|
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||||
TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
|
- CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
|
||||||
addons:
|
before_install:
|
||||||
apt:
|
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
packages:
|
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||||
- python3-pip
|
- sudo apt-get update -qq
|
||||||
- python3.4-venv
|
- 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
|
# Using newer GCC with sanitizers
|
||||||
- addons:
|
- addons:
|
||||||
apt:
|
apt:
|
||||||
update: true
|
|
||||||
sources:
|
sources:
|
||||||
# PPAs for newer toolchains
|
# PPAs for newer toolchains
|
||||||
- ubuntu-toolchain-r-test
|
- ubuntu-toolchain-r-test
|
||||||
packages:
|
packages:
|
||||||
# Extra toolchains
|
# Extra toolchains
|
||||||
- gcc-7
|
- gcc-5
|
||||||
- g++-7
|
- g++-5
|
||||||
# Build dependencies
|
# Build dependencies
|
||||||
- libaio-dev
|
- libaio-dev
|
||||||
- libattr1-dev
|
- libattr1-dev
|
||||||
@@ -170,16 +196,8 @@ matrix:
|
|||||||
language: generic
|
language: generic
|
||||||
compiler: none
|
compiler: none
|
||||||
env:
|
env:
|
||||||
- COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7
|
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
|
||||||
- CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user"
|
- CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
|
||||||
- TEST_CMD=""
|
- TEST_CMD=""
|
||||||
before_script:
|
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
|
||||||
- env:
|
|
||||||
- CONFIG="--disable-system --disable-docs"
|
|
||||||
- TEST_CMD="make check-tcg"
|
|
||||||
script:
|
|
||||||
- make ${MAKEFLAGS} && ${TEST_CMD} ${MAKEFLAGS}
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
compiler: gcc
|
|
||||||
|
|||||||
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
|
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.
|
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. trace-events style
|
||||||
|
|
||||||
8.1 0x prefix
|
8.1 0x prefix
|
||||||
|
|||||||
270
COPYING.PYTHON
Normal file
270
COPYING.PYTHON
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
A. HISTORY OF THE SOFTWARE
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||||
|
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
||||||
|
as a successor of a language called ABC. Guido remains Python's
|
||||||
|
principal author, although it includes many contributions from others.
|
||||||
|
|
||||||
|
In 1995, Guido continued his work on Python at the Corporation for
|
||||||
|
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
||||||
|
in Reston, Virginia where he released several versions of the
|
||||||
|
software.
|
||||||
|
|
||||||
|
In May 2000, Guido and the Python core development team moved to
|
||||||
|
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||||
|
year, the PythonLabs team moved to Digital Creations (now Zope
|
||||||
|
Corporation, see http://www.zope.com). In 2001, the Python Software
|
||||||
|
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
||||||
|
non-profit organization created specifically to own Python-related
|
||||||
|
Intellectual Property. Zope Corporation is a sponsoring member of
|
||||||
|
the PSF.
|
||||||
|
|
||||||
|
All Python releases are Open Source (see http://www.opensource.org for
|
||||||
|
the Open Source Definition). Historically, most, but not all, Python
|
||||||
|
releases have also been GPL-compatible; the table below summarizes
|
||||||
|
the various releases.
|
||||||
|
|
||||||
|
Release Derived Year Owner GPL-
|
||||||
|
from compatible? (1)
|
||||||
|
|
||||||
|
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||||
|
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||||
|
1.6 1.5.2 2000 CNRI no
|
||||||
|
2.0 1.6 2000 BeOpen.com no
|
||||||
|
1.6.1 1.6 2001 CNRI yes (2)
|
||||||
|
2.1 2.0+1.6.1 2001 PSF no
|
||||||
|
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||||
|
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||||
|
2.2 2.1.1 2001 PSF yes
|
||||||
|
2.1.2 2.1.1 2002 PSF yes
|
||||||
|
2.1.3 2.1.2 2002 PSF yes
|
||||||
|
2.2.1 2.2 2002 PSF yes
|
||||||
|
2.2.2 2.2.1 2002 PSF yes
|
||||||
|
2.2.3 2.2.2 2003 PSF yes
|
||||||
|
2.3 2.2.2 2002-2003 PSF yes
|
||||||
|
2.3.1 2.3 2002-2003 PSF yes
|
||||||
|
2.3.2 2.3.1 2002-2003 PSF yes
|
||||||
|
2.3.3 2.3.2 2002-2003 PSF yes
|
||||||
|
2.3.4 2.3.3 2004 PSF yes
|
||||||
|
2.3.5 2.3.4 2005 PSF yes
|
||||||
|
2.4 2.3 2004 PSF yes
|
||||||
|
2.4.1 2.4 2005 PSF yes
|
||||||
|
2.4.2 2.4.1 2005 PSF yes
|
||||||
|
2.4.3 2.4.2 2006 PSF yes
|
||||||
|
2.5 2.4 2006 PSF yes
|
||||||
|
2.7 2.6 2010 PSF yes
|
||||||
|
|
||||||
|
Footnotes:
|
||||||
|
|
||||||
|
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||||
|
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||||
|
a modified version without making your changes open source. The
|
||||||
|
GPL-compatible licenses make it possible to combine Python with
|
||||||
|
other software that is released under the GPL; the others don't.
|
||||||
|
|
||||||
|
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||||
|
because its license has a choice of law clause. According to
|
||||||
|
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||||
|
is "not incompatible" with the GPL.
|
||||||
|
|
||||||
|
Thanks to the many outside volunteers who have worked under Guido's
|
||||||
|
direction to make these releases possible.
|
||||||
|
|
||||||
|
|
||||||
|
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||||
|
===============================================================
|
||||||
|
|
||||||
|
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||||
|
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||||
|
otherwise using this software ("Python") in source or binary form and
|
||||||
|
its associated documentation.
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this License Agreement, PSF
|
||||||
|
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||||
|
license to reproduce, analyze, test, perform and/or display publicly,
|
||||||
|
prepare derivative works, distribute, and otherwise use Python
|
||||||
|
alone or in any derivative version, provided, however, that PSF's
|
||||||
|
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
|
||||||
|
2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights
|
||||||
|
Reserved" are retained in Python alone or in any derivative version
|
||||||
|
prepared by Licensee.
|
||||||
|
|
||||||
|
3. In the event Licensee prepares a derivative work that is based on
|
||||||
|
or incorporates Python or any part thereof, and wants to make
|
||||||
|
the derivative work available to others as provided herein, then
|
||||||
|
Licensee hereby agrees to include in any such work a brief summary of
|
||||||
|
the changes made to Python.
|
||||||
|
|
||||||
|
4. PSF is making Python available to Licensee on an "AS IS"
|
||||||
|
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||||
|
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||||
|
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||||
|
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
6. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
7. Nothing in this License Agreement shall be deemed to create any
|
||||||
|
relationship of agency, partnership, or joint venture between PSF and
|
||||||
|
Licensee. This License Agreement does not grant permission to use PSF
|
||||||
|
trademarks or trade name in a trademark sense to endorse or promote
|
||||||
|
products or services of Licensee, or any third party.
|
||||||
|
|
||||||
|
8. By copying, installing or otherwise using Python, Licensee
|
||||||
|
agrees to be bound by the terms and conditions of this License
|
||||||
|
Agreement.
|
||||||
|
|
||||||
|
|
||||||
|
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||||
|
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||||
|
Individual or Organization ("Licensee") accessing and otherwise using
|
||||||
|
this software in source or binary form and its associated
|
||||||
|
documentation ("the Software").
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this BeOpen Python License
|
||||||
|
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||||
|
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||||
|
and/or display publicly, prepare derivative works, distribute, and
|
||||||
|
otherwise use the Software alone or in any derivative version,
|
||||||
|
provided, however, that the BeOpen Python License is retained in the
|
||||||
|
Software, alone or in any derivative version prepared by Licensee.
|
||||||
|
|
||||||
|
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||||
|
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||||
|
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||||
|
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||||
|
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
5. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
6. This License Agreement shall be governed by and interpreted in all
|
||||||
|
respects by the law of the State of California, excluding conflict of
|
||||||
|
law provisions. Nothing in this License Agreement shall be deemed to
|
||||||
|
create any relationship of agency, partnership, or joint venture
|
||||||
|
between BeOpen and Licensee. This License Agreement does not grant
|
||||||
|
permission to use BeOpen trademarks or trade names in a trademark
|
||||||
|
sense to endorse or promote products or services of Licensee, or any
|
||||||
|
third party. As an exception, the "BeOpen Python" logos available at
|
||||||
|
http://www.pythonlabs.com/logos.html may be used according to the
|
||||||
|
permissions granted on that web page.
|
||||||
|
|
||||||
|
7. By copying, installing or otherwise using the software, Licensee
|
||||||
|
agrees to be bound by the terms and conditions of this License
|
||||||
|
Agreement.
|
||||||
|
|
||||||
|
|
||||||
|
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||||
|
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||||
|
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||||
|
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||||
|
source or binary form and its associated documentation.
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||||
|
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||||
|
license to reproduce, analyze, test, perform and/or display publicly,
|
||||||
|
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||||
|
alone or in any derivative version, provided, however, that CNRI's
|
||||||
|
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||||
|
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||||
|
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||||
|
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||||
|
Agreement, Licensee may substitute the following text (omitting the
|
||||||
|
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||||
|
conditions in CNRI's License Agreement. This Agreement together with
|
||||||
|
Python 1.6.1 may be located on the Internet using the following
|
||||||
|
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||||
|
Agreement may also be obtained from a proxy server on the Internet
|
||||||
|
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||||
|
|
||||||
|
3. In the event Licensee prepares a derivative work that is based on
|
||||||
|
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||||
|
the derivative work available to others as provided herein, then
|
||||||
|
Licensee hereby agrees to include in any such work a brief summary of
|
||||||
|
the changes made to Python 1.6.1.
|
||||||
|
|
||||||
|
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||||
|
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||||
|
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||||
|
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||||
|
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
6. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
7. This License Agreement shall be governed by the federal
|
||||||
|
intellectual property law of the United States, including without
|
||||||
|
limitation the federal copyright law, and, to the extent such
|
||||||
|
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||||
|
Virginia, excluding Virginia's conflict of law provisions.
|
||||||
|
Notwithstanding the foregoing, with regard to derivative works based
|
||||||
|
on Python 1.6.1 that incorporate non-separable material that was
|
||||||
|
previously distributed under the GNU General Public License (GPL), the
|
||||||
|
law of the Commonwealth of Virginia shall govern this License
|
||||||
|
Agreement only as to issues arising under or with respect to
|
||||||
|
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||||
|
License Agreement shall be deemed to create any relationship of
|
||||||
|
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||||
|
License Agreement does not grant permission to use CNRI trademarks or
|
||||||
|
trade name in a trademark sense to endorse or promote products or
|
||||||
|
services of Licensee, or any third party.
|
||||||
|
|
||||||
|
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||||
|
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||||
|
bound by the terms and conditions of this License Agreement.
|
||||||
|
|
||||||
|
ACCEPT
|
||||||
|
|
||||||
|
|
||||||
|
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||||
|
The Netherlands. All rights reserved.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose and without fee is hereby granted,
|
||||||
|
provided that the above copyright notice appear in all copies and that
|
||||||
|
both that copyright notice and this permission notice appear in
|
||||||
|
supporting documentation, and that the name of Stichting Mathematisch
|
||||||
|
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||||
|
distribution of the software without specific, written prior
|
||||||
|
permission.
|
||||||
|
|
||||||
|
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||||
|
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
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).
|
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.
|
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
|
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
|
||||||
qemu_vfree, since breaking this will cause problems on Win32.
|
qemu_vfree, since breaking this will cause problems on Win32.
|
||||||
|
|
||||||
|
|||||||
505
MAINTAINERS
505
MAINTAINERS
File diff suppressed because it is too large
Load Diff
89
Makefile
89
Makefile
@@ -20,6 +20,8 @@ ifneq ($(wildcard config-host.mak),)
|
|||||||
all:
|
all:
|
||||||
include config-host.mak
|
include config-host.mak
|
||||||
|
|
||||||
|
PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON)
|
||||||
|
|
||||||
git-submodule-update:
|
git-submodule-update:
|
||||||
|
|
||||||
.PHONY: git-submodule-update
|
.PHONY: git-submodule-update
|
||||||
@@ -96,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-common.h qapi/qapi-types-common.c
|
||||||
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.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-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-migration.h qapi/qapi-types-migration.c
|
||||||
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.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
|
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
|
||||||
@@ -115,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-common.h qapi/qapi-visit-common.c
|
||||||
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.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-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-migration.h qapi/qapi-visit-migration.c
|
||||||
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.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
|
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
|
||||||
@@ -133,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-common.h qapi/qapi-commands-common.c
|
||||||
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.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-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-migration.h qapi/qapi-commands-migration.c
|
||||||
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.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
|
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
|
||||||
@@ -151,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-common.h qapi/qapi-events-common.c
|
||||||
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.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-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-migration.h qapi/qapi-events-migration.c
|
||||||
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.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
|
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
|
||||||
@@ -320,7 +318,6 @@ KEYCODEMAP_FILES = \
|
|||||||
ui/input-keymap-xorgkbd-to-qcode.c \
|
ui/input-keymap-xorgkbd-to-qcode.c \
|
||||||
ui/input-keymap-xorgxquartz-to-qcode.c \
|
ui/input-keymap-xorgxquartz-to-qcode.c \
|
||||||
ui/input-keymap-xorgxwin-to-qcode.c \
|
ui/input-keymap-xorgxwin-to-qcode.c \
|
||||||
ui/input-keymap-osx-to-qcode.c \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
GENERATED_FILES += $(KEYCODEMAP_FILES)
|
GENERATED_FILES += $(KEYCODEMAP_FILES)
|
||||||
@@ -350,14 +347,13 @@ $(call set-vpath, $(SRC_PATH))
|
|||||||
|
|
||||||
LIBS+=-lz $(LIBS_TOOLS)
|
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
|
ifdef BUILD_DOCS
|
||||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||||
DOCS+=docs/qemu-block-drivers.7
|
DOCS+=docs/qemu-block-drivers.7
|
||||||
DOCS+=docs/qemu-cpu-models.7
|
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||||
endif
|
endif
|
||||||
@@ -415,7 +411,6 @@ dummy := $(call unnest-vars,, \
|
|||||||
chardev-obj-y \
|
chardev-obj-y \
|
||||||
util-obj-y \
|
util-obj-y \
|
||||||
qga-obj-y \
|
qga-obj-y \
|
||||||
elf2dmp-obj-y \
|
|
||||||
ivshmem-client-obj-y \
|
ivshmem-client-obj-y \
|
||||||
ivshmem-server-obj-y \
|
ivshmem-server-obj-y \
|
||||||
libvhost-user-obj-y \
|
libvhost-user-obj-y \
|
||||||
@@ -443,23 +438,21 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
|||||||
qemu-version.h: FORCE
|
qemu-version.h: FORCE
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
(cd $(SRC_PATH); \
|
(cd $(SRC_PATH); \
|
||||||
|
printf '#define QEMU_PKGVERSION '; \
|
||||||
if test -n "$(PKGVERSION)"; then \
|
if test -n "$(PKGVERSION)"; then \
|
||||||
pkgvers="$(PKGVERSION)"; \
|
printf '"$(PKGVERSION)"\n'; \
|
||||||
else \
|
else \
|
||||||
if test -d .git; then \
|
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 \
|
if ! git diff-index --quiet HEAD &>/dev/null; then \
|
||||||
pkgvers="$${pkgvers}-dirty"; \
|
printf -- '-dirty'; \
|
||||||
fi; \
|
fi; \
|
||||||
fi; \
|
printf ')"\n'; \
|
||||||
fi; \
|
|
||||||
printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
|
|
||||||
if test -n "$${pkgvers}"; then \
|
|
||||||
printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
|
|
||||||
else \
|
else \
|
||||||
printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
|
printf '""\n'; \
|
||||||
fi; \
|
fi; \
|
||||||
) > $@.tmp)
|
fi) > $@.tmp)
|
||||||
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
||||||
mv $@.tmp $@; \
|
mv $@.tmp $@; \
|
||||||
else \
|
else \
|
||||||
@@ -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,)
|
$(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
|
dtc/%: .git-submodule-status
|
||||||
@mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
# Overriding CFLAGS causes us to lose defines added in the sub-makefile.
|
# Overriding CFLAGS causes us to lose defines added in the sub-makefile.
|
||||||
# Not overriding CFLAGS leads to mis-matches between compilation modes.
|
# Not overriding CFLAGS leads to mis-matches between compilation modes.
|
||||||
@@ -544,8 +537,6 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
|||||||
|
|
||||||
qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
||||||
|
|
||||||
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
|
|
||||||
|
|
||||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
||||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||||
|
|
||||||
@@ -570,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \
|
|||||||
$(SRC_PATH)/scripts/qapi/visit.py \
|
$(SRC_PATH)/scripts/qapi/visit.py \
|
||||||
$(SRC_PATH)/scripts/qapi/common.py \
|
$(SRC_PATH)/scripts/qapi/common.py \
|
||||||
$(SRC_PATH)/scripts/qapi/doc.py \
|
$(SRC_PATH)/scripts/qapi/doc.py \
|
||||||
|
$(SRC_PATH)/scripts/ordereddict.py \
|
||||||
$(SRC_PATH)/scripts/qapi-gen.py
|
$(SRC_PATH)/scripts/qapi-gen.py
|
||||||
|
|
||||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
|
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
|
||||||
@@ -578,7 +570,7 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
|
|||||||
qga/qapi-generated/qga-qapi-doc.texi: \
|
qga/qapi-generated/qga-qapi-doc.texi: \
|
||||||
qga/qapi-generated/qapi-gen-timestamp ;
|
qga/qapi-generated/qapi-gen-timestamp ;
|
||||||
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
|
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
|
||||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \
|
||||||
-o qga/qapi-generated -p "qga-" $<, \
|
-o qga/qapi-generated -p "qga-" $<, \
|
||||||
"GEN","$(@:%-timestamp=%)")
|
"GEN","$(@:%-timestamp=%)")
|
||||||
@>$@
|
@>$@
|
||||||
@@ -588,7 +580,6 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
|||||||
$(SRC_PATH)/qapi/char.json \
|
$(SRC_PATH)/qapi/char.json \
|
||||||
$(SRC_PATH)/qapi/crypto.json \
|
$(SRC_PATH)/qapi/crypto.json \
|
||||||
$(SRC_PATH)/qapi/introspect.json \
|
$(SRC_PATH)/qapi/introspect.json \
|
||||||
$(SRC_PATH)/qapi/job.json \
|
|
||||||
$(SRC_PATH)/qapi/migration.json \
|
$(SRC_PATH)/qapi/migration.json \
|
||||||
$(SRC_PATH)/qapi/misc.json \
|
$(SRC_PATH)/qapi/misc.json \
|
||||||
$(SRC_PATH)/qapi/net.json \
|
$(SRC_PATH)/qapi/net.json \
|
||||||
@@ -608,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-common.c qapi/qapi-types-common.h \
|
||||||
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
|
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
|
||||||
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.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-migration.c qapi/qapi-types-migration.h \
|
||||||
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
|
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
|
||||||
qapi/qapi-types-net.c qapi/qapi-types-net.h \
|
qapi/qapi-types-net.c qapi/qapi-types-net.h \
|
||||||
@@ -627,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-common.c qapi/qapi-visit-common.h \
|
||||||
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
|
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
|
||||||
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.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-migration.c qapi/qapi-visit-migration.h \
|
||||||
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
|
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
|
||||||
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
|
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
|
||||||
@@ -645,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-common.c qapi/qapi-commands-common.h \
|
||||||
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
|
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
|
||||||
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.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-migration.c qapi/qapi-commands-migration.h \
|
||||||
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
|
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
|
||||||
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
|
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
|
||||||
@@ -663,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-common.c qapi/qapi-events-common.h \
|
||||||
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
|
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
|
||||||
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.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-migration.c qapi/qapi-events-migration.h \
|
||||||
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
|
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
|
||||||
qapi/qapi-events-net.c qapi/qapi-events-net.h \
|
qapi/qapi-events-net.c qapi/qapi-events-net.h \
|
||||||
@@ -678,7 +665,7 @@ qapi/qapi-introspect.h qapi/qapi-introspect.c \
|
|||||||
qapi/qapi-doc.texi: \
|
qapi/qapi-doc.texi: \
|
||||||
qapi-gen-timestamp ;
|
qapi-gen-timestamp ;
|
||||||
qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
|
qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
|
||||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \
|
||||||
-o "qapi" -b $<, \
|
-o "qapi" -b $<, \
|
||||||
"GEN","$(@:%-timestamp=%)")
|
"GEN","$(@:%-timestamp=%)")
|
||||||
@>$@
|
@>$@
|
||||||
@@ -711,10 +698,6 @@ ifneq ($(EXESUF),)
|
|||||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
elf2dmp: LIBS = $(CURL_LIBS)
|
|
||||||
elf2dmp: $(elf2dmp-obj-y)
|
|
||||||
$(call LINK, $^)
|
|
||||||
|
|
||||||
ifdef CONFIG_IVSHMEM
|
ifdef CONFIG_IVSHMEM
|
||||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
@@ -731,14 +714,6 @@ module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
|||||||
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
|
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
|
||||||
"GEN","$@")
|
"GEN","$@")
|
||||||
|
|
||||||
ifdef CONFIG_GCOV
|
|
||||||
.PHONY: clean-coverage
|
|
||||||
clean-coverage:
|
|
||||||
$(call quiet-command, \
|
|
||||||
find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \
|
|
||||||
"CLEAN", "coverage files")
|
|
||||||
endif
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
# avoid old build problems by removing potentially incorrect old files
|
# avoid old build problems by removing potentially incorrect old files
|
||||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||||
@@ -759,7 +734,7 @@ clean:
|
|||||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||||
rm -f $$d/qemu-options.def; \
|
rm -f $$d/qemu-options.def; \
|
||||||
done
|
done
|
||||||
rm -f config-all-devices.mak
|
rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak
|
||||||
|
|
||||||
VERSION ?= $(shell cat VERSION)
|
VERSION ?= $(shell cat VERSION)
|
||||||
|
|
||||||
@@ -771,7 +746,6 @@ qemu-%.tar.bz2:
|
|||||||
distclean: clean
|
distclean: clean
|
||||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||||
rm -f $(SUBDIR_DEVICES_MAK)
|
|
||||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
||||||
@@ -787,7 +761,6 @@ distclean: clean
|
|||||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
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/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||||
rm -f docs/qemu-block-drivers.7
|
rm -f docs/qemu-block-drivers.7
|
||||||
rm -f docs/qemu-cpu-models.7
|
|
||||||
for d in $(TARGET_DIRS); do \
|
for d in $(TARGET_DIRS); do \
|
||||||
rm -rf $$d || exit 1 ; \
|
rm -rf $$d || exit 1 ; \
|
||||||
done
|
done
|
||||||
@@ -802,7 +775,7 @@ bepo cz
|
|||||||
ifdef INSTALL_BLOBS
|
ifdef INSTALL_BLOBS
|
||||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
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 \
|
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||||
vgabios-ramfb.bin vgabios-bochs-display.bin \
|
acpi-dsdt.aml \
|
||||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
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-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||||
@@ -834,7 +807,6 @@ ifdef CONFIG_POSIX
|
|||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
|
||||||
ifneq ($(TOOLS),)
|
ifneq ($(TOOLS),)
|
||||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||||
@@ -883,7 +855,7 @@ ifneq ($(BLOBS),)
|
|||||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||||
done
|
done
|
||||||
endif
|
endif
|
||||||
ifdef CONFIG_GTK
|
ifeq ($(CONFIG_GTK),m)
|
||||||
$(MAKE) -C po $@
|
$(MAKE) -C po $@
|
||||||
endif
|
endif
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
||||||
@@ -977,7 +949,6 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
|||||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||||
qemu-ga.8: qemu-ga.texi
|
qemu-ga.8: qemu-ga.texi
|
||||||
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
||||||
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
|
|
||||||
|
|
||||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||||
@@ -986,9 +957,8 @@ txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
|||||||
|
|
||||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||||
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
|
qemu-monitor-info.texi docs/qemu-block-drivers.texi
|
||||||
docs/qemu-cpu-models.texi
|
|
||||||
|
|
||||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||||
@@ -1000,16 +970,6 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
|
|||||||
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
||||||
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
||||||
|
|
||||||
# Reports/Analysis
|
|
||||||
|
|
||||||
%/coverage-report.html:
|
|
||||||
@mkdir -p $*
|
|
||||||
$(call quiet-command,\
|
|
||||||
gcovr -p --html --html-details -o $@, \
|
|
||||||
"GEN", "coverage-report.html")
|
|
||||||
|
|
||||||
.PHONY: coverage-report
|
|
||||||
coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
|
|
||||||
|
|
||||||
ifdef CONFIG_WIN32
|
ifdef CONFIG_WIN32
|
||||||
|
|
||||||
@@ -1090,9 +1050,6 @@ include $(SRC_PATH)/tests/vm/Makefile.include
|
|||||||
help:
|
help:
|
||||||
@echo 'Generic targets:'
|
@echo 'Generic targets:'
|
||||||
@echo ' all - Build all'
|
@echo ' all - Build all'
|
||||||
ifdef CONFIG_MODULES
|
|
||||||
@echo ' modules - Build all modules'
|
|
||||||
endif
|
|
||||||
@echo ' dir/file.o - Build specified target only'
|
@echo ' dir/file.o - Build specified target only'
|
||||||
@echo ' install - Install QEMU, documentation and tools'
|
@echo ' install - Install QEMU, documentation and tools'
|
||||||
@echo ' ctags/TAGS - Generate tags file for editors'
|
@echo ' ctags/TAGS - Generate tags file for editors'
|
||||||
@@ -1105,9 +1062,6 @@ endif
|
|||||||
echo '')
|
echo '')
|
||||||
@echo 'Cleaning targets:'
|
@echo 'Cleaning targets:'
|
||||||
@echo ' clean - Remove most generated files but keep the config'
|
@echo ' clean - Remove most generated files but keep the config'
|
||||||
ifdef CONFIG_GCOV
|
|
||||||
@echo ' clean-coverage - Remove coverage files'
|
|
||||||
endif
|
|
||||||
@echo ' distclean - Remove all generated files'
|
@echo ' distclean - Remove all generated files'
|
||||||
@echo ' dist - Build a distributable tarball'
|
@echo ' dist - Build a distributable tarball'
|
||||||
@echo ''
|
@echo ''
|
||||||
@@ -1119,9 +1073,6 @@ endif
|
|||||||
@echo 'Documentation targets:'
|
@echo 'Documentation targets:'
|
||||||
@echo ' html info pdf txt'
|
@echo ' html info pdf txt'
|
||||||
@echo ' - Build documentation in specified format'
|
@echo ' - Build documentation in specified format'
|
||||||
ifdef CONFIG_GCOV
|
|
||||||
@echo ' coverage-report - Create code coverage report'
|
|
||||||
endif
|
|
||||||
@echo ''
|
@echo ''
|
||||||
ifdef CONFIG_WIN32
|
ifdef CONFIG_WIN32
|
||||||
@echo 'Windows targets:'
|
@echo 'Windows 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-common.o
|
||||||
util-obj-y += qapi/qapi-types-crypto.o
|
util-obj-y += qapi/qapi-types-crypto.o
|
||||||
util-obj-y += qapi/qapi-types-introspect.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-migration.o
|
||||||
util-obj-y += qapi/qapi-types-misc.o
|
util-obj-y += qapi/qapi-types-misc.o
|
||||||
util-obj-y += qapi/qapi-types-net.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-common.o
|
||||||
util-obj-y += qapi/qapi-visit-crypto.o
|
util-obj-y += qapi/qapi-visit-crypto.o
|
||||||
util-obj-y += qapi/qapi-visit-introspect.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-migration.o
|
||||||
util-obj-y += qapi/qapi-visit-misc.o
|
util-obj-y += qapi/qapi-visit-misc.o
|
||||||
util-obj-y += qapi/qapi-visit-net.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-common.o
|
||||||
util-obj-y += qapi/qapi-events-crypto.o
|
util-obj-y += qapi/qapi-events-crypto.o
|
||||||
util-obj-y += qapi/qapi-events-introspect.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-migration.o
|
||||||
util-obj-y += qapi/qapi-events-misc.o
|
util-obj-y += qapi/qapi-events-misc.o
|
||||||
util-obj-y += qapi/qapi-events-net.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 is code used by both qemu system emulation and qemu-img
|
||||||
|
|
||||||
block-obj-y += nbd/
|
block-obj-y += nbd/
|
||||||
block-obj-y += block.o blockjob.o job.o
|
block-obj-y += block.o blockjob.o
|
||||||
block-obj-y += block/ scsi/
|
block-obj-y += block/ scsi/
|
||||||
block-obj-y += qemu-io-cmds.o
|
block-obj-y += qemu-io-cmds.o
|
||||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||||
@@ -97,7 +94,6 @@ io-obj-y = io/
|
|||||||
ifeq ($(CONFIG_SOFTMMU),y)
|
ifeq ($(CONFIG_SOFTMMU),y)
|
||||||
common-obj-y = blockdev.o blockdev-nbd.o block/
|
common-obj-y = blockdev.o blockdev-nbd.o block/
|
||||||
common-obj-y += bootdevice.o iothread.o
|
common-obj-y += bootdevice.o iothread.o
|
||||||
common-obj-y += job-qmp.o
|
|
||||||
common-obj-y += net/
|
common-obj-y += net/
|
||||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||||
common-obj-$(CONFIG_WIN32) += os-win32.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-common.o
|
||||||
common-obj-y += qapi/qapi-commands-crypto.o
|
common-obj-y += qapi/qapi-commands-crypto.o
|
||||||
common-obj-y += qapi/qapi-commands-introspect.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-migration.o
|
||||||
common-obj-y += qapi/qapi-commands-misc.o
|
common-obj-y += qapi/qapi-commands-misc.o
|
||||||
common-obj-y += qapi/qapi-commands-net.o
|
common-obj-y += qapi/qapi-commands-net.o
|
||||||
@@ -186,7 +181,6 @@ qga-vss-dll-obj-y = qga/
|
|||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# contrib
|
# contrib
|
||||||
elf2dmp-obj-y = contrib/elf2dmp/
|
|
||||||
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
||||||
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
||||||
libvhost-user-obj-y = contrib/libvhost-user/
|
libvhost-user-obj-y = contrib/libvhost-user/
|
||||||
@@ -197,68 +191,66 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/
|
|||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
trace-events-subdirs =
|
trace-events-subdirs =
|
||||||
trace-events-subdirs += accel/kvm
|
trace-events-subdirs += util
|
||||||
trace-events-subdirs += accel/tcg
|
trace-events-subdirs += crypto
|
||||||
trace-events-subdirs += audio
|
trace-events-subdirs += io
|
||||||
|
trace-events-subdirs += migration
|
||||||
trace-events-subdirs += block
|
trace-events-subdirs += block
|
||||||
trace-events-subdirs += chardev
|
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
|
||||||
trace-events-subdirs += hw/block/dataplane
|
trace-events-subdirs += hw/block/dataplane
|
||||||
trace-events-subdirs += hw/char
|
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/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/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
|
||||||
trace-events-subdirs += hw/rdma/vmw
|
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/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/sparc
|
||||||
trace-events-subdirs += hw/sparc64
|
trace-events-subdirs += hw/sparc64
|
||||||
trace-events-subdirs += hw/timer
|
trace-events-subdirs += hw/sd
|
||||||
trace-events-subdirs += hw/tpm
|
trace-events-subdirs += hw/isa
|
||||||
trace-events-subdirs += hw/usb
|
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/vfio
|
||||||
trace-events-subdirs += hw/virtio
|
trace-events-subdirs += hw/acpi
|
||||||
trace-events-subdirs += hw/watchdog
|
trace-events-subdirs += hw/arm
|
||||||
|
trace-events-subdirs += hw/alpha
|
||||||
|
trace-events-subdirs += hw/hppa
|
||||||
trace-events-subdirs += hw/xen
|
trace-events-subdirs += hw/xen
|
||||||
trace-events-subdirs += io
|
trace-events-subdirs += hw/ide
|
||||||
trace-events-subdirs += linux-user
|
trace-events-subdirs += hw/tpm
|
||||||
trace-events-subdirs += migration
|
trace-events-subdirs += ui
|
||||||
trace-events-subdirs += nbd
|
trace-events-subdirs += audio
|
||||||
trace-events-subdirs += net
|
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/arm
|
||||||
trace-events-subdirs += target/i386
|
trace-events-subdirs += target/i386
|
||||||
trace-events-subdirs += target/mips
|
trace-events-subdirs += target/mips
|
||||||
trace-events-subdirs += target/ppc
|
|
||||||
trace-events-subdirs += target/s390x
|
|
||||||
trace-events-subdirs += target/sparc
|
trace-events-subdirs += target/sparc
|
||||||
trace-events-subdirs += ui
|
trace-events-subdirs += target/s390x
|
||||||
trace-events-subdirs += util
|
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)
|
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
|
ifdef CONFIG_LINUX
|
||||||
QEMU_CFLAGS += -I../linux-headers
|
QEMU_CFLAGS += -I../linux-headers
|
||||||
endif
|
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
|
ifdef CONFIG_USER_ONLY
|
||||||
# user emulator name
|
# user emulator name
|
||||||
@@ -36,11 +36,6 @@ endif
|
|||||||
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
||||||
STPFILES=
|
STPFILES=
|
||||||
|
|
||||||
# Makefile Tests
|
|
||||||
ifdef CONFIG_USER_ONLY
|
|
||||||
include $(SRC_PATH)/tests/tcg/Makefile.include
|
|
||||||
endif
|
|
||||||
|
|
||||||
config-target.h: config-target.h-timestamp
|
config-target.h: config-target.h-timestamp
|
||||||
config-target.h-timestamp: config-target.mak
|
config-target.h-timestamp: config-target.mak
|
||||||
|
|
||||||
@@ -102,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) += tcg/tcg-common.o tcg/optimize.o
|
||||||
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
|
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
|
||||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/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 += target/$(TARGET_BASE_ARCH)/
|
||||||
obj-y += disas.o
|
obj-y += disas.o
|
||||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||||
@@ -143,7 +138,6 @@ obj-y += hw/
|
|||||||
obj-y += memory.o
|
obj-y += memory.o
|
||||||
obj-y += memory_mapping.o
|
obj-y += memory_mapping.o
|
||||||
obj-y += dump.o
|
obj-y += dump.o
|
||||||
obj-$(TARGET_X86_64) += win_dump.o
|
|
||||||
obj-y += migration/ram.o
|
obj-y += migration/ram.o
|
||||||
LIBS := $(libs_softmmu) $(LIBS)
|
LIBS := $(libs_softmmu) $(LIBS)
|
||||||
|
|
||||||
|
|||||||
4
README
4
README
@@ -54,7 +54,7 @@ Submitting patches
|
|||||||
|
|
||||||
The QEMU source code is maintained under the GIT version control system.
|
The QEMU source code is maintained under the GIT version control system.
|
||||||
|
|
||||||
git clone https://git.qemu.org/git/qemu.git
|
git clone git://git.qemu.org/qemu.git
|
||||||
|
|
||||||
When submitting patches, one common approach is to use 'git
|
When submitting patches, one common approach is to use 'git
|
||||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||||
@@ -70,7 +70,7 @@ the QEMU website
|
|||||||
|
|
||||||
The QEMU website is also maintained under source control.
|
The QEMU website is also maintained under source control.
|
||||||
|
|
||||||
git clone https://git.qemu.org/git/qemu-web.git
|
git clone git://git.qemu.org/qemu-web.git
|
||||||
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
||||||
|
|
||||||
A 'git-publish' utility was created to make above process less
|
A 'git-publish' utility was created to make above process less
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
obj-$(CONFIG_SOFTMMU) += accel.o
|
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||||
obj-$(CONFIG_KVM) += kvm/
|
obj-y += kvm/
|
||||||
obj-$(CONFIG_TCG) += tcg/
|
obj-$(CONFIG_TCG) += tcg/
|
||||||
obj-y += stubs/
|
obj-y += stubs/
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
|||||||
|
|
||||||
void configure_accelerator(MachineState *ms)
|
void configure_accelerator(MachineState *ms)
|
||||||
{
|
{
|
||||||
const char *accel;
|
const char *accel, *p;
|
||||||
char **accel_list, **tmp;
|
char buf[10];
|
||||||
int ret;
|
int ret;
|
||||||
bool accel_initialised = false;
|
bool accel_initialised = false;
|
||||||
bool init_failed = false;
|
bool init_failed = false;
|
||||||
@@ -83,10 +83,13 @@ void configure_accelerator(MachineState *ms)
|
|||||||
accel = "tcg";
|
accel = "tcg";
|
||||||
}
|
}
|
||||||
|
|
||||||
accel_list = g_strsplit(accel, ":", 0);
|
p = accel;
|
||||||
|
while (!accel_initialised && *p != '\0') {
|
||||||
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
|
if (*p == ':') {
|
||||||
acc = accel_find(*tmp);
|
p++;
|
||||||
|
}
|
||||||
|
p = get_opt_name(buf, sizeof(buf), p, ':');
|
||||||
|
acc = accel_find(buf);
|
||||||
if (!acc) {
|
if (!acc) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -104,7 +107,6 @@ void configure_accelerator(MachineState *ms)
|
|||||||
accel_initialised = true;
|
accel_initialised = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_strfreev(accel_list);
|
|
||||||
|
|
||||||
if (!accel_initialised) {
|
if (!accel_initialised) {
|
||||||
if (!init_failed) {
|
if (!init_failed) {
|
||||||
@@ -124,15 +126,6 @@ void accel_register_compat_props(AccelState *accel)
|
|||||||
register_compat_props_array(class->global_props);
|
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)
|
static void register_accel_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&accel_type);
|
type_register_static(&accel_type);
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
obj-y += kvm-all.o
|
obj-$(CONFIG_KVM) += kvm-all.o
|
||||||
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
|
|
||||||
|
|||||||
@@ -38,8 +38,6 @@
|
|||||||
#include "qemu/event_notifier.h"
|
#include "qemu/event_notifier.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
#include "sysemu/sev.h"
|
|
||||||
#include "sysemu/balloon.h"
|
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
|
|
||||||
@@ -79,7 +77,6 @@ struct KVMState
|
|||||||
int fd;
|
int fd;
|
||||||
int vmfd;
|
int vmfd;
|
||||||
int coalesced_mmio;
|
int coalesced_mmio;
|
||||||
int coalesced_pio;
|
|
||||||
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
||||||
bool coalesced_flush_in_progress;
|
bool coalesced_flush_in_progress;
|
||||||
int vcpu_events;
|
int vcpu_events;
|
||||||
@@ -106,10 +103,6 @@ struct KVMState
|
|||||||
#endif
|
#endif
|
||||||
KVMMemoryListener memory_listener;
|
KVMMemoryListener memory_listener;
|
||||||
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
|
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;
|
KVMState *kvm_state;
|
||||||
@@ -145,26 +138,6 @@ int kvm_get_max_memslots(void)
|
|||||||
return s->nr_slots;
|
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)
|
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
|
||||||
{
|
{
|
||||||
KVMState *s = kvm_state;
|
KVMState *s = kvm_state;
|
||||||
@@ -258,7 +231,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
|
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot)
|
||||||
{
|
{
|
||||||
KVMState *s = kvm_state;
|
KVMState *s = kvm_state;
|
||||||
struct kvm_userspace_memory_region mem;
|
struct kvm_userspace_memory_region mem;
|
||||||
@@ -269,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
|
|||||||
mem.userspace_addr = (unsigned long)slot->ram;
|
mem.userspace_addr = (unsigned long)slot->ram;
|
||||||
mem.flags = slot->flags;
|
mem.flags = slot->flags;
|
||||||
|
|
||||||
if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) {
|
if (slot->memory_size && mem.flags & KVM_MEM_READONLY) {
|
||||||
/* Set the slot size to 0 before setting the slot to the desired
|
/* Set the slot size to 0 before setting the slot to the desired
|
||||||
* value. This is needed based on KVM commit 75d61fbc. */
|
* value. This is needed based on KVM commit 75d61fbc. */
|
||||||
mem.memory_size = 0;
|
mem.memory_size = 0;
|
||||||
@@ -277,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
|
|||||||
}
|
}
|
||||||
mem.memory_size = slot->memory_size;
|
mem.memory_size = slot->memory_size;
|
||||||
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||||
slot->old_flags = mem.flags;
|
|
||||||
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
|
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
|
||||||
mem.memory_size, mem.userspace_addr, ret);
|
mem.memory_size, mem.userspace_addr, ret);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -394,14 +366,17 @@ static int kvm_mem_flags(MemoryRegion *mr)
|
|||||||
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
|
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
|
||||||
MemoryRegion *mr)
|
MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
|
int old_flags;
|
||||||
|
|
||||||
|
old_flags = mem->flags;
|
||||||
mem->flags = kvm_mem_flags(mr);
|
mem->flags = kvm_mem_flags(mr);
|
||||||
|
|
||||||
/* If nothing changed effectively, no need to issue ioctl */
|
/* If nothing changed effectively, no need to issue ioctl */
|
||||||
if (mem->flags == mem->old_flags) {
|
if (mem->flags == old_flags) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return kvm_set_user_memory_region(kml, mem, false);
|
return kvm_set_user_memory_region(kml, mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_section_update_flags(KVMMemoryListener *kml,
|
static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||||
@@ -561,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_coalesce_pio_add(MemoryListener *listener,
|
|
||||||
MemoryRegionSection *section,
|
|
||||||
hwaddr start, hwaddr size)
|
|
||||||
{
|
|
||||||
KVMState *s = kvm_state;
|
|
||||||
|
|
||||||
if (s->coalesced_pio) {
|
|
||||||
struct kvm_coalesced_mmio_zone zone;
|
|
||||||
|
|
||||||
zone.addr = start;
|
|
||||||
zone.size = size;
|
|
||||||
zone.pio = 1;
|
|
||||||
|
|
||||||
(void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kvm_coalesce_pio_del(MemoryListener *listener,
|
|
||||||
MemoryRegionSection *section,
|
|
||||||
hwaddr start, hwaddr size)
|
|
||||||
{
|
|
||||||
KVMState *s = kvm_state;
|
|
||||||
|
|
||||||
if (s->coalesced_pio) {
|
|
||||||
struct kvm_coalesced_mmio_zone zone;
|
|
||||||
|
|
||||||
zone.addr = start;
|
|
||||||
zone.size = size;
|
|
||||||
zone.pio = 1;
|
|
||||||
|
|
||||||
(void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static MemoryListener kvm_coalesced_pio_listener = {
|
|
||||||
.coalesced_io_add = kvm_coalesce_pio_add,
|
|
||||||
.coalesced_io_del = kvm_coalesce_pio_del,
|
|
||||||
};
|
|
||||||
|
|
||||||
int kvm_check_extension(KVMState *s, unsigned int extension)
|
int kvm_check_extension(KVMState *s, unsigned int extension)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -794,8 +730,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
|||||||
|
|
||||||
/* unregister the slot */
|
/* unregister the slot */
|
||||||
mem->memory_size = 0;
|
mem->memory_size = 0;
|
||||||
mem->flags = 0;
|
err = kvm_set_user_memory_region(kml, mem);
|
||||||
err = kvm_set_user_memory_region(kml, mem, false);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "%s: error unregistering slot: %s\n",
|
fprintf(stderr, "%s: error unregistering slot: %s\n",
|
||||||
__func__, strerror(-err));
|
__func__, strerror(-err));
|
||||||
@@ -811,7 +746,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
|||||||
mem->ram = ram;
|
mem->ram = ram;
|
||||||
mem->flags = kvm_mem_flags(mr);
|
mem->flags = kvm_mem_flags(mr);
|
||||||
|
|
||||||
err = kvm_set_user_memory_region(kml, mem, true);
|
err = kvm_set_user_memory_region(kml, mem);
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||||
strerror(-err));
|
strerror(-err));
|
||||||
@@ -1656,8 +1591,6 @@ static int kvm_init(MachineState *ms)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
||||||
s->coalesced_pio = s->coalesced_mmio &&
|
|
||||||
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
|
||||||
|
|
||||||
#ifdef KVM_CAP_VCPU_EVENTS
|
#ifdef KVM_CAP_VCPU_EVENTS
|
||||||
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
||||||
@@ -1681,8 +1614,10 @@ static int kvm_init(MachineState *ms)
|
|||||||
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef KVM_CAP_READONLY_MEM
|
||||||
kvm_readonly_mem_allowed =
|
kvm_readonly_mem_allowed =
|
||||||
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
kvm_eventfds_allowed =
|
kvm_eventfds_allowed =
|
||||||
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
||||||
@@ -1701,20 +1636,6 @@ static int kvm_init(MachineState *ms)
|
|||||||
|
|
||||||
kvm_state = s;
|
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);
|
ret = kvm_arch_init(ms, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
@@ -1728,22 +1649,17 @@ static int kvm_init(MachineState *ms)
|
|||||||
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
||||||
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
||||||
}
|
}
|
||||||
s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
|
s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
|
||||||
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
|
s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
|
||||||
|
|
||||||
kvm_memory_listener_register(s, &s->memory_listener,
|
kvm_memory_listener_register(s, &s->memory_listener,
|
||||||
&address_space_memory, 0);
|
&address_space_memory, 0);
|
||||||
memory_listener_register(&kvm_io_listener,
|
memory_listener_register(&kvm_io_listener,
|
||||||
&address_space_io);
|
&address_space_io);
|
||||||
memory_listener_register(&kvm_coalesced_pio_listener,
|
|
||||||
&address_space_io);
|
|
||||||
|
|
||||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||||
|
|
||||||
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||||
if (!s->sync_mmu) {
|
|
||||||
qemu_balloon_inhibit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -1822,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
|||||||
|
|
||||||
ent = &ring->coalesced_mmio[ring->first];
|
ent = &ring->coalesced_mmio[ring->first];
|
||||||
|
|
||||||
if (ent->pio == 1) {
|
|
||||||
address_space_rw(&address_space_io, ent->phys_addr,
|
|
||||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
|
||||||
ent->len, true);
|
|
||||||
} else {
|
|
||||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||||
}
|
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
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
|
#ifndef CONFIG_USER_ONLY
|
||||||
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
|
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tb_unlock(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,37 +18,26 @@
|
|||||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "trace/mem.h"
|
|
||||||
|
|
||||||
#if DATA_SIZE == 16
|
#if DATA_SIZE == 16
|
||||||
# define SUFFIX o
|
# define SUFFIX o
|
||||||
# define DATA_TYPE Int128
|
# define DATA_TYPE Int128
|
||||||
# define BSWAP bswap128
|
# define BSWAP bswap128
|
||||||
# define SHIFT 4
|
|
||||||
#elif DATA_SIZE == 8
|
#elif DATA_SIZE == 8
|
||||||
# define SUFFIX q
|
# define SUFFIX q
|
||||||
# define DATA_TYPE uint64_t
|
# define DATA_TYPE uint64_t
|
||||||
# define SDATA_TYPE int64_t
|
|
||||||
# define BSWAP bswap64
|
# define BSWAP bswap64
|
||||||
# define SHIFT 3
|
|
||||||
#elif DATA_SIZE == 4
|
#elif DATA_SIZE == 4
|
||||||
# define SUFFIX l
|
# define SUFFIX l
|
||||||
# define DATA_TYPE uint32_t
|
# define DATA_TYPE uint32_t
|
||||||
# define SDATA_TYPE int32_t
|
|
||||||
# define BSWAP bswap32
|
# define BSWAP bswap32
|
||||||
# define SHIFT 2
|
|
||||||
#elif DATA_SIZE == 2
|
#elif DATA_SIZE == 2
|
||||||
# define SUFFIX w
|
# define SUFFIX w
|
||||||
# define DATA_TYPE uint16_t
|
# define DATA_TYPE uint16_t
|
||||||
# define SDATA_TYPE int16_t
|
|
||||||
# define BSWAP bswap16
|
# define BSWAP bswap16
|
||||||
# define SHIFT 1
|
|
||||||
#elif DATA_SIZE == 1
|
#elif DATA_SIZE == 1
|
||||||
# define SUFFIX b
|
# define SUFFIX b
|
||||||
# define DATA_TYPE uint8_t
|
# define DATA_TYPE uint8_t
|
||||||
# define SDATA_TYPE int8_t
|
|
||||||
# define BSWAP
|
# define BSWAP
|
||||||
# define SHIFT 0
|
|
||||||
#else
|
#else
|
||||||
# error unsupported data size
|
# error unsupported data size
|
||||||
#endif
|
#endif
|
||||||
@@ -59,37 +48,14 @@
|
|||||||
# define ABI_TYPE uint32_t
|
# define ABI_TYPE uint32_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ATOMIC_TRACE_RMW do { \
|
|
||||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
|
|
||||||
\
|
|
||||||
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \
|
|
||||||
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, \
|
|
||||||
info | TRACE_MEM_ST); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define ATOMIC_TRACE_LD do { \
|
|
||||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
|
|
||||||
\
|
|
||||||
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
# define ATOMIC_TRACE_ST do { \
|
|
||||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
|
|
||||||
\
|
|
||||||
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* Define host-endian atomic operations. Note that END is used within
|
/* Define host-endian atomic operations. Note that END is used within
|
||||||
the ATOMIC_NAME macro, and redefined below. */
|
the ATOMIC_NAME macro, and redefined below. */
|
||||||
#if DATA_SIZE == 1
|
#if DATA_SIZE == 1
|
||||||
# define END
|
# define END
|
||||||
# define MEND _be /* either le or be would be fine */
|
|
||||||
#elif defined(HOST_WORDS_BIGENDIAN)
|
#elif defined(HOST_WORDS_BIGENDIAN)
|
||||||
# define END _be
|
# define END _be
|
||||||
# define MEND _be
|
|
||||||
#else
|
#else
|
||||||
# define END _le
|
# define END _le
|
||||||
# define MEND _le
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||||
@@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
|||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
DATA_TYPE ret;
|
DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||||
|
|
||||||
ATOMIC_TRACE_RMW;
|
|
||||||
#if DATA_SIZE == 16
|
|
||||||
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
|
||||||
#else
|
|
||||||
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
|
||||||
#endif
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DATA_SIZE >= 16
|
#if DATA_SIZE >= 16
|
||||||
#if HAVE_ATOMIC128
|
|
||||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||||
ATOMIC_TRACE_LD;
|
|
||||||
val = atomic16_read(haddr);
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
|||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||||
ATOMIC_TRACE_ST;
|
|
||||||
atomic16_set(haddr, val);
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||||
ABI_TYPE val EXTRA_ARGS)
|
ABI_TYPE val EXTRA_ARGS)
|
||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
DATA_TYPE ret;
|
DATA_TYPE ret = atomic_xchg__nocheck(haddr, val);
|
||||||
|
|
||||||
ATOMIC_TRACE_RMW;
|
|
||||||
ret = atomic_xchg__nocheck(haddr, val);
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||||||
{ \
|
{ \
|
||||||
ATOMIC_MMU_DECLS; \
|
ATOMIC_MMU_DECLS; \
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||||
DATA_TYPE ret; \
|
DATA_TYPE ret = atomic_##X(haddr, val); \
|
||||||
\
|
|
||||||
ATOMIC_TRACE_RMW; \
|
|
||||||
ret = atomic_##X(haddr, val); \
|
|
||||||
ATOMIC_MMU_CLEANUP; \
|
ATOMIC_MMU_CLEANUP; \
|
||||||
return ret; \
|
return ret; \
|
||||||
}
|
}
|
||||||
@@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch)
|
|||||||
GEN_ATOMIC_HELPER(xor_fetch)
|
GEN_ATOMIC_HELPER(xor_fetch)
|
||||||
|
|
||||||
#undef GEN_ATOMIC_HELPER
|
#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.
|
|
||||||
*
|
|
||||||
* Trace this load + RMW loop as a single RMW op. This way, regardless
|
|
||||||
* of CF_PARALLEL's value, we'll trace just a read and a write.
|
|
||||||
*/
|
|
||||||
#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; \
|
|
||||||
\
|
|
||||||
ATOMIC_TRACE_RMW; \
|
|
||||||
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 */
|
#endif /* DATA SIZE >= 16 */
|
||||||
|
|
||||||
#undef END
|
#undef END
|
||||||
#undef MEND
|
|
||||||
|
|
||||||
#if DATA_SIZE > 1
|
#if DATA_SIZE > 1
|
||||||
|
|
||||||
@@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
|||||||
within the ATOMIC_NAME macro. */
|
within the ATOMIC_NAME macro. */
|
||||||
#ifdef HOST_WORDS_BIGENDIAN
|
#ifdef HOST_WORDS_BIGENDIAN
|
||||||
# define END _le
|
# define END _le
|
||||||
# define MEND _le
|
|
||||||
#else
|
#else
|
||||||
# define END _be
|
# define END _be
|
||||||
# define MEND _be
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||||
@@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
|||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
DATA_TYPE ret;
|
DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||||
|
|
||||||
ATOMIC_TRACE_RMW;
|
|
||||||
#if DATA_SIZE == 16
|
|
||||||
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
|
|
||||||
#else
|
|
||||||
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
|
||||||
#endif
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return BSWAP(ret);
|
return BSWAP(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DATA_SIZE >= 16
|
#if DATA_SIZE >= 16
|
||||||
#if HAVE_ATOMIC128
|
|
||||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||||
ATOMIC_TRACE_LD;
|
|
||||||
val = atomic16_read(haddr);
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return BSWAP(val);
|
return BSWAP(val);
|
||||||
}
|
}
|
||||||
@@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
|||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
|
||||||
ATOMIC_TRACE_ST;
|
|
||||||
val = BSWAP(val);
|
val = BSWAP(val);
|
||||||
atomic16_set(haddr, val);
|
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||||
ABI_TYPE val EXTRA_ARGS)
|
ABI_TYPE val EXTRA_ARGS)
|
||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
ABI_TYPE ret;
|
ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val));
|
||||||
|
|
||||||
ATOMIC_TRACE_RMW;
|
|
||||||
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
|
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return BSWAP(ret);
|
return BSWAP(ret);
|
||||||
}
|
}
|
||||||
@@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||||||
{ \
|
{ \
|
||||||
ATOMIC_MMU_DECLS; \
|
ATOMIC_MMU_DECLS; \
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||||
DATA_TYPE ret; \
|
DATA_TYPE ret = atomic_##X(haddr, BSWAP(val)); \
|
||||||
\
|
|
||||||
ATOMIC_TRACE_RMW; \
|
|
||||||
ret = atomic_##X(haddr, BSWAP(val)); \
|
|
||||||
ATOMIC_MMU_CLEANUP; \
|
ATOMIC_MMU_CLEANUP; \
|
||||||
return BSWAP(ret); \
|
return BSWAP(ret); \
|
||||||
}
|
}
|
||||||
@@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
|||||||
|
|
||||||
#undef GEN_ATOMIC_HELPER
|
#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.
|
|
||||||
*
|
|
||||||
* Trace this load + RMW loop as a single RMW op. This way, regardless
|
|
||||||
* of CF_PARALLEL's value, we'll trace just a read and a write.
|
|
||||||
*/
|
|
||||||
#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; \
|
|
||||||
\
|
|
||||||
ATOMIC_TRACE_RMW; \
|
|
||||||
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
|
/* Note that for addition, we need to use a separate cmpxchg loop instead
|
||||||
of bswaps for the reverse-host-endian helpers. */
|
of bswaps for the reverse-host-endian helpers. */
|
||||||
#define ADD(X, Y) (X + Y)
|
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
|
||||||
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
|
ABI_TYPE val EXTRA_ARGS)
|
||||||
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
{
|
||||||
#undef ADD
|
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 */
|
#endif /* DATA_SIZE >= 16 */
|
||||||
|
|
||||||
#undef END
|
#undef END
|
||||||
#undef MEND
|
|
||||||
#endif /* DATA_SIZE > 1 */
|
#endif /* DATA_SIZE > 1 */
|
||||||
|
|
||||||
#undef ATOMIC_TRACE_ST
|
|
||||||
#undef ATOMIC_TRACE_LD
|
|
||||||
#undef ATOMIC_TRACE_RMW
|
|
||||||
|
|
||||||
#undef BSWAP
|
#undef BSWAP
|
||||||
#undef ABI_TYPE
|
#undef ABI_TYPE
|
||||||
#undef DATA_TYPE
|
#undef DATA_TYPE
|
||||||
#undef SDATA_TYPE
|
|
||||||
#undef SUFFIX
|
#undef SUFFIX
|
||||||
#undef DATA_SIZE
|
#undef DATA_SIZE
|
||||||
#undef SHIFT
|
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ bool tcg_allowed;
|
|||||||
/* exit the current TB, but without causing any exception to be raised */
|
/* exit the current TB, but without causing any exception to be raised */
|
||||||
void cpu_loop_exit_noexc(CPUState *cpu)
|
void cpu_loop_exit_noexc(CPUState *cpu)
|
||||||
{
|
{
|
||||||
|
/* XXX: restore cpu registers saved in host registers */
|
||||||
|
|
||||||
cpu->exception_index = -1;
|
cpu->exception_index = -1;
|
||||||
cpu_loop_exit(cpu);
|
siglongjmp(cpu->jmp_env, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_SOFTMMU)
|
#if defined(CONFIG_SOFTMMU)
|
||||||
@@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void)
|
|||||||
|
|
||||||
void cpu_loop_exit(CPUState *cpu)
|
void cpu_loop_exit(CPUState *cpu)
|
||||||
{
|
{
|
||||||
/* Undo the setting in cpu_tb_exec. */
|
|
||||||
cpu->can_do_io = 1;
|
|
||||||
siglongjmp(cpu->jmp_env, 1);
|
siglongjmp(cpu->jmp_env, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
||||||
{
|
{
|
||||||
if (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)
|
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "qemu/atomic.h"
|
#include "qemu/atomic.h"
|
||||||
#include "sysemu/qtest.h"
|
#include "sysemu/qtest.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
#include "qemu/rcu.h"
|
#include "qemu/rcu.h"
|
||||||
#include "exec/tb-hash.h"
|
#include "exec/tb-hash.h"
|
||||||
#include "exec/tb-lookup.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)
|
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
|
||||||
&& qemu_log_in_addr_range(itb->pc)) {
|
&& qemu_log_in_addr_range(itb->pc)) {
|
||||||
qemu_log_lock();
|
qemu_log_lock();
|
||||||
int flags = 0;
|
|
||||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
|
||||||
flags |= CPU_DUMP_FPU;
|
|
||||||
}
|
|
||||||
#if defined(TARGET_I386)
|
#if defined(TARGET_I386)
|
||||||
flags |= CPU_DUMP_CCOP;
|
log_cpu_state(cpu, CPU_DUMP_CCOP);
|
||||||
|
#else
|
||||||
|
log_cpu_state(cpu, 0);
|
||||||
#endif
|
#endif
|
||||||
log_cpu_state(cpu, flags);
|
|
||||||
qemu_log_unlock();
|
qemu_log_unlock();
|
||||||
}
|
}
|
||||||
#endif /* DEBUG_DISAS */
|
#endif /* DEBUG_DISAS */
|
||||||
@@ -212,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
|||||||
We only end up here when an existing TB is too long. */
|
We only end up here when an existing TB is too long. */
|
||||||
cflags |= MIN(max_cycles, CF_COUNT_MASK);
|
cflags |= MIN(max_cycles, CF_COUNT_MASK);
|
||||||
|
|
||||||
mmap_lock();
|
tb_lock();
|
||||||
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
|
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
|
||||||
orig_tb->flags, cflags);
|
orig_tb->flags, cflags);
|
||||||
tb->orig_tb = orig_tb;
|
tb->orig_tb = orig_tb;
|
||||||
mmap_unlock();
|
tb_unlock();
|
||||||
|
|
||||||
/* execute the generated code */
|
/* execute the generated code */
|
||||||
trace_exec_tb_nocache(tb, tb->pc);
|
trace_exec_tb_nocache(tb, tb->pc);
|
||||||
cpu_tb_exec(cpu, tb);
|
cpu_tb_exec(cpu, tb);
|
||||||
|
|
||||||
mmap_lock();
|
tb_lock();
|
||||||
tb_phys_invalidate(tb, -1);
|
tb_phys_invalidate(tb, -1);
|
||||||
mmap_unlock();
|
tb_remove(tb);
|
||||||
tcg_tb_remove(tb);
|
tb_unlock();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -244,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||||
if (tb == NULL) {
|
if (tb == NULL) {
|
||||||
mmap_lock();
|
mmap_lock();
|
||||||
|
tb_lock();
|
||||||
|
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
|
||||||
|
if (likely(tb == NULL)) {
|
||||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||||
|
}
|
||||||
|
tb_unlock();
|
||||||
mmap_unlock();
|
mmap_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,14 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||||||
cpu_tb_exec(cpu, tb);
|
cpu_tb_exec(cpu, tb);
|
||||||
cc->cpu_exec_exit(cpu);
|
cc->cpu_exec_exit(cpu);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/* We may have exited due to another problem here, so we need
|
||||||
|
* to reset any tb_locks we may have taken but didn't release.
|
||||||
* The mmap_lock is dropped by tb_gen_code if it runs out of
|
* The mmap_lock is dropped by tb_gen_code if it runs out of
|
||||||
* memory.
|
* memory.
|
||||||
*/
|
*/
|
||||||
#ifndef CONFIG_SOFTMMU
|
#ifndef CONFIG_SOFTMMU
|
||||||
tcg_debug_assert(!have_mmap_lock());
|
tcg_debug_assert(!have_mmap_lock());
|
||||||
#endif
|
#endif
|
||||||
assert_no_pages_locked();
|
tb_lock_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_exclusive_region) {
|
if (in_exclusive_region) {
|
||||||
@@ -289,7 +293,7 @@ struct tb_desc {
|
|||||||
uint32_t trace_vcpu_dstate;
|
uint32_t trace_vcpu_dstate;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool tb_lookup_cmp(const void *p, const void *d)
|
static bool tb_cmp(const void *p, const void *d)
|
||||||
{
|
{
|
||||||
const TranslationBlock *tb = p;
|
const TranslationBlock *tb = p;
|
||||||
const struct tb_desc *desc = d;
|
const struct tb_desc *desc = d;
|
||||||
@@ -332,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
|||||||
desc.trace_vcpu_dstate = *cpu->trace_dstate;
|
desc.trace_vcpu_dstate = *cpu->trace_dstate;
|
||||||
desc.pc = pc;
|
desc.pc = pc;
|
||||||
phys_pc = get_page_addr_code(desc.env, pc);
|
phys_pc = get_page_addr_code(desc.env, pc);
|
||||||
if (phys_pc == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
||||||
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
|
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
|
||||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
|
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
|
||||||
@@ -351,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with tb_lock held. */
|
||||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||||
TranslationBlock *tb_next)
|
TranslationBlock *tb_next)
|
||||||
{
|
{
|
||||||
uintptr_t old;
|
|
||||||
|
|
||||||
assert(n < ARRAY_SIZE(tb->jmp_list_next));
|
assert(n < ARRAY_SIZE(tb->jmp_list_next));
|
||||||
qemu_spin_lock(&tb_next->jmp_lock);
|
if (tb->jmp_list_next[n]) {
|
||||||
|
/* Another thread has already done this while we were
|
||||||
/* make sure the destination TB is valid */
|
* outside of the lock; nothing to do in this case */
|
||||||
if (tb_next->cflags & CF_INVALID) {
|
return;
|
||||||
goto out_unlock_next;
|
|
||||||
}
|
}
|
||||||
/* Atomically claim the jump destination slot only if it was NULL */
|
|
||||||
old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next);
|
|
||||||
if (old) {
|
|
||||||
goto out_unlock_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* patch the native jump address */
|
|
||||||
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
|
|
||||||
|
|
||||||
/* add in TB jmp list */
|
|
||||||
tb->jmp_list_next[n] = tb_next->jmp_list_head;
|
|
||||||
tb_next->jmp_list_head = (uintptr_t)tb | n;
|
|
||||||
|
|
||||||
qemu_spin_unlock(&tb_next->jmp_lock);
|
|
||||||
|
|
||||||
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
|
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
|
||||||
"Linking TBs %p [" TARGET_FMT_lx
|
"Linking TBs %p [" TARGET_FMT_lx
|
||||||
"] index %d -> %p [" TARGET_FMT_lx "]\n",
|
"] index %d -> %p [" TARGET_FMT_lx "]\n",
|
||||||
tb->tc.ptr, tb->pc, n,
|
tb->tc.ptr, tb->pc, n,
|
||||||
tb_next->tc.ptr, tb_next->pc);
|
tb_next->tc.ptr, tb_next->pc);
|
||||||
return;
|
|
||||||
|
|
||||||
out_unlock_next:
|
/* patch the native jump address */
|
||||||
qemu_spin_unlock(&tb_next->jmp_lock);
|
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
|
||||||
return;
|
|
||||||
|
/* add in TB jmp circular list */
|
||||||
|
tb->jmp_list_next[n] = tb_next->jmp_list_first;
|
||||||
|
tb_next->jmp_list_first = (uintptr_t)tb | n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline TranslationBlock *tb_find(CPUState *cpu,
|
static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||||
@@ -397,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
|||||||
TranslationBlock *tb;
|
TranslationBlock *tb;
|
||||||
target_ulong cs_base, pc;
|
target_ulong cs_base, pc;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
bool acquired_tb_lock = false;
|
||||||
|
|
||||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||||
if (tb == NULL) {
|
if (tb == NULL) {
|
||||||
|
/* mmap_lock is needed by tb_gen_code, and mmap_lock must be
|
||||||
|
* taken outside tb_lock. As system emulation is currently
|
||||||
|
* single threaded the locks are NOPs.
|
||||||
|
*/
|
||||||
mmap_lock();
|
mmap_lock();
|
||||||
|
tb_lock();
|
||||||
|
acquired_tb_lock = true;
|
||||||
|
|
||||||
|
/* There's a chance that our desired tb has been translated while
|
||||||
|
* taking the locks so we check again inside the lock.
|
||||||
|
*/
|
||||||
|
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
|
||||||
|
if (likely(tb == NULL)) {
|
||||||
|
/* if no translated code available, then translate it now */
|
||||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
|
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
|
||||||
|
}
|
||||||
|
|
||||||
mmap_unlock();
|
mmap_unlock();
|
||||||
/* We add the TB in the virtual pc hash table for the fast lookup */
|
/* We add the TB in the virtual pc hash table for the fast lookup */
|
||||||
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
|
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
|
||||||
@@ -416,9 +418,18 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* See if we can patch the calling TB. */
|
/* See if we can patch the calling TB. */
|
||||||
if (last_tb) {
|
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||||
|
if (!acquired_tb_lock) {
|
||||||
|
tb_lock();
|
||||||
|
acquired_tb_lock = true;
|
||||||
|
}
|
||||||
|
if (!(tb->cflags & CF_INVALID)) {
|
||||||
tb_add_jump(last_tb, tb_exit, tb);
|
tb_add_jump(last_tb, tb_exit, tb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (acquired_tb_lock) {
|
||||||
|
tb_unlock();
|
||||||
|
}
|
||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
|||||||
else {
|
else {
|
||||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||||
replay_interrupt();
|
replay_interrupt();
|
||||||
cpu->exception_index = -1;
|
|
||||||
*last_tb = NULL;
|
*last_tb = NULL;
|
||||||
}
|
}
|
||||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
/* The target hook may have updated the 'cpu->interrupt_request';
|
||||||
@@ -596,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
|||||||
if (unlikely(atomic_read(&cpu->exit_request)
|
if (unlikely(atomic_read(&cpu->exit_request)
|
||||||
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
|
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
|
||||||
atomic_set(&cpu->exit_request, 0);
|
atomic_set(&cpu->exit_request, 0);
|
||||||
if (cpu->exception_index == -1) {
|
|
||||||
cpu->exception_index = EXCP_INTERRUPT;
|
cpu->exception_index = EXCP_INTERRUPT;
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,9 +701,8 @@ int cpu_exec(CPUState *cpu)
|
|||||||
g_assert(cpu == current_cpu);
|
g_assert(cpu == current_cpu);
|
||||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||||
#endif /* buggy compiler */
|
#endif /* buggy compiler */
|
||||||
#ifndef CONFIG_SOFTMMU
|
cpu->can_do_io = 1;
|
||||||
tcg_debug_assert(!have_mmap_lock());
|
tb_lock_reset();
|
||||||
#endif
|
|
||||||
if (qemu_mutex_iothread_locked()) {
|
if (qemu_mutex_iothread_locked()) {
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -98,23 +98,19 @@
|
|||||||
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||||
size_t mmu_idx, size_t index,
|
size_t mmu_idx, size_t index,
|
||||||
target_ulong addr,
|
target_ulong addr,
|
||||||
uintptr_t retaddr,
|
uintptr_t retaddr)
|
||||||
bool recheck,
|
|
||||||
MMUAccessType access_type)
|
|
||||||
{
|
{
|
||||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||||
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
|
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE);
|
||||||
access_type, DATA_SIZE);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
unsigned mmu_idx = get_mmuidx(oi);
|
||||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||||
target_ulong tlb_addr = entry->ADDR_READ;
|
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
@@ -125,12 +121,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If the TLB entry is for a different page, reload and try again. */
|
/* If the TLB entry is for a different page, reload and try again. */
|
||||||
if (!tlb_hit(tlb_addr, addr)) {
|
if ((addr & TARGET_PAGE_MASK)
|
||||||
|
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
}
|
}
|
||||||
tlb_addr = entry->ADDR_READ;
|
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
@@ -141,9 +138,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
|
|
||||||
/* ??? Note that the io helpers always read data in the target
|
/* ??? Note that the io helpers always read data in the target
|
||||||
byte ordering. We should push the LE/BE request down into io. */
|
byte ordering. We should push the LE/BE request down into io. */
|
||||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
|
||||||
tlb_addr & TLB_RECHECK,
|
|
||||||
READ_ACCESS_TYPE);
|
|
||||||
res = TGT_LE(res);
|
res = TGT_LE(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -167,7 +162,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + entry->addend;
|
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||||
#if DATA_SIZE == 1
|
#if DATA_SIZE == 1
|
||||||
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
||||||
#else
|
#else
|
||||||
@@ -180,10 +175,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
unsigned mmu_idx = get_mmuidx(oi);
|
||||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||||
target_ulong tlb_addr = entry->ADDR_READ;
|
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
@@ -194,12 +188,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If the TLB entry is for a different page, reload and try again. */
|
/* If the TLB entry is for a different page, reload and try again. */
|
||||||
if (!tlb_hit(tlb_addr, addr)) {
|
if ((addr & TARGET_PAGE_MASK)
|
||||||
|
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
}
|
}
|
||||||
tlb_addr = entry->ADDR_READ;
|
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
@@ -210,9 +205,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
|
|
||||||
/* ??? Note that the io helpers always read data in the target
|
/* ??? Note that the io helpers always read data in the target
|
||||||
byte ordering. We should push the LE/BE request down into io. */
|
byte ordering. We should push the LE/BE request down into io. */
|
||||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
|
||||||
tlb_addr & TLB_RECHECK,
|
|
||||||
READ_ACCESS_TYPE);
|
|
||||||
res = TGT_BE(res);
|
res = TGT_BE(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -236,7 +229,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + entry->addend;
|
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||||
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -266,21 +259,18 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
|||||||
size_t mmu_idx, size_t index,
|
size_t mmu_idx, size_t index,
|
||||||
DATA_TYPE val,
|
DATA_TYPE val,
|
||||||
target_ulong addr,
|
target_ulong addr,
|
||||||
uintptr_t retaddr,
|
uintptr_t retaddr)
|
||||||
bool recheck)
|
|
||||||
{
|
{
|
||||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||||
return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
|
return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE);
|
||||||
recheck, DATA_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
unsigned mmu_idx = get_mmuidx(oi);
|
||||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
|
||||||
@@ -290,12 +280,13 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If the TLB entry is for a different page, reload and try again. */
|
/* If the TLB entry is for a different page, reload and try again. */
|
||||||
if (!tlb_hit(tlb_addr, addr)) {
|
if ((addr & TARGET_PAGE_MASK)
|
||||||
|
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
}
|
}
|
||||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
@@ -307,8 +298,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
/* ??? Note that the io helpers always read data in the target
|
/* ??? Note that the io helpers always read data in the target
|
||||||
byte ordering. We should push the LE/BE request down into io. */
|
byte ordering. We should push the LE/BE request down into io. */
|
||||||
val = TGT_LE(val);
|
val = TGT_LE(val);
|
||||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
|
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
|
||||||
retaddr, tlb_addr & TLB_RECHECK);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,16 +306,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
if (DATA_SIZE > 1
|
if (DATA_SIZE > 1
|
||||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||||
>= TARGET_PAGE_SIZE)) {
|
>= TARGET_PAGE_SIZE)) {
|
||||||
int i;
|
int i, index2;
|
||||||
target_ulong page2;
|
target_ulong page2, tlb_addr2;
|
||||||
CPUTLBEntry *entry2;
|
|
||||||
do_unaligned_access:
|
do_unaligned_access:
|
||||||
/* Ensure the second page is in the TLB. Note that the first page
|
/* Ensure the second page is in the TLB. Note that the first page
|
||||||
is already guaranteed to be filled, and that the second page
|
is already guaranteed to be filled, and that the second page
|
||||||
cannot evict the first. */
|
cannot evict the first. */
|
||||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||||
|
if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK))
|
||||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
@@ -343,7 +333,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + entry->addend;
|
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||||
#if DATA_SIZE == 1
|
#if DATA_SIZE == 1
|
||||||
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
||||||
#else
|
#else
|
||||||
@@ -355,10 +345,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
unsigned mmu_idx = get_mmuidx(oi);
|
||||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
|
||||||
@@ -368,12 +357,13 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If the TLB entry is for a different page, reload and try again. */
|
/* If the TLB entry is for a different page, reload and try again. */
|
||||||
if (!tlb_hit(tlb_addr, addr)) {
|
if ((addr & TARGET_PAGE_MASK)
|
||||||
|
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
}
|
}
|
||||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
@@ -385,8 +375,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
/* ??? Note that the io helpers always read data in the target
|
/* ??? Note that the io helpers always read data in the target
|
||||||
byte ordering. We should push the LE/BE request down into io. */
|
byte ordering. We should push the LE/BE request down into io. */
|
||||||
val = TGT_BE(val);
|
val = TGT_BE(val);
|
||||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
|
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
|
||||||
tlb_addr & TLB_RECHECK);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,16 +383,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
if (DATA_SIZE > 1
|
if (DATA_SIZE > 1
|
||||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||||
>= TARGET_PAGE_SIZE)) {
|
>= TARGET_PAGE_SIZE)) {
|
||||||
int i;
|
int i, index2;
|
||||||
target_ulong page2;
|
target_ulong page2, tlb_addr2;
|
||||||
CPUTLBEntry *entry2;
|
|
||||||
do_unaligned_access:
|
do_unaligned_access:
|
||||||
/* Ensure the second page is in the TLB. Note that the first page
|
/* Ensure the second page is in the TLB. Note that the first page
|
||||||
is already guaranteed to be filled, and that the second page
|
is already guaranteed to be filled, and that the second page
|
||||||
cannot evict the first. */
|
cannot evict the first. */
|
||||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||||
|
if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK))
|
||||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
@@ -421,7 +410,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + entry->addend;
|
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||||
}
|
}
|
||||||
#endif /* DATA_SIZE > 1 */
|
#endif /* DATA_SIZE > 1 */
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
|||||||
if (!qemu_cpu_is_self(cpu)) {
|
if (!qemu_cpu_is_self(cpu)) {
|
||||||
qemu_cpu_kick(cpu);
|
qemu_cpu_kick(cpu);
|
||||||
} else {
|
} else {
|
||||||
atomic_set(&cpu->icount_decr.u16.high, -1);
|
cpu->icount_decr.u16.high = -1;
|
||||||
if (use_icount &&
|
if (use_icount &&
|
||||||
!cpu->can_do_io
|
!cpu->can_do_io
|
||||||
&& (mask & ~old_mask) != 0) {
|
&& (mask & ~old_mask) != 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 oprsz = simd_oprsz(desc); \
|
||||||
intptr_t i; \
|
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)); \
|
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||||
} \
|
} \
|
||||||
clear_high(d, oprsz, desc); \
|
clear_high(d, oprsz, desc); \
|
||||||
|
|||||||
@@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add)
|
|||||||
GEN_ATOMIC_HELPERS(fetch_and)
|
GEN_ATOMIC_HELPERS(fetch_and)
|
||||||
GEN_ATOMIC_HELPERS(fetch_or)
|
GEN_ATOMIC_HELPERS(fetch_or)
|
||||||
GEN_ATOMIC_HELPERS(fetch_xor)
|
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(add_fetch)
|
||||||
GEN_ATOMIC_HELPERS(and_fetch)
|
GEN_ATOMIC_HELPERS(and_fetch)
|
||||||
GEN_ATOMIC_HELPERS(or_fetch)
|
GEN_ATOMIC_HELPERS(or_fetch)
|
||||||
GEN_ATOMIC_HELPERS(xor_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)
|
GEN_ATOMIC_HELPERS(xchg)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -23,13 +23,10 @@
|
|||||||
|
|
||||||
|
|
||||||
/* translate-all.c */
|
/* translate-all.c */
|
||||||
struct page_collection *page_collection_lock(tb_page_addr_t start,
|
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
|
||||||
tb_page_addr_t end);
|
|
||||||
void page_collection_unlock(struct page_collection *set);
|
|
||||||
void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
|
||||||
tb_page_addr_t start, int len);
|
|
||||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
|
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
|
||||||
int is_cpu_write_access);
|
int is_cpu_write_access);
|
||||||
|
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
|
||||||
void tb_check_watchpoint(CPUState *cpu);
|
void tb_check_watchpoint(CPUState *cpu);
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ void translator_loop_temp_check(DisasContextBase *db)
|
|||||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||||
CPUState *cpu, TranslationBlock *tb)
|
CPUState *cpu, TranslationBlock *tb)
|
||||||
{
|
{
|
||||||
int bp_insn = 0;
|
int max_insns;
|
||||||
|
|
||||||
/* Initialize DisasContext */
|
/* Initialize DisasContext */
|
||||||
db->tb = tb;
|
db->tb = tb;
|
||||||
@@ -45,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||||||
db->singlestep_enabled = cpu->singlestep_enabled;
|
db->singlestep_enabled = cpu->singlestep_enabled;
|
||||||
|
|
||||||
/* Instruction counting */
|
/* Instruction counting */
|
||||||
db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
||||||
if (db->max_insns == 0) {
|
if (max_insns == 0) {
|
||||||
db->max_insns = CF_COUNT_MASK;
|
max_insns = CF_COUNT_MASK;
|
||||||
}
|
}
|
||||||
if (db->max_insns > TCG_MAX_INSNS) {
|
if (max_insns > TCG_MAX_INSNS) {
|
||||||
db->max_insns = TCG_MAX_INSNS;
|
max_insns = TCG_MAX_INSNS;
|
||||||
}
|
}
|
||||||
if (db->singlestep_enabled || singlestep) {
|
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 */
|
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||||
|
|
||||||
/* Reset the temp count so that we can identify leaks */
|
/* Reset the temp count so that we can identify leaks */
|
||||||
@@ -73,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||||
|
|
||||||
/* Pass breakpoint hits to target for further processing */
|
/* Pass breakpoint hits to target for further processing */
|
||||||
if (!db->singlestep_enabled
|
if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
|
||||||
CPUBreakpoint *bp;
|
CPUBreakpoint *bp;
|
||||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||||
if (bp->pc == db->pc_next) {
|
if (bp->pc == db->pc_next) {
|
||||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||||
bp_insn = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||||||
update db->pc_next and db->is_jmp to indicate what should be
|
update db->pc_next and db->is_jmp to indicate what should be
|
||||||
done next -- either exiting this loop or locate the start of
|
done next -- either exiting this loop or locate the start of
|
||||||
the next instruction. */
|
the next instruction. */
|
||||||
if (db->num_insns == db->max_insns
|
if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) {
|
||||||
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
|
|
||||||
/* Accept I/O on the last instruction. */
|
/* Accept I/O on the last instruction. */
|
||||||
gen_io_start();
|
gen_io_start();
|
||||||
ops->translate_insn(db, cpu);
|
ops->translate_insn(db, cpu);
|
||||||
@@ -114,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||||||
|
|
||||||
/* Stop translation if the output buffer is full,
|
/* Stop translation if the output buffer is full,
|
||||||
or we have executed all of the allowed instructions. */
|
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;
|
db->is_jmp = DISAS_TOO_MANY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -122,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||||||
|
|
||||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||||
ops->tb_stop(db, cpu);
|
ops->tb_stop(db, cpu);
|
||||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
gen_tb_end(db->tb, db->num_insns);
|
||||||
|
|
||||||
/* The disas_log hook may use these values rather than recompute. */
|
/* The disas_log hook may use these values rather than recompute. */
|
||||||
db->tb->size = db->pc_next - db->pc_first;
|
db->tb->size = db->pc_next - db->pc_first;
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qom/cpu.h"
|
#include "qom/cpu.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
#include "sysemu/sysemu.h"
|
|
||||||
|
|
||||||
bool enable_cpu_pm = false;
|
|
||||||
|
|
||||||
void cpu_resume(CPUState *cpu)
|
void cpu_resume(CPUState *cpu)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include "exec/cpu_ldst.h"
|
#include "exec/cpu_ldst.h"
|
||||||
#include "translate-all.h"
|
#include "translate-all.h"
|
||||||
#include "exec/helper-proto.h"
|
#include "exec/helper-proto.h"
|
||||||
#include "qemu/atomic128.h"
|
|
||||||
|
|
||||||
#undef EAX
|
#undef EAX
|
||||||
#undef ECX
|
#undef ECX
|
||||||
@@ -169,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now we have a real cpu fault. */
|
/* Now we have a real cpu fault. */
|
||||||
cpu_restore_state(cpu, pc, true);
|
cpu_restore_state(cpu, pc);
|
||||||
|
|
||||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||||
cpu_loop_exit(cpu);
|
cpu_loop_exit(cpu);
|
||||||
@@ -616,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
|||||||
/* The following is only callable from other helpers, and matches up
|
/* The following is only callable from other helpers, and matches up
|
||||||
with the softmmu version. */
|
with the softmmu version. */
|
||||||
|
|
||||||
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
|
#ifdef CONFIG_ATOMIC128
|
||||||
|
|
||||||
#undef EXTRA_ARGS
|
#undef EXTRA_ARGS
|
||||||
#undef ATOMIC_NAME
|
#undef ATOMIC_NAME
|
||||||
@@ -629,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
|||||||
|
|
||||||
#define DATA_SIZE 16
|
#define DATA_SIZE 16
|
||||||
#include "atomic_template.h"
|
#include "atomic_template.h"
|
||||||
#endif
|
#endif /* CONFIG_ATOMIC128 */
|
||||||
|
|||||||
20
arch_init.c
20
arch_init.c
@@ -29,7 +29,6 @@
|
|||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/audio/soundhw.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "qapi/qapi-commands-misc.h"
|
#include "qapi/qapi-commands-misc.h"
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "hw/acpi/acpi.h"
|
#include "hw/acpi/acpi.h"
|
||||||
@@ -52,14 +51,14 @@ int graphic_depth = 32;
|
|||||||
#define QEMU_ARCH QEMU_ARCH_ARM
|
#define QEMU_ARCH QEMU_ARCH_ARM
|
||||||
#elif defined(TARGET_CRIS)
|
#elif defined(TARGET_CRIS)
|
||||||
#define QEMU_ARCH QEMU_ARCH_CRIS
|
#define QEMU_ARCH QEMU_ARCH_CRIS
|
||||||
#elif defined(TARGET_HPPA)
|
|
||||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
|
||||||
#elif defined(TARGET_I386)
|
#elif defined(TARGET_I386)
|
||||||
#define QEMU_ARCH QEMU_ARCH_I386
|
#define QEMU_ARCH QEMU_ARCH_I386
|
||||||
#elif defined(TARGET_LM32)
|
#elif defined(TARGET_HPPA)
|
||||||
#define QEMU_ARCH QEMU_ARCH_LM32
|
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||||
#elif defined(TARGET_M68K)
|
#elif defined(TARGET_M68K)
|
||||||
#define QEMU_ARCH QEMU_ARCH_M68K
|
#define QEMU_ARCH QEMU_ARCH_M68K
|
||||||
|
#elif defined(TARGET_LM32)
|
||||||
|
#define QEMU_ARCH QEMU_ARCH_LM32
|
||||||
#elif defined(TARGET_MICROBLAZE)
|
#elif defined(TARGET_MICROBLAZE)
|
||||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
|
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
|
||||||
#elif defined(TARGET_MIPS)
|
#elif defined(TARGET_MIPS)
|
||||||
@@ -80,12 +79,12 @@ int graphic_depth = 32;
|
|||||||
#define QEMU_ARCH QEMU_ARCH_SH4
|
#define QEMU_ARCH QEMU_ARCH_SH4
|
||||||
#elif defined(TARGET_SPARC)
|
#elif defined(TARGET_SPARC)
|
||||||
#define QEMU_ARCH QEMU_ARCH_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)
|
#elif defined(TARGET_XTENSA)
|
||||||
#define QEMU_ARCH QEMU_ARCH_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
|
#endif
|
||||||
|
|
||||||
const uint32_t arch_type = QEMU_ARCH;
|
const uint32_t arch_type = QEMU_ARCH;
|
||||||
@@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp)
|
|||||||
{
|
{
|
||||||
TargetInfo *info = g_malloc0(sizeof(*info));
|
TargetInfo *info = g_malloc0(sizeof(*info));
|
||||||
|
|
||||||
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
|
info->arch = g_strdup(TARGET_NAME);
|
||||||
&error_abort);
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
#define AUDIO_CAP "audio"
|
#define AUDIO_CAP "audio"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
@@ -336,8 +335,9 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp)
|
|||||||
char *strval;
|
char *strval;
|
||||||
|
|
||||||
strval = getenv (key);
|
strval = getenv (key);
|
||||||
if (strval && !qemu_strtoi(strval, NULL, 10, &val)) {
|
if (strval) {
|
||||||
*defaultp = 0;
|
*defaultp = 0;
|
||||||
|
val = atoi (strval);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1130,10 +1130,6 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
|
|||||||
/*
|
/*
|
||||||
* Timer
|
* Timer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool audio_timer_running;
|
|
||||||
static uint64_t audio_timer_last;
|
|
||||||
|
|
||||||
static int audio_is_timer_needed (void)
|
static int audio_is_timer_needed (void)
|
||||||
{
|
{
|
||||||
HWVoiceIn *hwi = NULL;
|
HWVoiceIn *hwi = NULL;
|
||||||
@@ -1153,31 +1149,14 @@ static void audio_reset_timer (AudioState *s)
|
|||||||
if (audio_is_timer_needed ()) {
|
if (audio_is_timer_needed ()) {
|
||||||
timer_mod_anticipate_ns(s->ts,
|
timer_mod_anticipate_ns(s->ts,
|
||||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
|
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
|
||||||
if (!audio_timer_running) {
|
|
||||||
audio_timer_running = true;
|
|
||||||
audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
||||||
trace_audio_timer_start(conf.period.ticks / SCALE_MS);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timer_del(s->ts);
|
|
||||||
if (audio_timer_running) {
|
|
||||||
audio_timer_running = false;
|
|
||||||
trace_audio_timer_stop();
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
timer_del (s->ts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_timer (void *opaque)
|
static void audio_timer (void *opaque)
|
||||||
{
|
{
|
||||||
int64_t now, diff;
|
|
||||||
|
|
||||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
||||||
diff = now - audio_timer_last;
|
|
||||||
if (diff > conf.period.ticks * 3 / 2) {
|
|
||||||
trace_audio_timer_delayed(diff / SCALE_MS);
|
|
||||||
}
|
|
||||||
audio_timer_last = now;
|
|
||||||
|
|
||||||
audio_run ("timer");
|
audio_run ("timer");
|
||||||
audio_reset_timer (opaque);
|
audio_reset_timer (opaque);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ static void *qpa_thread_out (void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5);
|
decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
|
||||||
rpos = pa->rpos;
|
rpos = pa->rpos;
|
||||||
|
|
||||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||||
@@ -319,7 +319,7 @@ static void *qpa_thread_in (void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5);
|
incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
|
||||||
wpos = pa->wpos;
|
wpos = pa->wpos;
|
||||||
|
|
||||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||||
|
|||||||
@@ -15,8 +15,3 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d"
|
|||||||
# audio/ossaudio.c
|
# audio/ossaudio.c
|
||||||
oss_version(int version) "OSS version = 0x%x"
|
oss_version(int version) "OSS version = 0x%x"
|
||||||
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
|
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
|
||||||
|
|
||||||
# audio/audio.c
|
|
||||||
audio_timer_start(int interval) "interval %d ms"
|
|
||||||
audio_timer_stop(void) ""
|
|
||||||
audio_timer_delayed(int interval) "interval %d ms"
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
|
|||||||
common-obj-$(CONFIG_TPM) += tpm.o
|
common-obj-$(CONFIG_TPM) += tpm.o
|
||||||
|
|
||||||
common-obj-y += hostmem.o hostmem-ram.o
|
common-obj-y += hostmem.o hostmem-ram.o
|
||||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
||||||
|
|
||||||
common-obj-y += cryptodev.o
|
common-obj-y += cryptodev.o
|
||||||
common-obj-y += cryptodev-builtin.o
|
common-obj-y += cryptodev-builtin.o
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "hw/virtio/vhost-user.h"
|
|
||||||
#include "standard-headers/linux/virtio_crypto.h"
|
#include "standard-headers/linux/virtio_crypto.h"
|
||||||
#include "sysemu/cryptodev-vhost.h"
|
#include "sysemu/cryptodev-vhost.h"
|
||||||
#include "chardev/char-fe.h"
|
#include "chardev/char-fe.h"
|
||||||
@@ -47,7 +46,6 @@
|
|||||||
typedef struct CryptoDevBackendVhostUser {
|
typedef struct CryptoDevBackendVhostUser {
|
||||||
CryptoDevBackend parent_obj;
|
CryptoDevBackend parent_obj;
|
||||||
|
|
||||||
VhostUserState *vhost_user;
|
|
||||||
CharBackend chr;
|
CharBackend chr;
|
||||||
char *chr_name;
|
char *chr_name;
|
||||||
bool opened;
|
bool opened;
|
||||||
@@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.opaque = s->vhost_user;
|
options.opaque = &s->chr;
|
||||||
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
||||||
options.cc = b->conf.peers.ccs[i];
|
options.cc = b->conf.peers.ccs[i];
|
||||||
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
|
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
|
||||||
@@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
|
|||||||
{
|
{
|
||||||
CryptoDevBackendVhostUser *s = opaque;
|
CryptoDevBackendVhostUser *s = opaque;
|
||||||
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
|
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
|
||||||
|
Error *err = NULL;
|
||||||
int queues = b->conf.peers.queues;
|
int queues = b->conf.peers.queues;
|
||||||
|
|
||||||
assert(queues < MAX_CRYPTO_QUEUE_NUM);
|
assert(queues < MAX_CRYPTO_QUEUE_NUM);
|
||||||
@@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
|
|||||||
cryptodev_vhost_user_stop(queues, s);
|
cryptodev_vhost_user_stop(queues, s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
error_report_err(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cryptodev_vhost_user_init(
|
static void cryptodev_vhost_user_init(
|
||||||
@@ -182,7 +185,6 @@ static void cryptodev_vhost_user_init(
|
|||||||
size_t i;
|
size_t i;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
Chardev *chr;
|
Chardev *chr;
|
||||||
VhostUserState *user;
|
|
||||||
CryptoDevBackendClient *cc;
|
CryptoDevBackendClient *cc;
|
||||||
CryptoDevBackendVhostUser *s =
|
CryptoDevBackendVhostUser *s =
|
||||||
CRYPTODEV_BACKEND_VHOST_USER(backend);
|
CRYPTODEV_BACKEND_VHOST_USER(backend);
|
||||||
@@ -213,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,
|
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
|
||||||
cryptodev_vhost_user_event, NULL, s, NULL, true);
|
cryptodev_vhost_user_event, NULL, s, NULL, true);
|
||||||
|
|
||||||
@@ -306,12 +299,6 @@ static void cryptodev_vhost_user_cleanup(
|
|||||||
backend->conf.peers.ccs[i] = NULL;
|
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,
|
static void cryptodev_vhost_user_set_chardev(Object *obj,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/error-report.h"
|
|
||||||
#include "sysemu/hostmem.h"
|
#include "sysemu/hostmem.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
@@ -32,10 +31,9 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
|
|||||||
struct HostMemoryBackendFile {
|
struct HostMemoryBackendFile {
|
||||||
HostMemoryBackend parent_obj;
|
HostMemoryBackend parent_obj;
|
||||||
|
|
||||||
|
bool discard_data;
|
||||||
char *mem_path;
|
char *mem_path;
|
||||||
uint64_t align;
|
uint64_t align;
|
||||||
bool discard_data;
|
|
||||||
bool is_pmem;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -51,7 +49,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
|||||||
error_setg(errp, "mem-path property not set");
|
error_setg(errp, "mem-path property not set");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifndef CONFIG_POSIX
|
#ifndef CONFIG_LINUX
|
||||||
error_setg(errp, "-mem-path not supported on this host");
|
error_setg(errp, "-mem-path not supported on this host");
|
||||||
#else
|
#else
|
||||||
if (!host_memory_backend_mr_inited(backend)) {
|
if (!host_memory_backend_mr_inited(backend)) {
|
||||||
@@ -60,9 +58,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
|||||||
path = object_get_canonical_path(OBJECT(backend));
|
path = object_get_canonical_path(OBJECT(backend));
|
||||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||||
path,
|
path,
|
||||||
backend->size, fb->align,
|
backend->size, fb->align, backend->share,
|
||||||
(backend->share ? RAM_SHARED : 0) |
|
|
||||||
(fb->is_pmem ? RAM_PMEM : 0),
|
|
||||||
fb->mem_path, errp);
|
fb->mem_path, errp);
|
||||||
g_free(path);
|
g_free(path);
|
||||||
}
|
}
|
||||||
@@ -134,45 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
|||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
|
|
||||||
{
|
|
||||||
return MEMORY_BACKEND_FILE(o)->is_pmem;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
|
|
||||||
{
|
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
|
||||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
|
||||||
|
|
||||||
if (host_memory_backend_mr_inited(backend)) {
|
|
||||||
char *path = object_get_canonical_path_component(o);
|
|
||||||
|
|
||||||
error_setg(errp, "cannot change property 'pmem' of %s '%s'",
|
|
||||||
object_get_typename(o),
|
|
||||||
path);
|
|
||||||
g_free(path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_LIBPMEM
|
|
||||||
if (value) {
|
|
||||||
Error *local_err = NULL;
|
|
||||||
char *path = object_get_canonical_path_component(o);
|
|
||||||
|
|
||||||
error_setg(&local_err,
|
|
||||||
"Lack of libpmem support while setting the 'pmem=on'"
|
|
||||||
" of %s '%s'. We can't ensure data persistence.",
|
|
||||||
object_get_typename(o),
|
|
||||||
path);
|
|
||||||
g_free(path);
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fb->is_pmem = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void file_backend_unparent(Object *obj)
|
static void file_backend_unparent(Object *obj)
|
||||||
{
|
{
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
@@ -204,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data)
|
|||||||
file_memory_backend_get_align,
|
file_memory_backend_get_align,
|
||||||
file_memory_backend_set_align,
|
file_memory_backend_set_align,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
object_class_property_add_bool(oc, "pmem",
|
|
||||||
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
|
|
||||||
&error_abort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void file_backend_instance_finalize(Object *o)
|
static void file_backend_instance_finalize(Object *o)
|
||||||
|
|||||||
@@ -140,31 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
bc->alloc = memfd_backend_memory_alloc;
|
bc->alloc = memfd_backend_memory_alloc;
|
||||||
|
|
||||||
if (qemu_memfd_check(MFD_HUGETLB)) {
|
|
||||||
object_class_property_add_bool(oc, "hugetlb",
|
object_class_property_add_bool(oc, "hugetlb",
|
||||||
memfd_backend_get_hugetlb,
|
memfd_backend_get_hugetlb,
|
||||||
memfd_backend_set_hugetlb,
|
memfd_backend_set_hugetlb,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
object_class_property_set_description(oc, "hugetlb",
|
|
||||||
"Use huge pages",
|
|
||||||
&error_abort);
|
|
||||||
object_class_property_add(oc, "hugetlbsize", "int",
|
object_class_property_add(oc, "hugetlbsize", "int",
|
||||||
memfd_backend_get_hugetlbsize,
|
memfd_backend_get_hugetlbsize,
|
||||||
memfd_backend_set_hugetlbsize,
|
memfd_backend_set_hugetlbsize,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
object_class_property_set_description(oc, "hugetlbsize",
|
|
||||||
"Huge pages size (ex: 2M, 1G)",
|
|
||||||
&error_abort);
|
|
||||||
}
|
|
||||||
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
|
||||||
object_class_property_add_bool(oc, "seal",
|
object_class_property_add_bool(oc, "seal",
|
||||||
memfd_backend_get_seal,
|
memfd_backend_get_seal,
|
||||||
memfd_backend_set_seal,
|
memfd_backend_set_seal,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
object_class_property_set_description(oc, "seal",
|
|
||||||
"Seal growing & shrinking",
|
|
||||||
&error_abort);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo memfd_backend_info = {
|
static const TypeInfo memfd_backend_info = {
|
||||||
@@ -177,9 +164,7 @@ static const TypeInfo memfd_backend_info = {
|
|||||||
|
|
||||||
static void register_types(void)
|
static void register_types(void)
|
||||||
{
|
{
|
||||||
if (qemu_memfd_check(0)) {
|
|
||||||
type_register_static(&memfd_backend_info);
|
type_register_static(&memfd_backend_info);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(register_types);
|
type_init(register_types);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
#include "qemu/mmap-alloc.h"
|
|
||||||
|
|
||||||
#ifdef CONFIG_NUMA
|
#ifdef CONFIG_NUMA
|
||||||
#include <numaif.h>
|
#include <numaif.h>
|
||||||
@@ -247,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
|||||||
return memory_region_size(&backend->mr) != 0;
|
return memory_region_size(&backend->mr) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend)
|
MemoryRegion *
|
||||||
|
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
|
||||||
{
|
{
|
||||||
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
|
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
|
||||||
}
|
}
|
||||||
@@ -262,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
|
|||||||
return backend->is_mapped;
|
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
|
static void
|
||||||
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||||
{
|
{
|
||||||
@@ -368,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)
|
static bool host_memory_backend_get_share(Object *o, Error **errp)
|
||||||
{
|
{
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||||
@@ -397,41 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
|||||||
object_class_property_add_bool(oc, "merge",
|
object_class_property_add_bool(oc, "merge",
|
||||||
host_memory_backend_get_merge,
|
host_memory_backend_get_merge,
|
||||||
host_memory_backend_set_merge, &error_abort);
|
host_memory_backend_set_merge, &error_abort);
|
||||||
object_class_property_set_description(oc, "merge",
|
|
||||||
"Mark memory as mergeable", &error_abort);
|
|
||||||
object_class_property_add_bool(oc, "dump",
|
object_class_property_add_bool(oc, "dump",
|
||||||
host_memory_backend_get_dump,
|
host_memory_backend_get_dump,
|
||||||
host_memory_backend_set_dump, &error_abort);
|
host_memory_backend_set_dump, &error_abort);
|
||||||
object_class_property_set_description(oc, "dump",
|
|
||||||
"Set to 'off' to exclude from core dump", &error_abort);
|
|
||||||
object_class_property_add_bool(oc, "prealloc",
|
object_class_property_add_bool(oc, "prealloc",
|
||||||
host_memory_backend_get_prealloc,
|
host_memory_backend_get_prealloc,
|
||||||
host_memory_backend_set_prealloc, &error_abort);
|
host_memory_backend_set_prealloc, &error_abort);
|
||||||
object_class_property_set_description(oc, "prealloc",
|
|
||||||
"Preallocate memory", &error_abort);
|
|
||||||
object_class_property_add(oc, "size", "int",
|
object_class_property_add(oc, "size", "int",
|
||||||
host_memory_backend_get_size,
|
host_memory_backend_get_size,
|
||||||
host_memory_backend_set_size,
|
host_memory_backend_set_size,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
object_class_property_set_description(oc, "size",
|
|
||||||
"Size of the memory region (ex: 500M)", &error_abort);
|
|
||||||
object_class_property_add(oc, "host-nodes", "int",
|
object_class_property_add(oc, "host-nodes", "int",
|
||||||
host_memory_backend_get_host_nodes,
|
host_memory_backend_get_host_nodes,
|
||||||
host_memory_backend_set_host_nodes,
|
host_memory_backend_set_host_nodes,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
object_class_property_set_description(oc, "host-nodes",
|
|
||||||
"Binds memory to the list of NUMA host nodes", &error_abort);
|
|
||||||
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
||||||
&HostMemPolicy_lookup,
|
&HostMemPolicy_lookup,
|
||||||
host_memory_backend_get_policy,
|
host_memory_backend_get_policy,
|
||||||
host_memory_backend_set_policy, &error_abort);
|
host_memory_backend_set_policy, &error_abort);
|
||||||
object_class_property_set_description(oc, "policy",
|
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
|
||||||
"Set the NUMA policy", &error_abort);
|
|
||||||
object_class_property_add_bool(oc, "share",
|
object_class_property_add_bool(oc, "share",
|
||||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
object_class_property_set_description(oc, "share",
|
}
|
||||||
"Mark the memory as private to QEMU or shared", &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 = {
|
static const TypeInfo host_memory_backend_info = {
|
||||||
@@ -442,6 +436,7 @@ static const TypeInfo host_memory_backend_info = {
|
|||||||
.class_init = host_memory_backend_class_init,
|
.class_init = host_memory_backend_class_init,
|
||||||
.instance_size = sizeof(HostMemoryBackend),
|
.instance_size = sizeof(HostMemoryBackend),
|
||||||
.instance_init = host_memory_backend_init,
|
.instance_init = host_memory_backend_init,
|
||||||
|
.instance_finalize = host_memory_backend_finalize,
|
||||||
.interfaces = (InterfaceInfo[]) {
|
.interfaces = (InterfaceInfo[]) {
|
||||||
{ TYPE_USER_CREATABLE },
|
{ TYPE_USER_CREATABLE },
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
13
balloon.c
13
balloon.c
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/atomic.h"
|
|
||||||
#include "exec/cpu-common.h"
|
#include "exec/cpu-common.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "sysemu/balloon.h"
|
#include "sysemu/balloon.h"
|
||||||
@@ -38,22 +37,16 @@
|
|||||||
static QEMUBalloonEvent *balloon_event_fn;
|
static QEMUBalloonEvent *balloon_event_fn;
|
||||||
static QEMUBalloonStatus *balloon_stat_fn;
|
static QEMUBalloonStatus *balloon_stat_fn;
|
||||||
static void *balloon_opaque;
|
static void *balloon_opaque;
|
||||||
static int balloon_inhibit_count;
|
static bool balloon_inhibited;
|
||||||
|
|
||||||
bool qemu_balloon_is_inhibited(void)
|
bool qemu_balloon_is_inhibited(void)
|
||||||
{
|
{
|
||||||
return atomic_read(&balloon_inhibit_count) > 0;
|
return balloon_inhibited;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_balloon_inhibit(bool state)
|
void qemu_balloon_inhibit(bool state)
|
||||||
{
|
{
|
||||||
if (state) {
|
balloon_inhibited = state;
|
||||||
atomic_inc(&balloon_inhibit_count);
|
|
||||||
} else {
|
|
||||||
atomic_dec(&balloon_inhibit_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(atomic_read(&balloon_inhibit_count) >= 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool have_balloon(Error **errp)
|
static bool have_balloon(Error **errp)
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
block-obj-y += raw-format.o vmdk.o vpc.o
|
block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
|
||||||
block-obj-$(CONFIG_QCOW1) += qcow.o
|
|
||||||
block-obj-$(CONFIG_VDI) += vdi.o
|
|
||||||
block-obj-$(CONFIG_CLOOP) += cloop.o
|
|
||||||
block-obj-$(CONFIG_BOCHS) += bochs.o
|
|
||||||
block-obj-$(CONFIG_VVFAT) += vvfat.o
|
|
||||||
block-obj-$(CONFIG_DMG) += dmg.o
|
|
||||||
|
|
||||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
|
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
|
||||||
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||||
block-obj-$(CONFIG_QED) += qed-check.o
|
block-obj-y += qed-check.o
|
||||||
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
||||||
block-obj-y += quorum.o
|
block-obj-y += quorum.o
|
||||||
block-obj-y += blkdebug.o blkverify.o blkreplay.o
|
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
|
||||||
block-obj-$(CONFIG_PARALLELS) += parallels.o
|
|
||||||
block-obj-y += blklogwrites.o
|
|
||||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||||
@@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
|
|||||||
block-obj-y += throttle-groups.o
|
block-obj-y += throttle-groups.o
|
||||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||||
|
|
||||||
block-obj-y += nbd.o nbd-client.o
|
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||||
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
|
|
||||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||||
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
||||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||||
@@ -36,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o
|
|||||||
block-obj-y += write-threshold.o
|
block-obj-y += write-threshold.o
|
||||||
block-obj-y += backup.o
|
block-obj-y += backup.o
|
||||||
block-obj-$(CONFIG_REPLICATION) += replication.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
|
block-obj-y += crypto.o
|
||||||
|
|
||||||
@@ -54,8 +44,7 @@ gluster.o-libs := $(GLUSTERFS_LIBS)
|
|||||||
vxhs.o-libs := $(VXHS_LIBS)
|
vxhs.o-libs := $(VXHS_LIBS)
|
||||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||||
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
|
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
|
||||||
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
|
|
||||||
dmg-bz2.o-libs := $(BZIP2_LIBS)
|
dmg-bz2.o-libs := $(BZIP2_LIBS)
|
||||||
qcow.o-libs := -lz
|
qcow.o-libs := -lz
|
||||||
linux-aio.o-libs := -laio
|
linux-aio.o-libs := -laio
|
||||||
|
|||||||
@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
|||||||
cookie->type = type;
|
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,
|
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||||
bool failed)
|
bool failed)
|
||||||
{
|
{
|
||||||
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
|||||||
stats->nr_ops[cookie->type]++;
|
stats->nr_ops[cookie->type]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
|
|
||||||
latency_ns);
|
|
||||||
|
|
||||||
if (!failed || stats->account_failed) {
|
if (!failed || stats->account_failed) {
|
||||||
stats->total_time_ns[cookie->type] += latency_ns;
|
stats->total_time_ns[cookie->type] += latency_ns;
|
||||||
stats->last_access_time_ns = time_ns;
|
stats->last_access_time_ns = time_ns;
|
||||||
|
|||||||
323
block/backup.c
323
block/backup.c
@@ -27,6 +27,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||||
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
|
|
||||||
typedef struct BackupBlockJob {
|
typedef struct BackupBlockJob {
|
||||||
BlockJob common;
|
BlockJob common;
|
||||||
@@ -34,10 +35,10 @@ typedef struct BackupBlockJob {
|
|||||||
/* bitmap for sync=incremental */
|
/* bitmap for sync=incremental */
|
||||||
BdrvDirtyBitmap *sync_bitmap;
|
BdrvDirtyBitmap *sync_bitmap;
|
||||||
MirrorSyncMode sync_mode;
|
MirrorSyncMode sync_mode;
|
||||||
|
RateLimit limit;
|
||||||
BlockdevOnError on_source_error;
|
BlockdevOnError on_source_error;
|
||||||
BlockdevOnError on_target_error;
|
BlockdevOnError on_target_error;
|
||||||
CoRwlock flush_rwlock;
|
CoRwlock flush_rwlock;
|
||||||
uint64_t len;
|
|
||||||
uint64_t bytes_read;
|
uint64_t bytes_read;
|
||||||
int64_t cluster_size;
|
int64_t cluster_size;
|
||||||
bool compress;
|
bool compress;
|
||||||
@@ -45,14 +46,8 @@ typedef struct BackupBlockJob {
|
|||||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||||
|
|
||||||
HBitmap *copy_bitmap;
|
HBitmap *copy_bitmap;
|
||||||
bool use_copy_range;
|
|
||||||
int64_t copy_range_size;
|
|
||||||
|
|
||||||
bool serialize_target_writes;
|
|
||||||
} BackupBlockJob;
|
} BackupBlockJob;
|
||||||
|
|
||||||
static const BlockJobDriver backup_job_driver;
|
|
||||||
|
|
||||||
/* See if in-flight requests overlap and wait for them to complete */
|
/* See if in-flight requests overlap and wait for them to complete */
|
||||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||||
int64_t start,
|
int64_t start,
|
||||||
@@ -90,104 +85,19 @@ static void cow_request_end(CowRequest *req)
|
|||||||
qemu_co_queue_restart_all(&req->wait_queue);
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy range to target with a bounce buffer and return the bytes copied. If
|
|
||||||
* error occurred, return a negative error number */
|
|
||||||
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
|
||||||
int64_t start,
|
|
||||||
int64_t end,
|
|
||||||
bool is_write_notifier,
|
|
||||||
bool *error_is_read,
|
|
||||||
void **bounce_buffer)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct iovec iov;
|
|
||||||
QEMUIOVector qiov;
|
|
||||||
BlockBackend *blk = job->common.blk;
|
|
||||||
int nbytes;
|
|
||||||
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
|
|
||||||
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
|
|
||||||
|
|
||||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
|
||||||
nbytes = MIN(job->cluster_size, job->len - start);
|
|
||||||
if (!*bounce_buffer) {
|
|
||||||
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
|
||||||
}
|
|
||||||
iov.iov_base = *bounce_buffer;
|
|
||||||
iov.iov_len = nbytes;
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
|
|
||||||
if (ret < 0) {
|
|
||||||
trace_backup_do_cow_read_fail(job, start, ret);
|
|
||||||
if (error_is_read) {
|
|
||||||
*error_is_read = true;
|
|
||||||
}
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemu_iovec_is_zero(&qiov)) {
|
|
||||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
|
||||||
qiov.size, write_flags | BDRV_REQ_MAY_UNMAP);
|
|
||||||
} else {
|
|
||||||
ret = blk_co_pwritev(job->target, start,
|
|
||||||
qiov.size, &qiov, write_flags |
|
|
||||||
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
|
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
trace_backup_do_cow_write_fail(job, start, ret);
|
|
||||||
if (error_is_read) {
|
|
||||||
*error_is_read = false;
|
|
||||||
}
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nbytes;
|
|
||||||
fail:
|
|
||||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy range to target and return the bytes copied. If error occurred, return a
|
|
||||||
* negative error number. */
|
|
||||||
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
|
|
||||||
int64_t start,
|
|
||||||
int64_t end,
|
|
||||||
bool is_write_notifier)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
int nr_clusters;
|
|
||||||
BlockBackend *blk = job->common.blk;
|
|
||||||
int nbytes;
|
|
||||||
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
|
|
||||||
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
|
|
||||||
|
|
||||||
assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
|
|
||||||
nbytes = MIN(job->copy_range_size, end - start);
|
|
||||||
nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
|
|
||||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size,
|
|
||||||
nr_clusters);
|
|
||||||
ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
|
|
||||||
read_flags, write_flags);
|
|
||||||
if (ret < 0) {
|
|
||||||
trace_backup_do_cow_copy_range_fail(job, start, ret);
|
|
||||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size,
|
|
||||||
nr_clusters);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nbytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||||
int64_t offset, uint64_t bytes,
|
int64_t offset, uint64_t bytes,
|
||||||
bool *error_is_read,
|
bool *error_is_read,
|
||||||
bool is_write_notifier)
|
bool is_write_notifier)
|
||||||
{
|
{
|
||||||
|
BlockBackend *blk = job->common.blk;
|
||||||
CowRequest cow_request;
|
CowRequest cow_request;
|
||||||
|
struct iovec iov;
|
||||||
|
QEMUIOVector bounce_qiov;
|
||||||
|
void *bounce_buffer = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int64_t start, end; /* bytes */
|
int64_t start, end; /* bytes */
|
||||||
void *bounce_buffer = NULL;
|
int n; /* bytes */
|
||||||
|
|
||||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||||
|
|
||||||
@@ -199,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
|||||||
wait_for_overlapping_requests(job, start, end);
|
wait_for_overlapping_requests(job, start, end);
|
||||||
cow_request_begin(&cow_request, job, start, end);
|
cow_request_begin(&cow_request, job, start, end);
|
||||||
|
|
||||||
while (start < end) {
|
for (; start < end; start += job->cluster_size) {
|
||||||
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
||||||
trace_backup_do_cow_skip(job, start);
|
trace_backup_do_cow_skip(job, start);
|
||||||
start += job->cluster_size;
|
|
||||||
continue; /* already copied */
|
continue; /* already copied */
|
||||||
}
|
}
|
||||||
|
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
||||||
|
|
||||||
trace_backup_do_cow_process(job, start);
|
trace_backup_do_cow_process(job, start);
|
||||||
|
|
||||||
if (job->use_copy_range) {
|
n = MIN(job->cluster_size, job->common.len - start);
|
||||||
ret = backup_cow_with_offload(job, start, end, is_write_notifier);
|
|
||||||
|
if (!bounce_buffer) {
|
||||||
|
bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||||
|
}
|
||||||
|
iov.iov_base = bounce_buffer;
|
||||||
|
iov.iov_len = n;
|
||||||
|
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
|
||||||
|
|
||||||
|
ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
|
||||||
|
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
job->use_copy_range = false;
|
trace_backup_do_cow_read_fail(job, start, ret);
|
||||||
|
if (error_is_read) {
|
||||||
|
*error_is_read = true;
|
||||||
}
|
}
|
||||||
|
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
if (!job->use_copy_range) {
|
|
||||||
ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
|
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||||
error_is_read, &bounce_buffer);
|
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||||
|
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
|
||||||
|
} else {
|
||||||
|
ret = blk_co_pwritev(job->target, start,
|
||||||
|
bounce_qiov.size, &bounce_qiov,
|
||||||
|
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
trace_backup_do_cow_write_fail(job, start, ret);
|
||||||
|
if (error_is_read) {
|
||||||
|
*error_is_read = false;
|
||||||
|
}
|
||||||
|
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||||
* offset field is an opaque progress value, it is not a disk offset.
|
* offset field is an opaque progress value, it is not a disk offset.
|
||||||
*/
|
*/
|
||||||
start += ret;
|
job->bytes_read += n;
|
||||||
job->bytes_read += ret;
|
job->common.offset += n;
|
||||||
job_progress_update(&job->common.job, ret);
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
if (bounce_buffer) {
|
if (bounce_buffer) {
|
||||||
qemu_vfree(bounce_buffer);
|
qemu_vfree(bounce_buffer);
|
||||||
}
|
}
|
||||||
@@ -258,12 +190,23 @@ static int coroutine_fn backup_before_write_notify(
|
|||||||
return backup_do_cow(job, req->offset, req->bytes, NULL, true);
|
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)
|
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm;
|
BdrvDirtyBitmap *bm;
|
||||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
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. */
|
/* Merge the successor back into the parent, delete nothing. */
|
||||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||||
assert(bm);
|
assert(bm);
|
||||||
@@ -274,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) {
|
if (s->sync_bitmap) {
|
||||||
backup_cleanup_sync_bitmap(s, 0);
|
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) {
|
if (s->sync_bitmap) {
|
||||||
backup_cleanup_sync_bitmap(s, -1);
|
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);
|
assert(s->target);
|
||||||
blk_unref(s->target);
|
blk_unref(s->target);
|
||||||
s->target = NULL;
|
s->target = NULL;
|
||||||
@@ -310,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
|||||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||||
int64_t len;
|
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) {
|
if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
|
||||||
error_setg(errp, "The backup job only supports block checkpoint in"
|
error_setg(errp, "The backup job only supports block checkpoint in"
|
||||||
@@ -318,7 +261,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
|||||||
return;
|
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);
|
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +271,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
|||||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||||
int64_t start, end;
|
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);
|
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||||
@@ -341,7 +284,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job,
|
|||||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||||
int64_t start, end;
|
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);
|
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||||
@@ -380,21 +323,37 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ret;
|
||||||
|
} BackupCompleteData;
|
||||||
|
|
||||||
|
static void backup_complete(BlockJob *job, void *opaque)
|
||||||
|
{
|
||||||
|
BackupCompleteData *data = opaque;
|
||||||
|
|
||||||
|
block_job_completed(job, data->ret);
|
||||||
|
g_free(data);
|
||||||
|
}
|
||||||
|
|
||||||
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||||
{
|
{
|
||||||
uint64_t delay_ns;
|
if (block_job_is_cancelled(&job->common)) {
|
||||||
|
|
||||||
if (job_is_cancelled(&job->common.job)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
|
/* we need to yield so that bdrv_drain_all() returns.
|
||||||
* return. Without a yield, the VM would not reboot. */
|
* (without, VM does not reboot)
|
||||||
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
|
*/
|
||||||
|
if (job->common.speed) {
|
||||||
|
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
|
||||||
|
job->bytes_read);
|
||||||
job->bytes_read = 0;
|
job->bytes_read = 0;
|
||||||
job_sleep_ns(&job->common.job, delay_ns);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +368,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
HBitmapIter hbi;
|
HBitmapIter hbi;
|
||||||
|
|
||||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||||
while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) {
|
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||||
do {
|
do {
|
||||||
if (yield_and_check(job)) {
|
if (yield_and_check(job)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -461,66 +420,64 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
|||||||
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
|
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO job_progress_set_remaining() would make more sense */
|
job->common.offset = job->common.len -
|
||||||
job_progress_update(&job->common.job,
|
hbitmap_count(job->copy_bitmap) * job->cluster_size;
|
||||||
job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
|
|
||||||
|
|
||||||
bdrv_dirty_iter_free(dbi);
|
bdrv_dirty_iter_free(dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn backup_run(Job *job, Error **errp)
|
static void coroutine_fn backup_run(void *opaque)
|
||||||
{
|
{
|
||||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
BackupBlockJob *job = opaque;
|
||||||
BlockDriverState *bs = blk_bs(s->common.blk);
|
BackupCompleteData *data;
|
||||||
|
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||||
int64_t offset, nb_clusters;
|
int64_t offset, nb_clusters;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
QLIST_INIT(&s->inflight_reqs);
|
QLIST_INIT(&job->inflight_reqs);
|
||||||
qemu_co_rwlock_init(&s->flush_rwlock);
|
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||||
|
|
||||||
nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size);
|
nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
job_progress_set_remaining(job, s->len);
|
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||||
|
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||||
s->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
backup_incremental_init_copy_bitmap(job);
|
||||||
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
|
||||||
backup_incremental_init_copy_bitmap(s);
|
|
||||||
} else {
|
} else {
|
||||||
hbitmap_set(s->copy_bitmap, 0, nb_clusters);
|
hbitmap_set(job->copy_bitmap, 0, nb_clusters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
s->before_write.notify = backup_before_write_notify;
|
job->before_write.notify = backup_before_write_notify;
|
||||||
bdrv_add_before_write_notifier(bs, &s->before_write);
|
bdrv_add_before_write_notifier(bs, &job->before_write);
|
||||||
|
|
||||||
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||||
* This does not actually require them to be copied. */
|
* This does not actually require them to be copied. */
|
||||||
while (!job_is_cancelled(job)) {
|
while (!block_job_is_cancelled(&job->common)) {
|
||||||
/* Yield until the job is cancelled. We just let our before_write
|
/* Yield until the job is cancelled. We just let our before_write
|
||||||
* notify callback service CoW requests. */
|
* notify callback service CoW requests. */
|
||||||
job_yield(job);
|
block_job_yield(&job->common);
|
||||||
}
|
}
|
||||||
} else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||||
ret = backup_run_incremental(s);
|
ret = backup_run_incremental(job);
|
||||||
} else {
|
} else {
|
||||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||||
for (offset = 0; offset < s->len;
|
for (offset = 0; offset < job->common.len;
|
||||||
offset += s->cluster_size) {
|
offset += job->cluster_size) {
|
||||||
bool error_is_read;
|
bool error_is_read;
|
||||||
int alloced = 0;
|
int alloced = 0;
|
||||||
|
|
||||||
if (yield_and_check(s)) {
|
if (yield_and_check(job)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||||
int i;
|
int i;
|
||||||
int64_t n;
|
int64_t n;
|
||||||
|
|
||||||
/* Check to see if these blocks are already in the
|
/* Check to see if these blocks are already in the
|
||||||
* backing file. */
|
* backing file. */
|
||||||
|
|
||||||
for (i = 0; i < s->cluster_size;) {
|
for (i = 0; i < job->cluster_size;) {
|
||||||
/* bdrv_is_allocated() only returns true/false based
|
/* bdrv_is_allocated() only returns true/false based
|
||||||
* on the first set of sectors it comes across that
|
* on the first set of sectors it comes across that
|
||||||
* are are all in the same state.
|
* are are all in the same state.
|
||||||
@@ -529,7 +486,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
|
|||||||
* needed but at some point that is always the case. */
|
* needed but at some point that is always the case. */
|
||||||
alloced =
|
alloced =
|
||||||
bdrv_is_allocated(bs, offset + i,
|
bdrv_is_allocated(bs, offset + i,
|
||||||
s->cluster_size - i, &n);
|
job->cluster_size - i, &n);
|
||||||
i += n;
|
i += n;
|
||||||
|
|
||||||
if (alloced || n == 0) {
|
if (alloced || n == 0) {
|
||||||
@@ -547,45 +504,43 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
|
|||||||
if (alloced < 0) {
|
if (alloced < 0) {
|
||||||
ret = alloced;
|
ret = alloced;
|
||||||
} else {
|
} else {
|
||||||
ret = backup_do_cow(s, offset, s->cluster_size,
|
ret = backup_do_cow(job, offset, job->cluster_size,
|
||||||
&error_is_read, false);
|
&error_is_read, false);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Depending on error action, fail now or retry cluster */
|
/* Depending on error action, fail now or retry cluster */
|
||||||
BlockErrorAction action =
|
BlockErrorAction action =
|
||||||
backup_error_action(s, error_is_read, -ret);
|
backup_error_action(job, error_is_read, -ret);
|
||||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
offset -= s->cluster_size;
|
offset -= job->cluster_size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifier_with_return_remove(&s->before_write);
|
notifier_with_return_remove(&job->before_write);
|
||||||
|
|
||||||
/* wait until pending backup_do_cow() calls have completed */
|
/* wait until pending backup_do_cow() calls have completed */
|
||||||
qemu_co_rwlock_wrlock(&s->flush_rwlock);
|
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||||
qemu_co_rwlock_unlock(&s->flush_rwlock);
|
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||||
hbitmap_free(s->copy_bitmap);
|
hbitmap_free(job->copy_bitmap);
|
||||||
|
|
||||||
return ret;
|
data = g_malloc(sizeof(*data));
|
||||||
|
data->ret = ret;
|
||||||
|
block_job_defer_to_main_loop(&job->common, backup_complete, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const BlockJobDriver backup_job_driver = {
|
static const BlockJobDriver backup_job_driver = {
|
||||||
.job_driver = {
|
|
||||||
.instance_size = sizeof(BackupBlockJob),
|
.instance_size = sizeof(BackupBlockJob),
|
||||||
.job_type = JOB_TYPE_BACKUP,
|
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||||
.free = block_job_free,
|
.start = backup_run,
|
||||||
.user_resume = block_job_user_resume,
|
.set_speed = backup_set_speed,
|
||||||
.drain = block_job_drain,
|
|
||||||
.run = backup_run,
|
|
||||||
.commit = backup_commit,
|
.commit = backup_commit,
|
||||||
.abort = backup_abort,
|
.abort = backup_abort,
|
||||||
.clean = backup_clean,
|
.clean = backup_clean,
|
||||||
},
|
|
||||||
.attached_aio_context = backup_attached_aio_context,
|
.attached_aio_context = backup_attached_aio_context,
|
||||||
.drain = backup_drain,
|
.drain = backup_drain,
|
||||||
};
|
};
|
||||||
@@ -598,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
BlockdevOnError on_target_error,
|
BlockdevOnError on_target_error,
|
||||||
int creation_flags,
|
int creation_flags,
|
||||||
BlockCompletionFunc *cb, void *opaque,
|
BlockCompletionFunc *cb, void *opaque,
|
||||||
JobTxn *txn, Error **errp)
|
BlockJobTxn *txn, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t len;
|
int64_t len;
|
||||||
BlockDriverInfo bdi;
|
BlockDriverInfo bdi;
|
||||||
@@ -665,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* job->len is fixed, so we can't allow resize */
|
/* job->common.len is fixed, so we can't allow resize */
|
||||||
job = block_job_create(job_id, &backup_job_driver, txn, bs,
|
job = block_job_create(job_id, &backup_job_driver, bs,
|
||||||
BLK_PERM_CONSISTENT_READ,
|
BLK_PERM_CONSISTENT_READ,
|
||||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||||
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
|
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
|
||||||
@@ -691,9 +646,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
sync_bitmap : NULL;
|
sync_bitmap : NULL;
|
||||||
job->compress = compress;
|
job->compress = compress;
|
||||||
|
|
||||||
/* Detect image-fleecing (and similar) schemes */
|
|
||||||
job->serialize_target_writes = bdrv_chain_contains(target, bs);
|
|
||||||
|
|
||||||
/* If there is no backing file on the target, we cannot rely on COW if our
|
/* If there is no backing file on the target, we cannot rely on COW if our
|
||||||
* backup cluster size is smaller than the target cluster size. Even for
|
* backup cluster size is smaller than the target cluster size. Even for
|
||||||
* targets with a backing file, try to avoid COW if possible. */
|
* targets with a backing file, try to avoid COW if possible. */
|
||||||
@@ -720,17 +672,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
} else {
|
} else {
|
||||||
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||||
}
|
}
|
||||||
job->use_copy_range = true;
|
|
||||||
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
|
|
||||||
blk_get_max_transfer(job->target));
|
|
||||||
job->copy_range_size = MAX(job->cluster_size,
|
|
||||||
QEMU_ALIGN_UP(job->copy_range_size,
|
|
||||||
job->cluster_size));
|
|
||||||
|
|
||||||
/* Required permissions are already taken with target's blk_new() */
|
/* Required permissions are already taken with target's blk_new() */
|
||||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
job->len = len;
|
job->common.len = len;
|
||||||
|
block_job_txn_add_job(txn, &job->common);
|
||||||
|
|
||||||
return &job->common;
|
return &job->common;
|
||||||
|
|
||||||
@@ -739,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
|
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
|
||||||
}
|
}
|
||||||
if (job) {
|
if (job) {
|
||||||
backup_clean(&job->common.job);
|
backup_clean(&job->common);
|
||||||
job_early_fail(&job->common.job);
|
block_job_early_fail(&job->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
|||||||
|
|
||||||
if (c != filename) {
|
if (c != filename) {
|
||||||
QString *config_path;
|
QString *config_path;
|
||||||
config_path = qstring_from_substr(filename, 0, c - filename);
|
config_path = qstring_from_substr(filename, 0, c - filename - 1);
|
||||||
qdict_put(options, "config", config_path);
|
qdict_put(options, "config", config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
bs->file->bs->supported_write_flags;
|
||||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
bs->file->bs->supported_zero_flags;
|
||||||
bs->file->bs->supported_zero_flags);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
|
||||||
/* Set alignment overrides */
|
/* Set alignment overrides */
|
||||||
@@ -625,7 +624,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
|
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
|
||||||
@@ -846,12 +845,13 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
opts = qdict_new();
|
opts = qdict_new();
|
||||||
qdict_put_str(opts, "driver", "blkdebug");
|
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)) {
|
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||||
qdict_put_obj(opts, qdict_entry_key(e),
|
qobject_incref(qdict_entry_value(e));
|
||||||
qobject_ref(qdict_entry_value(e)));
|
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,550 +0,0 @@
|
|||||||
/*
|
|
||||||
* Write logging blk driver based on blkverify and blkdebug.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
|
|
||||||
* Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
|
|
||||||
* Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
|
|
||||||
*
|
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
||||||
* See the COPYING file in the top-level directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
|
|
||||||
#include "block/block_int.h"
|
|
||||||
#include "qapi/qmp/qdict.h"
|
|
||||||
#include "qapi/qmp/qstring.h"
|
|
||||||
#include "qemu/cutils.h"
|
|
||||||
#include "qemu/option.h"
|
|
||||||
|
|
||||||
/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
|
|
||||||
|
|
||||||
#define LOG_FLUSH_FLAG (1 << 0)
|
|
||||||
#define LOG_FUA_FLAG (1 << 1)
|
|
||||||
#define LOG_DISCARD_FLAG (1 << 2)
|
|
||||||
#define LOG_MARK_FLAG (1 << 3)
|
|
||||||
#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
|
|
||||||
| LOG_FUA_FLAG \
|
|
||||||
| LOG_DISCARD_FLAG \
|
|
||||||
| LOG_MARK_FLAG)
|
|
||||||
|
|
||||||
#define WRITE_LOG_VERSION 1ULL
|
|
||||||
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
|
|
||||||
|
|
||||||
/* All fields are little-endian. */
|
|
||||||
struct log_write_super {
|
|
||||||
uint64_t magic;
|
|
||||||
uint64_t version;
|
|
||||||
uint64_t nr_entries;
|
|
||||||
uint32_t sectorsize;
|
|
||||||
} QEMU_PACKED;
|
|
||||||
|
|
||||||
struct log_write_entry {
|
|
||||||
uint64_t sector;
|
|
||||||
uint64_t nr_sectors;
|
|
||||||
uint64_t flags;
|
|
||||||
uint64_t data_len;
|
|
||||||
} QEMU_PACKED;
|
|
||||||
|
|
||||||
/* End of disk format structures. */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
BdrvChild *log_file;
|
|
||||||
uint32_t sectorsize;
|
|
||||||
uint32_t sectorbits;
|
|
||||||
uint64_t cur_log_sector;
|
|
||||||
uint64_t nr_entries;
|
|
||||||
uint64_t update_interval;
|
|
||||||
} BDRVBlkLogWritesState;
|
|
||||||
|
|
||||||
static QemuOptsList runtime_opts = {
|
|
||||||
.name = "blklogwrites",
|
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
|
||||||
.desc = {
|
|
||||||
{
|
|
||||||
.name = "log-append",
|
|
||||||
.type = QEMU_OPT_BOOL,
|
|
||||||
.help = "Append to an existing log",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "log-sector-size",
|
|
||||||
.type = QEMU_OPT_SIZE,
|
|
||||||
.help = "Log sector size",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "log-super-update-interval",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "Log superblock update interval (# of write requests)",
|
|
||||||
},
|
|
||||||
{ /* end of list */ }
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline uint32_t blk_log_writes_log2(uint32_t value)
|
|
||||||
{
|
|
||||||
assert(value > 0);
|
|
||||||
return 31 - clz32(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
|
|
||||||
{
|
|
||||||
return is_power_of_2(sector_size) &&
|
|
||||||
sector_size >= sizeof(struct log_write_super) &&
|
|
||||||
sector_size >= sizeof(struct log_write_entry) &&
|
|
||||||
sector_size < (1ull << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
|
|
||||||
uint32_t sector_size,
|
|
||||||
uint64_t nr_entries,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
uint64_t cur_sector = 1;
|
|
||||||
uint64_t cur_idx = 0;
|
|
||||||
uint32_t sector_bits = blk_log_writes_log2(sector_size);
|
|
||||||
struct log_write_entry cur_entry;
|
|
||||||
|
|
||||||
while (cur_idx < nr_entries) {
|
|
||||||
int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
|
|
||||||
sizeof(cur_entry));
|
|
||||||
if (read_ret < 0) {
|
|
||||||
error_setg_errno(errp, -read_ret,
|
|
||||||
"Failed to read log entry %"PRIu64, cur_idx);
|
|
||||||
return (uint64_t)-1ull;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
|
|
||||||
error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
|
|
||||||
le64_to_cpu(cur_entry.flags), cur_idx);
|
|
||||||
return (uint64_t)-1ull;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Account for the sector of the entry itself */
|
|
||||||
++cur_sector;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Account for the data of the write.
|
|
||||||
* For discards, this data is not present.
|
|
||||||
*/
|
|
||||||
if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
|
|
||||||
cur_sector += le64_to_cpu(cur_entry.nr_sectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
++cur_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cur_sector;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
BDRVBlkLogWritesState *s = bs->opaque;
|
|
||||||
QemuOpts *opts;
|
|
||||||
Error *local_err = NULL;
|
|
||||||
int ret;
|
|
||||||
uint64_t log_sector_size;
|
|
||||||
bool log_append;
|
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open the file */
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
|
||||||
&local_err);
|
|
||||||
if (local_err) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open the log file */
|
|
||||||
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
|
|
||||||
&local_err);
|
|
||||||
if (local_err) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_append = qemu_opt_get_bool(opts, "log-append", false);
|
|
||||||
|
|
||||||
if (log_append) {
|
|
||||||
struct log_write_super log_sb = { 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
if (qemu_opt_find(opts, "log-sector-size")) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_setg(errp, "log-append and log-sector-size are mutually "
|
|
||||||
"exclusive");
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read log superblock or fake one for an empty log */
|
|
||||||
if (!bdrv_getlength(s->log_file->bs)) {
|
|
||||||
log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
|
|
||||||
log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
|
|
||||||
log_sb.nr_entries = cpu_to_le64(0);
|
|
||||||
log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
|
|
||||||
} else {
|
|
||||||
ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
|
|
||||||
if (ret < 0) {
|
|
||||||
error_setg_errno(errp, -ret, "Could not read log superblock");
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_setg(errp, "Invalid log superblock magic");
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_setg(errp, "Unsupported log version %"PRIu64,
|
|
||||||
le64_to_cpu(log_sb.version));
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_sector_size = le32_to_cpu(log_sb.sectorsize);
|
|
||||||
s->cur_log_sector = 1;
|
|
||||||
s->nr_entries = 0;
|
|
||||||
|
|
||||||
if (blk_log_writes_sector_size_valid(log_sector_size)) {
|
|
||||||
s->cur_log_sector =
|
|
||||||
blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
|
|
||||||
le64_to_cpu(log_sb.nr_entries), &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->nr_entries = le64_to_cpu(log_sb.nr_entries);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
|
|
||||||
BDRV_SECTOR_SIZE);
|
|
||||||
s->cur_log_sector = 1;
|
|
||||||
s->nr_entries = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blk_log_writes_sector_size_valid(log_sector_size)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->sectorsize = log_sector_size;
|
|
||||||
s->sectorbits = blk_log_writes_log2(log_sector_size);
|
|
||||||
s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
|
|
||||||
4096);
|
|
||||||
if (!s->update_interval) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
error_setg(errp, "Invalid log superblock update interval %"PRIu64,
|
|
||||||
s->update_interval);
|
|
||||||
goto fail_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
fail_log:
|
|
||||||
if (ret < 0) {
|
|
||||||
bdrv_unref_child(bs, s->log_file);
|
|
||||||
s->log_file = NULL;
|
|
||||||
}
|
|
||||||
fail:
|
|
||||||
if (ret < 0) {
|
|
||||||
bdrv_unref_child(bs, bs->file);
|
|
||||||
bs->file = NULL;
|
|
||||||
}
|
|
||||||
qemu_opts_del(opts);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blk_log_writes_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BDRVBlkLogWritesState *s = bs->opaque;
|
|
||||||
|
|
||||||
bdrv_unref_child(bs, s->log_file);
|
|
||||||
s->log_file = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t blk_log_writes_getlength(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
return bdrv_getlength(bs->file->bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blk_log_writes_refresh_filename(BlockDriverState *bs,
|
|
||||||
QDict *options)
|
|
||||||
{
|
|
||||||
BDRVBlkLogWritesState *s = bs->opaque;
|
|
||||||
|
|
||||||
/* bs->file->bs has already been refreshed */
|
|
||||||
bdrv_refresh_filename(s->log_file->bs);
|
|
||||||
|
|
||||||
if (bs->file->bs->full_open_options
|
|
||||||
&& s->log_file->bs->full_open_options)
|
|
||||||
{
|
|
||||||
QDict *opts = qdict_new();
|
|
||||||
qdict_put_str(opts, "driver", "blklogwrites");
|
|
||||||
|
|
||||||
qobject_ref(bs->file->bs->full_open_options);
|
|
||||||
qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
|
|
||||||
qobject_ref(s->log_file->bs->full_open_options);
|
|
||||||
qdict_put_obj(opts, "log",
|
|
||||||
QOBJECT(s->log_file->bs->full_open_options));
|
|
||||||
qdict_put_int(opts, "log-sector-size", s->sectorsize);
|
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
|
|
||||||
const BdrvChildRole *role,
|
|
||||||
BlockReopenQueue *ro_q,
|
|
||||||
uint64_t perm, uint64_t shrd,
|
|
||||||
uint64_t *nperm, uint64_t *nshrd)
|
|
||||||
{
|
|
||||||
if (!c) {
|
|
||||||
*nperm = perm & DEFAULT_PERM_PASSTHROUGH;
|
|
||||||
*nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(c->name, "log")) {
|
|
||||||
bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
|
|
||||||
} else {
|
|
||||||
bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
|
|
||||||
{
|
|
||||||
BDRVBlkLogWritesState *s = bs->opaque;
|
|
||||||
bs->bl.request_alignment = s->sectorsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
|
||||||
QEMUIOVector *qiov, int flags)
|
|
||||||
{
|
|
||||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct BlkLogWritesFileReq {
|
|
||||||
BlockDriverState *bs;
|
|
||||||
uint64_t offset;
|
|
||||||
uint64_t bytes;
|
|
||||||
int file_flags;
|
|
||||||
QEMUIOVector *qiov;
|
|
||||||
int (*func)(struct BlkLogWritesFileReq *r);
|
|
||||||
int file_ret;
|
|
||||||
} BlkLogWritesFileReq;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
BlockDriverState *bs;
|
|
||||||
QEMUIOVector *qiov;
|
|
||||||
struct log_write_entry entry;
|
|
||||||
uint64_t zero_size;
|
|
||||||
int log_ret;
|
|
||||||
} BlkLogWritesLogReq;
|
|
||||||
|
|
||||||
static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
|
|
||||||
{
|
|
||||||
BDRVBlkLogWritesState *s = lr->bs->opaque;
|
|
||||||
uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
|
|
||||||
|
|
||||||
s->nr_entries++;
|
|
||||||
s->cur_log_sector +=
|
|
||||||
ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
|
|
||||||
|
|
||||||
lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
|
|
||||||
lr->qiov, 0);
|
|
||||||
|
|
||||||
/* Logging for the "write zeroes" operation */
|
|
||||||
if (lr->log_ret == 0 && lr->zero_size) {
|
|
||||||
cur_log_offset = s->cur_log_sector << s->sectorbits;
|
|
||||||
s->cur_log_sector +=
|
|
||||||
ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
|
|
||||||
|
|
||||||
lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
|
|
||||||
lr->zero_size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update super block on flush or every update interval */
|
|
||||||
if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
|
|
||||||
|| (s->nr_entries % s->update_interval == 0)))
|
|
||||||
{
|
|
||||||
struct log_write_super super = {
|
|
||||||
.magic = cpu_to_le64(WRITE_LOG_MAGIC),
|
|
||||||
.version = cpu_to_le64(WRITE_LOG_VERSION),
|
|
||||||
.nr_entries = cpu_to_le64(s->nr_entries),
|
|
||||||
.sectorsize = cpu_to_le32(s->sectorsize),
|
|
||||||
};
|
|
||||||
void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
|
|
||||||
QEMUIOVector qiov;
|
|
||||||
|
|
||||||
qemu_iovec_init(&qiov, 2);
|
|
||||||
qemu_iovec_add(&qiov, &super, sizeof(super));
|
|
||||||
qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
|
|
||||||
|
|
||||||
lr->log_ret =
|
|
||||||
bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
|
|
||||||
if (lr->log_ret == 0) {
|
|
||||||
lr->log_ret = bdrv_co_flush(s->log_file->bs);
|
|
||||||
}
|
|
||||||
qemu_iovec_destroy(&qiov);
|
|
||||||
g_free(zeroes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
|
|
||||||
{
|
|
||||||
fr->file_ret = fr->func(fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
|
||||||
QEMUIOVector *qiov, int flags,
|
|
||||||
int (*file_func)(BlkLogWritesFileReq *r),
|
|
||||||
uint64_t entry_flags, bool is_zero_write)
|
|
||||||
{
|
|
||||||
QEMUIOVector log_qiov;
|
|
||||||
size_t niov = qiov ? qiov->niov : 0;
|
|
||||||
BDRVBlkLogWritesState *s = bs->opaque;
|
|
||||||
BlkLogWritesFileReq fr = {
|
|
||||||
.bs = bs,
|
|
||||||
.offset = offset,
|
|
||||||
.bytes = bytes,
|
|
||||||
.file_flags = flags,
|
|
||||||
.qiov = qiov,
|
|
||||||
.func = file_func,
|
|
||||||
};
|
|
||||||
BlkLogWritesLogReq lr = {
|
|
||||||
.bs = bs,
|
|
||||||
.qiov = &log_qiov,
|
|
||||||
.entry = {
|
|
||||||
.sector = cpu_to_le64(offset >> s->sectorbits),
|
|
||||||
.nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
|
|
||||||
.flags = cpu_to_le64(entry_flags),
|
|
||||||
.data_len = 0,
|
|
||||||
},
|
|
||||||
.zero_size = is_zero_write ? bytes : 0,
|
|
||||||
};
|
|
||||||
void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
|
|
||||||
|
|
||||||
assert((1 << s->sectorbits) == s->sectorsize);
|
|
||||||
assert(bs->bl.request_alignment == s->sectorsize);
|
|
||||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
|
||||||
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
|
||||||
|
|
||||||
qemu_iovec_init(&log_qiov, niov + 2);
|
|
||||||
qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
|
|
||||||
qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
|
|
||||||
if (qiov) {
|
|
||||||
qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
blk_log_writes_co_do_file(&fr);
|
|
||||||
blk_log_writes_co_do_log(&lr);
|
|
||||||
|
|
||||||
qemu_iovec_destroy(&log_qiov);
|
|
||||||
g_free(zeroes);
|
|
||||||
|
|
||||||
if (lr.log_ret < 0) {
|
|
||||||
return lr.log_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fr.file_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
|
|
||||||
{
|
|
||||||
return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
|
|
||||||
fr->qiov, fr->file_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
|
|
||||||
{
|
|
||||||
return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
|
|
||||||
fr->file_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
|
|
||||||
{
|
|
||||||
return bdrv_co_flush(fr->bs->file->bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
|
|
||||||
{
|
|
||||||
return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
|
||||||
QEMUIOVector *qiov, int flags)
|
|
||||||
{
|
|
||||||
return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
|
|
||||||
blk_log_writes_co_do_file_pwritev, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
|
|
||||||
BdrvRequestFlags flags)
|
|
||||||
{
|
|
||||||
return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
|
|
||||||
blk_log_writes_co_do_file_pwrite_zeroes, 0,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
|
|
||||||
blk_log_writes_co_do_file_flush,
|
|
||||||
LOG_FLUSH_FLAG, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn
|
|
||||||
blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
|
|
||||||
{
|
|
||||||
return blk_log_writes_co_log(bs, offset, count, NULL, 0,
|
|
||||||
blk_log_writes_co_do_file_pdiscard,
|
|
||||||
LOG_DISCARD_FLAG, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BlockDriver bdrv_blk_log_writes = {
|
|
||||||
.format_name = "blklogwrites",
|
|
||||||
.instance_size = sizeof(BDRVBlkLogWritesState),
|
|
||||||
|
|
||||||
.bdrv_open = blk_log_writes_open,
|
|
||||||
.bdrv_close = blk_log_writes_close,
|
|
||||||
.bdrv_getlength = blk_log_writes_getlength,
|
|
||||||
.bdrv_refresh_filename = blk_log_writes_refresh_filename,
|
|
||||||
.bdrv_child_perm = blk_log_writes_child_perm,
|
|
||||||
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
|
|
||||||
|
|
||||||
.bdrv_co_preadv = blk_log_writes_co_preadv,
|
|
||||||
.bdrv_co_pwritev = blk_log_writes_co_pwritev,
|
|
||||||
.bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
|
|
||||||
.bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
|
|
||||||
.bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
|
|
||||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
|
||||||
|
|
||||||
.is_filter = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void bdrv_blk_log_writes_init(void)
|
|
||||||
{
|
|
||||||
bdrv_register(&bdrv_blk_log_writes);
|
|
||||||
}
|
|
||||||
|
|
||||||
block_init(bdrv_blk_log_writes_init);
|
|
||||||
10
block/blkreplay.c
Normal file → Executable file
10
block/blkreplay.c
Normal file → Executable file
@@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
|
||||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void blkreplay_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t blkreplay_getlength(BlockDriverState *bs)
|
static int64_t blkreplay_getlength(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bdrv_getlength(bs->file->bs);
|
return bdrv_getlength(bs->file->bs);
|
||||||
@@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
|
|||||||
int64_t offset, int bytes)
|
int64_t offset, int bytes)
|
||||||
{
|
{
|
||||||
uint64_t reqid = blkreplay_next_id();
|
uint64_t reqid = blkreplay_next_id();
|
||||||
int ret = bdrv_co_pdiscard(bs->file, offset, bytes);
|
int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
|
|
||||||
@@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = {
|
|||||||
.instance_size = 0,
|
.instance_size = 0,
|
||||||
|
|
||||||
.bdrv_open = blkreplay_open,
|
.bdrv_open = blkreplay_open,
|
||||||
|
.bdrv_close = blkreplay_close,
|
||||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||||
.bdrv_getlength = blkreplay_getlength,
|
.bdrv_getlength = blkreplay_getlength,
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Implement option pass-through and set raw.filename here */
|
/* TODO Implement option pass-through and set raw.filename here */
|
||||||
raw_path = qstring_from_substr(filename, 0, c - filename);
|
raw_path = qstring_from_substr(filename, 0, c - filename - 1);
|
||||||
qdict_put(options, "x-raw", raw_path);
|
qdict_put(options, "x-raw", raw_path);
|
||||||
|
|
||||||
/* TODO Allow multi-level nesting and set file.filename here */
|
/* TODO Allow multi-level nesting and set file.filename here */
|
||||||
@@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
|
||||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
@@ -294,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
QDict *opts = qdict_new();
|
QDict *opts = qdict_new();
|
||||||
qdict_put_str(opts, "driver", "blkverify");
|
qdict_put_str(opts, "driver", "blkverify");
|
||||||
|
|
||||||
qdict_put(opts, "raw",
|
QINCREF(bs->file->bs->full_open_options);
|
||||||
qobject_ref(bs->file->bs->full_open_options));
|
qdict_put(opts, "raw", bs->file->bs->full_open_options);
|
||||||
qdict_put(opts, "test",
|
QINCREF(s->test_file->bs->full_open_options);
|
||||||
qobject_ref(s->test_file->bs->full_open_options));
|
qdict_put(opts, "test", s->test_file->bs->full_open_options);
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,6 @@
|
|||||||
|
|
||||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
|
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 {
|
struct BlockBackend {
|
||||||
char *name;
|
char *name;
|
||||||
int refcnt;
|
int refcnt;
|
||||||
@@ -76,7 +69,6 @@ struct BlockBackend {
|
|||||||
bool allow_write_beyond_eof;
|
bool allow_write_beyond_eof;
|
||||||
|
|
||||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||||
QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
|
|
||||||
|
|
||||||
int quiesce_counter;
|
int quiesce_counter;
|
||||||
VMChangeStateEntry *vmsh;
|
VMChangeStateEntry *vmsh;
|
||||||
@@ -88,6 +80,7 @@ struct BlockBackend {
|
|||||||
* Accessed with atomic ops.
|
* Accessed with atomic ops.
|
||||||
*/
|
*/
|
||||||
unsigned int in_flight;
|
unsigned int in_flight;
|
||||||
|
AioWait wait;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct BlockBackendAIOCB {
|
typedef struct BlockBackendAIOCB {
|
||||||
@@ -120,7 +113,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
static void blk_root_drained_begin(BdrvChild *child);
|
static void blk_root_drained_begin(BdrvChild *child);
|
||||||
static bool blk_root_drained_poll(BdrvChild *child);
|
|
||||||
static void blk_root_drained_end(BdrvChild *child);
|
static void blk_root_drained_end(BdrvChild *child);
|
||||||
|
|
||||||
static void blk_root_change_media(BdrvChild *child, bool load);
|
static void blk_root_change_media(BdrvChild *child, bool load);
|
||||||
@@ -255,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child)
|
|||||||
return 0;
|
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 = {
|
static const BdrvChildRole child_root = {
|
||||||
.inherit_options = blk_root_inherit_options,
|
.inherit_options = blk_root_inherit_options,
|
||||||
|
|
||||||
@@ -294,14 +256,10 @@ static const BdrvChildRole child_root = {
|
|||||||
.get_parent_desc = blk_root_get_parent_desc,
|
.get_parent_desc = blk_root_get_parent_desc,
|
||||||
|
|
||||||
.drained_begin = blk_root_drained_begin,
|
.drained_begin = blk_root_drained_begin,
|
||||||
.drained_poll = blk_root_drained_poll,
|
|
||||||
.drained_end = blk_root_drained_end,
|
.drained_end = blk_root_drained_end,
|
||||||
|
|
||||||
.activate = blk_root_activate,
|
.activate = blk_root_activate,
|
||||||
.inactivate = blk_root_inactivate,
|
.inactivate = blk_root_inactivate,
|
||||||
|
|
||||||
.attach = blk_root_attach,
|
|
||||||
.detach = blk_root_detach,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -325,14 +283,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
|||||||
blk->shared_perm = shared_perm;
|
blk->shared_perm = shared_perm;
|
||||||
blk_set_enable_write_cache(blk, true);
|
blk_set_enable_write_cache(blk, true);
|
||||||
|
|
||||||
blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
||||||
blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
|
||||||
|
|
||||||
block_acct_init(&blk->stats);
|
block_acct_init(&blk->stats);
|
||||||
|
|
||||||
notifier_list_init(&blk->remove_bs_notifiers);
|
notifier_list_init(&blk->remove_bs_notifiers);
|
||||||
notifier_list_init(&blk->insert_bs_notifiers);
|
notifier_list_init(&blk->insert_bs_notifiers);
|
||||||
QLIST_INIT(&blk->aio_notifiers);
|
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
|
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
|
||||||
return blk;
|
return blk;
|
||||||
@@ -410,7 +364,6 @@ static void blk_delete(BlockBackend *blk)
|
|||||||
}
|
}
|
||||||
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
||||||
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
||||||
assert(QLIST_EMPTY(&blk->aio_notifiers));
|
|
||||||
QTAILQ_REMOVE(&block_backends, blk, link);
|
QTAILQ_REMOVE(&block_backends, blk, link);
|
||||||
drive_info_del(blk->legacy_dinfo);
|
drive_info_del(blk->legacy_dinfo);
|
||||||
block_acct_cleanup(&blk->stats);
|
block_acct_cleanup(&blk->stats);
|
||||||
@@ -423,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qemu_opts_del(dinfo->opts);
|
qemu_opts_del(dinfo->opts);
|
||||||
|
g_free(dinfo->serial);
|
||||||
g_free(dinfo);
|
g_free(dinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk)
|
|||||||
*/
|
*/
|
||||||
void blk_ref(BlockBackend *blk)
|
void blk_ref(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
assert(blk->refcnt > 0);
|
|
||||||
blk->refcnt++;
|
blk->refcnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,13 +403,7 @@ void blk_unref(BlockBackend *blk)
|
|||||||
{
|
{
|
||||||
if (blk) {
|
if (blk) {
|
||||||
assert(blk->refcnt > 0);
|
assert(blk->refcnt > 0);
|
||||||
if (blk->refcnt > 1) {
|
if (!--blk->refcnt) {
|
||||||
blk->refcnt--;
|
|
||||||
} else {
|
|
||||||
blk_drain(blk);
|
|
||||||
/* blk_drain() cannot resurrect blk, nobody held a reference */
|
|
||||||
assert(blk->refcnt == 1);
|
|
||||||
blk->refcnt = 0;
|
|
||||||
blk_delete(blk);
|
blk_delete(blk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -778,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk)
|
|||||||
|
|
||||||
blk_update_root_state(blk);
|
blk_update_root_state(blk);
|
||||||
|
|
||||||
/* bdrv_root_unref_child() will cause blk->root to become stale and may
|
|
||||||
* switch to a completion coroutine later on. Let's drain all I/O here
|
|
||||||
* to avoid that and a potential QEMU crash.
|
|
||||||
*/
|
|
||||||
blk_drain(blk);
|
|
||||||
bdrv_root_unref_child(blk->root);
|
bdrv_root_unref_child(blk->root);
|
||||||
blk->root = NULL;
|
blk->root = NULL;
|
||||||
}
|
}
|
||||||
@@ -918,8 +860,7 @@ char *blk_get_attached_dev_id(BlockBackend *blk)
|
|||||||
} else if (dev->id) {
|
} else if (dev->id) {
|
||||||
return g_strdup(dev->id);
|
return g_strdup(dev->id);
|
||||||
}
|
}
|
||||||
|
return object_get_canonical_path(OBJECT(dev));
|
||||||
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -992,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
|
|||||||
|
|
||||||
if (tray_was_open != tray_is_open) {
|
if (tray_was_open != tray_is_open) {
|
||||||
char *id = blk_get_attached_dev_id(blk);
|
char *id = blk_get_attached_dev_id(blk);
|
||||||
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open);
|
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open,
|
||||||
|
&error_abort);
|
||||||
g_free(id);
|
g_free(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1301,7 +1243,7 @@ static void blk_inc_in_flight(BlockBackend *blk)
|
|||||||
static void blk_dec_in_flight(BlockBackend *blk)
|
static void blk_dec_in_flight(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
atomic_dec(&blk->in_flight);
|
atomic_dec(&blk->in_flight);
|
||||||
aio_wait_kick();
|
aio_wait_kick(&blk->wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void error_callback_bh(void *opaque)
|
static void error_callback_bh(void *opaque)
|
||||||
@@ -1342,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
|
|||||||
static void blk_aio_complete(BlkAioEmAIOCB *acb)
|
static void blk_aio_complete(BlkAioEmAIOCB *acb)
|
||||||
{
|
{
|
||||||
if (acb->has_returned) {
|
if (acb->has_returned) {
|
||||||
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
|
||||||
blk_dec_in_flight(acb->rwco.blk);
|
blk_dec_in_flight(acb->rwco.blk);
|
||||||
|
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
||||||
qemu_aio_unref(acb);
|
qemu_aio_unref(acb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1570,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_co_pdiscard(blk->root, offset, bytes);
|
return bdrv_co_pdiscard(blk_bs(blk), offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_co_flush(BlockBackend *blk)
|
int blk_co_flush(BlockBackend *blk)
|
||||||
@@ -1602,7 +1544,8 @@ void blk_drain(BlockBackend *blk)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We may have -ENOMEDIUM completions in flight */
|
/* We may have -ENOMEDIUM completions in flight */
|
||||||
AIO_WAIT_WHILE(blk_get_aio_context(blk),
|
AIO_WAIT_WHILE(&blk->wait,
|
||||||
|
blk_get_aio_context(blk),
|
||||||
atomic_mb_read(&blk->in_flight) > 0);
|
atomic_mb_read(&blk->in_flight) > 0);
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
@@ -1622,7 +1565,8 @@ void blk_drain_all(void)
|
|||||||
aio_context_acquire(ctx);
|
aio_context_acquire(ctx);
|
||||||
|
|
||||||
/* We may have -ENOMEDIUM completions in flight */
|
/* We may have -ENOMEDIUM completions in flight */
|
||||||
AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0);
|
AIO_WAIT_WHILE(&blk->wait, ctx,
|
||||||
|
atomic_mb_read(&blk->in_flight) > 0);
|
||||||
|
|
||||||
aio_context_release(ctx);
|
aio_context_release(ctx);
|
||||||
}
|
}
|
||||||
@@ -1674,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk,
|
|||||||
qapi_event_send_block_io_error(blk_name(blk), !!bs,
|
qapi_event_send_block_io_error(blk_name(blk), !!bs,
|
||||||
bs ? bdrv_get_node_name(bs) : NULL, optype,
|
bs ? bdrv_get_node_name(bs) : NULL, optype,
|
||||||
action, blk_iostatus_is_enabled(blk),
|
action, blk_iostatus_is_enabled(blk),
|
||||||
error == ENOSPC, strerror(error));
|
error == ENOSPC, strerror(error),
|
||||||
|
&error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is done by device models because, while the block layer knows
|
/* This is done by device models because, while the block layer knows
|
||||||
@@ -1709,7 +1654,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blk_is_read_only(BlockBackend *blk)
|
int blk_is_read_only(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
@@ -1720,18 +1665,18 @@ bool blk_is_read_only(BlockBackend *blk)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blk_is_sg(BlockBackend *blk)
|
int blk_is_sg(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_is_sg(bs);
|
return bdrv_is_sg(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blk_enable_write_cache(BlockBackend *blk)
|
int blk_enable_write_cache(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
return blk->enable_write_cache;
|
return blk->enable_write_cache;
|
||||||
}
|
}
|
||||||
@@ -1790,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
|||||||
* the frontend experienced a tray event. */
|
* the frontend experienced a tray event. */
|
||||||
id = blk_get_attached_dev_id(blk);
|
id = blk_get_attached_dev_id(blk);
|
||||||
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
||||||
eject_flag);
|
eject_flag, &error_abort);
|
||||||
g_free(id);
|
g_free(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1877,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason)
|
|||||||
|
|
||||||
AioContext *blk_get_aio_context(BlockBackend *blk)
|
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)
|
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||||
@@ -1906,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk,
|
|||||||
void (*attached_aio_context)(AioContext *new_context, void *opaque),
|
void (*attached_aio_context)(AioContext *new_context, void *opaque),
|
||||||
void (*detach_aio_context)(void *opaque), void *opaque)
|
void (*detach_aio_context)(void *opaque), void *opaque)
|
||||||
{
|
{
|
||||||
BlockBackendAioNotifier *notifier;
|
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
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) {
|
if (bs) {
|
||||||
bdrv_add_aio_context_notifier(bs, attached_aio_context,
|
bdrv_add_aio_context_notifier(bs, attached_aio_context,
|
||||||
detach_aio_context, opaque);
|
detach_aio_context, opaque);
|
||||||
@@ -1927,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
|
|||||||
void (*detach_aio_context)(void *),
|
void (*detach_aio_context)(void *),
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
BlockBackendAioNotifier *notifier;
|
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_remove_aio_context_notifier(bs, attached_aio_context,
|
bdrv_remove_aio_context_notifier(bs, attached_aio_context,
|
||||||
detach_aio_context, opaque);
|
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)
|
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
|
||||||
@@ -2199,13 +2130,6 @@ static void blk_root_drained_begin(BdrvChild *child)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool blk_root_drained_poll(BdrvChild *child)
|
|
||||||
{
|
|
||||||
BlockBackend *blk = child->opaque;
|
|
||||||
assert(blk->quiesce_counter);
|
|
||||||
return !!blk->in_flight;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blk_root_drained_end(BdrvChild *child)
|
static void blk_root_drained_end(BdrvChild *child)
|
||||||
{
|
{
|
||||||
BlockBackend *blk = child->opaque;
|
BlockBackend *blk = child->opaque;
|
||||||
@@ -2230,22 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
|
|||||||
{
|
{
|
||||||
bdrv_unregister_buf(blk_bs(blk), 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 read_flags,
|
|
||||||
BdrvRequestFlags write_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, read_flags, write_flags);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
struct bochs_header bochs;
|
struct bochs_header bochs;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* No write support yet */
|
|
||||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||||
false, errp);
|
false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!bdrv_is_read_only(bs)) {
|
||||||
|
error_report("Opening bochs images without an explicit read-only=on "
|
||||||
|
"option is deprecated. Future versions will refuse to "
|
||||||
|
"open the image instead of automatically marking the "
|
||||||
|
"image read-only.");
|
||||||
|
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -67,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||||
false, errp);
|
false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!bdrv_is_read_only(bs)) {
|
||||||
|
error_report("Opening cloop images without an explicit read-only=on "
|
||||||
|
"option is deprecated. Future versions will refuse to "
|
||||||
|
"open the image instead of automatically marking the "
|
||||||
|
"image read-only.");
|
||||||
|
ret = bdrv_set_read_only(bs, true, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* read header */
|
/* read header */
|
||||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|||||||
172
block/commit.c
172
block/commit.c
@@ -31,12 +31,14 @@ enum {
|
|||||||
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
|
|
||||||
typedef struct CommitBlockJob {
|
typedef struct CommitBlockJob {
|
||||||
BlockJob common;
|
BlockJob common;
|
||||||
|
RateLimit limit;
|
||||||
BlockDriverState *commit_top_bs;
|
BlockDriverState *commit_top_bs;
|
||||||
BlockBackend *top;
|
BlockBackend *top;
|
||||||
BlockBackend *base;
|
BlockBackend *base;
|
||||||
BlockDriverState *base_bs;
|
|
||||||
BlockdevOnError on_error;
|
BlockdevOnError on_error;
|
||||||
int base_flags;
|
int base_flags;
|
||||||
char *backing_file_str;
|
char *backing_file_str;
|
||||||
@@ -69,93 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int commit_prepare(Job *job)
|
typedef struct {
|
||||||
|
int ret;
|
||||||
|
} CommitCompleteData;
|
||||||
|
|
||||||
|
static void commit_complete(BlockJob *job, void *opaque)
|
||||||
{
|
{
|
||||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||||
|
CommitCompleteData *data = opaque;
|
||||||
|
BlockDriverState *top = blk_bs(s->top);
|
||||||
|
BlockDriverState *base = blk_bs(s->base);
|
||||||
|
BlockDriverState *commit_top_bs = s->commit_top_bs;
|
||||||
|
int ret = data->ret;
|
||||||
|
bool remove_commit_top_bs = false;
|
||||||
|
|
||||||
|
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
||||||
|
bdrv_ref(top);
|
||||||
|
bdrv_ref(commit_top_bs);
|
||||||
|
|
||||||
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
||||||
* the normal backing chain can be restored. */
|
* the normal backing chain can be restored. */
|
||||||
blk_unref(s->base);
|
blk_unref(s->base);
|
||||||
s->base = NULL;
|
|
||||||
|
|
||||||
/* FIXME: bdrv_drop_intermediate treats total failures and partial failures
|
if (!block_job_is_cancelled(&s->common) && ret == 0) {
|
||||||
* identically. Further work is needed to disambiguate these cases. */
|
/* success */
|
||||||
return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs,
|
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
|
||||||
s->backing_file_str);
|
s->backing_file_str);
|
||||||
}
|
} else {
|
||||||
|
/* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
||||||
static void commit_abort(Job *job)
|
|
||||||
{
|
|
||||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
|
||||||
BlockDriverState *top_bs = blk_bs(s->top);
|
|
||||||
|
|
||||||
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
|
||||||
bdrv_ref(top_bs);
|
|
||||||
bdrv_ref(s->commit_top_bs);
|
|
||||||
|
|
||||||
if (s->base) {
|
|
||||||
blk_unref(s->base);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free the blockers on the intermediate nodes so that bdrv_replace_nodes
|
|
||||||
* can succeed */
|
|
||||||
block_job_remove_all_bdrv(&s->common);
|
|
||||||
|
|
||||||
/* If bdrv_drop_intermediate() failed (or was not invoked), remove the
|
|
||||||
* commit filter driver from the backing chain now. Do this as the final
|
|
||||||
* step so that the 'consistent read' permission can be granted.
|
|
||||||
*
|
|
||||||
* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
|
||||||
* after the failed/cancelled commit job is gone? If we already wrote
|
* after the failed/cancelled commit job is gone? If we already wrote
|
||||||
* something to base, the intermediate images aren't valid any more. */
|
* something to base, the intermediate images aren't valid any more. */
|
||||||
bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL,
|
remove_commit_top_bs = true;
|
||||||
&error_abort);
|
}
|
||||||
bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs),
|
|
||||||
&error_abort);
|
|
||||||
|
|
||||||
bdrv_unref(s->commit_top_bs);
|
|
||||||
bdrv_unref(top_bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void commit_clean(Job *job)
|
|
||||||
{
|
|
||||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
|
||||||
|
|
||||||
/* restore base open flags here if appropriate (e.g., change the base back
|
/* restore base open flags here if appropriate (e.g., change the base back
|
||||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||||
* even on failure here */
|
* even on failure here */
|
||||||
if (s->base_flags != bdrv_get_flags(s->base_bs)) {
|
if (s->base_flags != bdrv_get_flags(base)) {
|
||||||
bdrv_reopen(s->base_bs, s->base_flags, NULL);
|
bdrv_reopen(base, s->base_flags, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(s->backing_file_str);
|
g_free(s->backing_file_str);
|
||||||
blk_unref(s->top);
|
blk_unref(s->top);
|
||||||
|
|
||||||
|
/* If there is more than one reference to the job (e.g. if called from
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
block_job_completed(&s->common, ret);
|
||||||
|
g_free(data);
|
||||||
|
|
||||||
|
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
|
||||||
|
* filter driver from the backing chain. Do this as the final step so that
|
||||||
|
* the 'consistent read' permission can be granted. */
|
||||||
|
if (remove_commit_top_bs) {
|
||||||
|
bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL,
|
||||||
|
&error_abort);
|
||||||
|
bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs),
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_unref(commit_top_bs);
|
||||||
|
bdrv_unref(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn commit_run(Job *job, Error **errp)
|
static void coroutine_fn commit_run(void *opaque)
|
||||||
{
|
{
|
||||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
CommitBlockJob *s = opaque;
|
||||||
|
CommitCompleteData *data;
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
uint64_t delay_ns = 0;
|
uint64_t delay_ns = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int64_t n = 0; /* bytes */
|
int64_t n = 0; /* bytes */
|
||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
int bytes_written = 0;
|
int bytes_written = 0;
|
||||||
int64_t len, base_len;
|
int64_t base_len;
|
||||||
|
|
||||||
ret = len = blk_getlength(s->top);
|
ret = s->common.len = blk_getlength(s->top);
|
||||||
if (len < 0) {
|
|
||||||
|
if (s->common.len < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
job_progress_set_remaining(&s->common.job, len);
|
|
||||||
|
|
||||||
ret = base_len = blk_getlength(s->base);
|
ret = base_len = blk_getlength(s->base);
|
||||||
if (base_len < 0) {
|
if (base_len < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base_len < len) {
|
if (base_len < s->common.len) {
|
||||||
ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
|
ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -163,14 +168,14 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
|||||||
|
|
||||||
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
|
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;
|
bool copy;
|
||||||
|
|
||||||
/* Note that even when no rate limit is applied we need to yield
|
/* 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.
|
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||||
*/
|
*/
|
||||||
job_sleep_ns(&s->common.job, delay_ns);
|
block_job_sleep_ns(&s->common, delay_ns);
|
||||||
if (job_is_cancelled(&s->common.job)) {
|
if (block_job_is_cancelled(&s->common)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Copy if allocated above the base */
|
/* Copy if allocated above the base */
|
||||||
@@ -193,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Publish progress */
|
/* Publish progress */
|
||||||
job_progress_update(&s->common.job, n);
|
s->common.offset += n;
|
||||||
|
|
||||||
if (copy) {
|
if (copy && s->common.speed) {
|
||||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||||
} else {
|
|
||||||
delay_ns = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
|||||||
out:
|
out:
|
||||||
qemu_vfree(buf);
|
qemu_vfree(buf);
|
||||||
|
|
||||||
return ret;
|
data = g_malloc(sizeof(*data));
|
||||||
|
data->ret = ret;
|
||||||
|
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 = {
|
static const BlockJobDriver commit_job_driver = {
|
||||||
.job_driver = {
|
|
||||||
.instance_size = sizeof(CommitBlockJob),
|
.instance_size = sizeof(CommitBlockJob),
|
||||||
.job_type = JOB_TYPE_COMMIT,
|
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||||
.free = block_job_free,
|
.set_speed = commit_set_speed,
|
||||||
.user_resume = block_job_user_resume,
|
.start = commit_run,
|
||||||
.drain = block_job_drain,
|
|
||||||
.run = commit_run,
|
|
||||||
.prepare = commit_prepare,
|
|
||||||
.abort = commit_abort,
|
|
||||||
.clean = commit_clean
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
||||||
@@ -237,6 +246,10 @@ static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
|
|||||||
bs->backing->bs->filename);
|
bs->backing->bs->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bdrv_commit_top_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||||
const BdrvChildRole *role,
|
const BdrvChildRole *role,
|
||||||
BlockReopenQueue *reopen_queue,
|
BlockReopenQueue *reopen_queue,
|
||||||
@@ -254,12 +267,12 @@ static BlockDriver bdrv_commit_top = {
|
|||||||
.bdrv_co_preadv = bdrv_commit_top_preadv,
|
.bdrv_co_preadv = bdrv_commit_top_preadv,
|
||||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||||
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
|
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
|
||||||
|
.bdrv_close = bdrv_commit_top_close,
|
||||||
.bdrv_child_perm = bdrv_commit_top_child_perm,
|
.bdrv_child_perm = bdrv_commit_top_child_perm,
|
||||||
};
|
};
|
||||||
|
|
||||||
void commit_start(const char *job_id, BlockDriverState *bs,
|
void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
BlockDriverState *base, BlockDriverState *top,
|
BlockDriverState *base, BlockDriverState *top, int64_t speed,
|
||||||
int creation_flags, int64_t speed,
|
|
||||||
BlockdevOnError on_error, const char *backing_file_str,
|
BlockdevOnError on_error, const char *backing_file_str,
|
||||||
const char *filter_node_name, Error **errp)
|
const char *filter_node_name, Error **errp)
|
||||||
{
|
{
|
||||||
@@ -276,8 +289,8 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
|
||||||
speed, creation_flags, NULL, NULL, errp);
|
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -354,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
s->base_bs = base;
|
|
||||||
|
|
||||||
/* Required permissions are already taken with block_job_add_bdrv() */
|
/* Required permissions are already taken with block_job_add_bdrv() */
|
||||||
s->top = blk_new(0, BLK_PERM_ALL);
|
s->top = blk_new(0, BLK_PERM_ALL);
|
||||||
@@ -368,7 +380,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
|||||||
s->on_error = on_error;
|
s->on_error = on_error;
|
||||||
|
|
||||||
trace_commit_start(bs, base, top, s);
|
trace_commit_start(bs, base, top, s);
|
||||||
job_start(&s->common.job);
|
block_job_start(&s->common);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@@ -381,7 +393,7 @@ fail:
|
|||||||
if (commit_top_bs) {
|
if (commit_top_bs) {
|
||||||
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
||||||
}
|
}
|
||||||
job_early_fail(&s->common.job);
|
block_job_early_fail(&s->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,167 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#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 coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset,
|
|
||||||
PreallocMode prealloc, Error **errp)
|
|
||||||
{
|
|
||||||
return bdrv_co_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, 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_child_perm = cor_child_perm,
|
|
||||||
|
|
||||||
.bdrv_getlength = cor_getlength,
|
|
||||||
.bdrv_co_truncate = cor_co_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,44 +24,28 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/job.h"
|
|
||||||
#include "qapi/qapi-commands-block-core.h"
|
#include "qapi/qapi-commands-block-core.h"
|
||||||
#include "qapi/qapi-visit-block-core.h"
|
|
||||||
#include "qapi/clone-visitor.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
||||||
typedef struct BlockdevCreateJob {
|
typedef struct BlockdevCreateCo {
|
||||||
Job common;
|
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
BlockdevCreateOptions *opts;
|
BlockdevCreateOptions *opts;
|
||||||
} BlockdevCreateJob;
|
|
||||||
|
|
||||||
static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
|
|
||||||
{
|
|
||||||
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
Error **errp;
|
||||||
|
} BlockdevCreateCo;
|
||||||
|
|
||||||
job_progress_set_remaining(&s->common, 1);
|
static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
|
||||||
ret = s->drv->bdrv_co_create(s->opts, errp);
|
{
|
||||||
job_progress_update(&s->common, 1);
|
BlockdevCreateCo *cco = opaque;
|
||||||
|
cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
|
||||||
qapi_free_BlockdevCreateOptions(s->opts);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const JobDriver blockdev_create_job_driver = {
|
void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
|
||||||
.instance_size = sizeof(BlockdevCreateJob),
|
|
||||||
.job_type = JOB_TYPE_CREATE,
|
|
||||||
.run = blockdev_create_run,
|
|
||||||
};
|
|
||||||
|
|
||||||
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BlockdevCreateJob *s;
|
|
||||||
const char *fmt = BlockdevDriver_str(options->driver);
|
const char *fmt = BlockdevDriver_str(options->driver);
|
||||||
BlockDriver *drv = bdrv_find_format(fmt);
|
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
|
/* If the driver is in the schema, we know that it exists. But it may not
|
||||||
* be whitelisted. */
|
* be whitelisted. */
|
||||||
@@ -71,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Error out if the driver doesn't support .bdrv_co_create */
|
/* Call callback if it exists */
|
||||||
if (!drv->bdrv_co_create) {
|
if (!drv->bdrv_co_create) {
|
||||||
error_setg(errp, "Driver does not support blockdev-create");
|
error_setg(errp, "Driver does not support blockdev-create");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the block job */
|
cco = (BlockdevCreateCo) {
|
||||||
/* TODO Running in the main context. Block drivers need to error out or add
|
.drv = drv,
|
||||||
* locking when they use a BDS in a different AioContext. */
|
.opts = options,
|
||||||
s = job_create(job_id, &blockdev_create_job_driver, NULL,
|
.ret = -EINPROGRESS,
|
||||||
qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
|
.errp = errp,
|
||||||
NULL, NULL, errp);
|
};
|
||||||
if (!s) {
|
|
||||||
return;
|
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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
264
block/crypto.c
264
block/crypto.c
@@ -21,15 +21,15 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "crypto/block.h"
|
#include "crypto/block.h"
|
||||||
#include "qapi/opts-visitor.h"
|
#include "qapi/opts-visitor.h"
|
||||||
#include "qapi/qapi-visit-crypto.h"
|
#include "qapi/qapi-visit-crypto.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "crypto.h"
|
#include "block/crypto.h"
|
||||||
|
|
||||||
typedef struct BlockCrypto BlockCrypto;
|
typedef struct BlockCrypto BlockCrypto;
|
||||||
|
|
||||||
@@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
|||||||
|
|
||||||
|
|
||||||
struct BlockCryptoCreateData {
|
struct BlockCryptoCreateData {
|
||||||
|
const char *filename;
|
||||||
|
QemuOpts *opts;
|
||||||
BlockBackend *blk;
|
BlockBackend *blk;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
};
|
};
|
||||||
@@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct BlockCryptoCreateData *data = opaque;
|
struct BlockCryptoCreateData *data = opaque;
|
||||||
|
int ret;
|
||||||
if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) {
|
|
||||||
error_setg(errp, "The requested file size is too large");
|
|
||||||
return -EFBIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* User provided size should reflect amount of space made
|
/* User provided size should reflect amount of space made
|
||||||
* available to the guest, so we must take account of that
|
* available to the guest, so we must take account of that
|
||||||
* which will be used by the crypto header
|
* which will be used by the crypto header
|
||||||
*/
|
*/
|
||||||
return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
|
data->size += headerlen;
|
||||||
errp);
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -148,36 +159,102 @@ static QemuOptsList block_crypto_create_opts_luks = {
|
|||||||
|
|
||||||
|
|
||||||
QCryptoBlockOpenOptions *
|
QCryptoBlockOpenOptions *
|
||||||
block_crypto_open_opts_init(QDict *opts, Error **errp)
|
block_crypto_open_opts_init(QCryptoBlockFormat format,
|
||||||
|
QDict *opts,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
QCryptoBlockOpenOptions *ret;
|
QCryptoBlockOpenOptions *ret = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
v = qobject_input_visitor_new_flat_confused(opts, errp);
|
ret = g_new0(QCryptoBlockOpenOptions, 1);
|
||||||
if (!v) {
|
ret->format = format;
|
||||||
return NULL;
|
|
||||||
|
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
|
||||||
|
|
||||||
|
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp);
|
switch (format) {
|
||||||
|
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
|
||||||
|
visit_type_QCryptoBlockOptionsLUKS_members(
|
||||||
|
v, &ret->u.luks, &local_err);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_CRYPTO_BLOCK_FORMAT_QCOW:
|
||||||
|
visit_type_QCryptoBlockOptionsQCow_members(
|
||||||
|
v, &ret->u.qcow, &local_err);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_setg(&local_err, "Unsupported block format %d", format);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!local_err) {
|
||||||
|
visit_check_struct(v, &local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
visit_end_struct(v, NULL);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
qapi_free_QCryptoBlockOpenOptions(ret);
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QCryptoBlockCreateOptions *
|
QCryptoBlockCreateOptions *
|
||||||
block_crypto_create_opts_init(QDict *opts, Error **errp)
|
block_crypto_create_opts_init(QCryptoBlockFormat format,
|
||||||
|
QDict *opts,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
QCryptoBlockCreateOptions *ret;
|
QCryptoBlockCreateOptions *ret = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
v = qobject_input_visitor_new_flat_confused(opts, errp);
|
ret = g_new0(QCryptoBlockCreateOptions, 1);
|
||||||
if (!v) {
|
ret->format = format;
|
||||||
return NULL;
|
|
||||||
|
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
|
||||||
|
|
||||||
|
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp);
|
switch (format) {
|
||||||
|
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
|
||||||
|
visit_type_QCryptoBlockCreateOptionsLUKS_members(
|
||||||
|
v, &ret->u.luks, &local_err);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_CRYPTO_BLOCK_FORMAT_QCOW:
|
||||||
|
visit_type_QCryptoBlockOptionsQCow_members(
|
||||||
|
v, &ret->u.qcow, &local_err);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_setg(&local_err, "Unsupported block format %d", format);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!local_err) {
|
||||||
|
visit_check_struct(v, &local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
visit_end_struct(v, NULL);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
qapi_free_QCryptoBlockCreateOptions(ret);
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -215,9 +292,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cryptoopts = qemu_opts_to_qdict(opts, NULL);
|
cryptoopts = qemu_opts_to_qdict(opts, NULL);
|
||||||
qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format));
|
|
||||||
|
|
||||||
open_opts = block_crypto_open_opts_init(cryptoopts, errp);
|
open_opts = block_crypto_open_opts_init(format, cryptoopts, errp);
|
||||||
if (!open_opts) {
|
if (!open_opts) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -240,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
cleanup:
|
cleanup:
|
||||||
qobject_unref(cryptoopts);
|
QDECREF(cryptoopts);
|
||||||
qapi_free_QCryptoBlockOpenOptions(open_opts);
|
qapi_free_QCryptoBlockOpenOptions(open_opts);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int block_crypto_co_create_generic(BlockDriverState *bs,
|
static int block_crypto_create_generic(QCryptoBlockFormat format,
|
||||||
int64_t size,
|
const char *filename,
|
||||||
QCryptoBlockCreateOptions *opts,
|
QemuOpts *opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret = -EINVAL;
|
||||||
BlockBackend *blk;
|
QCryptoBlockCreateOptions *create_opts = NULL;
|
||||||
QCryptoBlock *crypto = 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);
|
create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
|
||||||
if (ret < 0) {
|
if (!create_opts) {
|
||||||
goto cleanup;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = (struct BlockCryptoCreateData) {
|
crypto = qcrypto_block_create(create_opts, NULL,
|
||||||
.blk = blk,
|
|
||||||
.size = size,
|
|
||||||
};
|
|
||||||
|
|
||||||
crypto = qcrypto_block_create(opts, NULL,
|
|
||||||
block_crypto_init_func,
|
block_crypto_init_func,
|
||||||
block_crypto_write_func,
|
block_crypto_write_func,
|
||||||
&data,
|
&data,
|
||||||
@@ -281,27 +358,24 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
cleanup:
|
cleanup:
|
||||||
|
QDECREF(cryptoopts);
|
||||||
qcrypto_block_free(crypto);
|
qcrypto_block_free(crypto);
|
||||||
blk_unref(blk);
|
blk_unref(data.blk);
|
||||||
|
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn
|
static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
|
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
BlockCrypto *crypto = bs->opaque;
|
BlockCrypto *crypto = bs->opaque;
|
||||||
uint64_t payload_offset =
|
uint64_t payload_offset =
|
||||||
qcrypto_block_get_payload_offset(crypto->block);
|
qcrypto_block_get_payload_offset(crypto->block);
|
||||||
|
assert(payload_offset < (INT64_MAX - offset));
|
||||||
if (payload_offset > INT64_MAX - offset) {
|
|
||||||
error_setg(errp, "The requested file size is too large");
|
|
||||||
return -EFBIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += payload_offset;
|
offset += payload_offset;
|
||||||
|
|
||||||
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
|
return bdrv_truncate(bs->file, offset, prealloc, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block_crypto_close(BlockDriverState *bs)
|
static void block_crypto_close(BlockDriverState *bs)
|
||||||
@@ -463,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
|
|||||||
|
|
||||||
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
|
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
|
||||||
assert(offset < INT64_MAX);
|
assert(offset < INT64_MAX);
|
||||||
|
assert(offset < len);
|
||||||
if (offset > len) {
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
len -= offset;
|
len -= offset;
|
||||||
|
|
||||||
@@ -491,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs,
|
|||||||
bs, options, flags, errp);
|
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,
|
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
||||||
QemuOpts *opts,
|
QemuOpts *opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QCryptoBlockCreateOptions *create_opts = NULL;
|
return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||||
BlockDriverState *bs = NULL;
|
filename, opts, errp);
|
||||||
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);
|
|
||||||
|
|
||||||
qdict_put_str(cryptoopts, "format", "luks");
|
|
||||||
create_opts = block_crypto_create_opts_init(cryptoopts, errp);
|
|
||||||
if (!create_opts) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create protocol layer */
|
|
||||||
ret = bdrv_create_file(filename, opts, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int block_crypto_get_info_luks(BlockDriverState *bs,
|
static int block_crypto_get_info_luks(BlockDriverState *bs,
|
||||||
@@ -627,12 +622,9 @@ BlockDriver bdrv_crypto_luks = {
|
|||||||
.bdrv_probe = block_crypto_probe_luks,
|
.bdrv_probe = block_crypto_probe_luks,
|
||||||
.bdrv_open = block_crypto_open_luks,
|
.bdrv_open = block_crypto_open_luks,
|
||||||
.bdrv_close = block_crypto_close,
|
.bdrv_close = block_crypto_close,
|
||||||
/* This driver doesn't modify LUKS metadata except when creating image.
|
.bdrv_child_perm = bdrv_format_default_perms,
|
||||||
* Allow share-rw=on as a special case. */
|
|
||||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
|
||||||
.bdrv_co_create = block_crypto_co_create_luks,
|
|
||||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||||
.bdrv_co_truncate = block_crypto_co_truncate,
|
.bdrv_truncate = block_crypto_truncate,
|
||||||
.create_opts = &block_crypto_create_opts_luks,
|
.create_opts = &block_crypto_create_opts_luks,
|
||||||
|
|
||||||
.bdrv_reopen_prepare = block_crypto_reopen_prepare,
|
.bdrv_reopen_prepare = block_crypto_reopen_prepare,
|
||||||
|
|||||||
@@ -89,9 +89,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
QCryptoBlockCreateOptions *
|
QCryptoBlockCreateOptions *
|
||||||
block_crypto_create_opts_init(QDict *opts, Error **errp);
|
block_crypto_create_opts_init(QCryptoBlockFormat format,
|
||||||
|
QDict *opts,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
QCryptoBlockOpenOptions *
|
QCryptoBlockOpenOptions *
|
||||||
block_crypto_open_opts_init(QDict *opts, Error **errp);
|
block_crypto_open_opts_init(QCryptoBlockFormat format,
|
||||||
|
QDict *opts,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
#endif /* BLOCK_CRYPTO_H__ */
|
#endif /* BLOCK_CRYPTO_H__ */
|
||||||
|
|||||||
12
block/curl.c
12
block/curl.c
@@ -483,8 +483,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
|||||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||||
(long) s->sslverify);
|
(long) s->sslverify);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
|
|
||||||
s->sslverify ? 2L : 0L);
|
|
||||||
if (s->cookie) {
|
if (s->cookie) {
|
||||||
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
||||||
}
|
}
|
||||||
@@ -684,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
const char *protocol_delimiter;
|
const char *protocol_delimiter;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
|
|
||||||
errp);
|
if (flags & BDRV_O_RDWR) {
|
||||||
if (ret < 0) {
|
error_setg(errp, "curl block device does not support writes");
|
||||||
return ret;
|
return -EROFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!libcurl_initialized) {
|
if (!libcurl_initialized) {
|
||||||
@@ -806,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
/* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
|
/* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
|
||||||
* know or the size is zero. From 7.19.4 CURL returns -1 if size is not
|
* know or the size is zero. From 7.19.4 CURL returns -1 if size is not
|
||||||
* known and zero if it is really zero-length file. */
|
* known and zero if it is realy zero-length file. */
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071304
|
#if LIBCURL_VERSION_NUM >= 0x071304
|
||||||
if (d < 0) {
|
if (d < 0) {
|
||||||
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ struct BdrvDirtyBitmap {
|
|||||||
QemuMutex *mutex;
|
QemuMutex *mutex;
|
||||||
HBitmap *bitmap; /* Dirty bitmap implementation */
|
HBitmap *bitmap; /* Dirty bitmap implementation */
|
||||||
HBitmap *meta; /* Meta dirty bitmap */
|
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 */
|
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||||
char *name; /* Optional non-empty unique ID */
|
char *name; /* Optional non-empty unique ID */
|
||||||
int64_t size; /* Size of the bitmap, in bytes */
|
int64_t size; /* Size of the bitmap, in bytes */
|
||||||
@@ -55,10 +53,6 @@ struct BdrvDirtyBitmap {
|
|||||||
and this bitmap must remain unchanged while
|
and this bitmap must remain unchanged while
|
||||||
this flag is set. */
|
this flag is set. */
|
||||||
bool persistent; /* bitmap must be saved to owner disk image */
|
bool persistent; /* bitmap must be saved to owner disk image */
|
||||||
bool migration; /* Bitmap is selected for migration, it should
|
|
||||||
not be stored on the next inactivation
|
|
||||||
(persistent flag doesn't matter until next
|
|
||||||
invalidation).*/
|
|
||||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
|||||||
return NULL;
|
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. */
|
/* Called with BQL taken. */
|
||||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||||
uint32_t granularity,
|
uint32_t granularity,
|
||||||
@@ -180,24 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
|||||||
return bitmap->successor;
|
return bitmap->successor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Both conditions disallow user-modification via QMP. */
|
|
||||||
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) {
|
|
||||||
return bdrv_dirty_bitmap_frozen(bitmap) ||
|
|
||||||
bdrv_dirty_bitmap_qmp_locked(bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_set_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. */
|
/* Called with BQL taken. */
|
||||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
@@ -209,8 +194,6 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
|||||||
{
|
{
|
||||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
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)) {
|
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||||
} else {
|
} else {
|
||||||
@@ -251,33 +234,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
bitmap->disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
|
||||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
assert(bitmap->mutex == bitmap->successor->mutex);
|
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
|
||||||
bdrv_enable_dirty_bitmap_locked(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,
|
* For a bitmap with a successor, yield our name to the successor,
|
||||||
* delete the old bitmap, and return a handle to the new bitmap.
|
* delete the old bitmap, and return a handle to the new bitmap.
|
||||||
@@ -311,9 +267,9 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
|||||||
* In cases of failure where we can no longer safely delete the parent,
|
* In cases of failure where we can no longer safely delete the parent,
|
||||||
* we may wish to re-join the parent and child/successor.
|
* we may wish to re-join the parent and child/successor.
|
||||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
* 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 *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *parent,
|
BdrvDirtyBitmap *parent,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@@ -324,30 +280,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
|
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
||||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
bdrv_release_dirty_bitmap_locked(successor);
|
bdrv_release_dirty_bitmap(bs, successor);
|
||||||
parent->successor = NULL;
|
parent->successor = NULL;
|
||||||
|
|
||||||
return parent;
|
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.
|
* Truncates _all_ bitmaps attached to a BDS.
|
||||||
* Called with BQL taken.
|
* Called with BQL taken.
|
||||||
@@ -366,12 +308,45 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
|
|||||||
bdrv_dirty_bitmaps_unlock(bs);
|
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. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmaps_lock(bs);
|
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL);
|
||||||
bdrv_release_dirty_bitmap_locked(bitmap);
|
|
||||||
bdrv_dirty_bitmaps_unlock(bs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,15 +357,19 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
|||||||
*/
|
*/
|
||||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm, *next;
|
bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name);
|
||||||
|
}
|
||||||
|
|
||||||
bdrv_dirty_bitmaps_lock(bs);
|
/**
|
||||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
* Release all persistent dirty bitmaps attached to a BDS (for use in
|
||||||
if (bdrv_dirty_bitmap_name(bm)) {
|
* bdrv_inactivate_recurse()).
|
||||||
bdrv_release_dirty_bitmap_locked(bm);
|
* There must not be any frozen bitmaps attached.
|
||||||
}
|
* This function does not remove persistent bitmaps from the storage.
|
||||||
}
|
*/
|
||||||
bdrv_dirty_bitmaps_unlock(bs);
|
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
bdrv_do_release_matching_dirty_bitmap(bs, NULL,
|
||||||
|
bdrv_dirty_bitmap_get_persistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -410,19 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
bitmap->disabled = true;
|
bitmap->disabled = true;
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
bdrv_enable_dirty_bitmap_locked(bitmap);
|
bitmap->disabled = false;
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||||
@@ -515,62 +493,7 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
|
|||||||
|
|
||||||
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
||||||
{
|
{
|
||||||
return hbitmap_iter_next(&iter->hbi, true);
|
return hbitmap_iter_next(&iter->hbi);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next consecutively dirty area in the dirty bitmap
|
|
||||||
* belonging to the given iterator @iter.
|
|
||||||
*
|
|
||||||
* @max_offset: Maximum value that may be returned for
|
|
||||||
* *offset + *bytes
|
|
||||||
* @offset: Will contain the start offset of the next dirty area
|
|
||||||
* @bytes: Will contain the length of the next dirty area
|
|
||||||
*
|
|
||||||
* Returns: True if a dirty area could be found before max_offset
|
|
||||||
* (which means that *offset and *bytes then contain valid
|
|
||||||
* values), false otherwise.
|
|
||||||
*
|
|
||||||
* Note that @iter is never advanced if false is returned. If an area
|
|
||||||
* is found (which means that true is returned), it will be advanced
|
|
||||||
* past that area.
|
|
||||||
*/
|
|
||||||
bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset,
|
|
||||||
uint64_t *offset, int *bytes)
|
|
||||||
{
|
|
||||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap);
|
|
||||||
uint64_t gran_max_offset;
|
|
||||||
int64_t ret;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if (max_offset == iter->bitmap->size) {
|
|
||||||
/* If max_offset points to the image end, round it up by the
|
|
||||||
* bitmap granularity */
|
|
||||||
gran_max_offset = ROUND_UP(max_offset, granularity);
|
|
||||||
} else {
|
|
||||||
gran_max_offset = max_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = hbitmap_iter_next(&iter->hbi, false);
|
|
||||||
if (ret < 0 || ret + granularity > gran_max_offset) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*offset = ret;
|
|
||||||
size = 0;
|
|
||||||
|
|
||||||
assert(granularity <= INT_MAX);
|
|
||||||
|
|
||||||
do {
|
|
||||||
/* Advance iterator */
|
|
||||||
ret = hbitmap_iter_next(&iter->hbi, true);
|
|
||||||
size += granularity;
|
|
||||||
} while (ret + granularity <= gran_max_offset &&
|
|
||||||
hbitmap_iter_next(&iter->hbi, false) == ret + granularity &&
|
|
||||||
size <= INT_MAX - granularity);
|
|
||||||
|
|
||||||
*bytes = MIN(size, max_offset - *offset);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||||
@@ -609,6 +532,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
|||||||
|
|
||||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
{
|
{
|
||||||
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
@@ -622,12 +546,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
|||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
|
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||||
{
|
{
|
||||||
HBitmap *tmp = bitmap->bitmap;
|
HBitmap *tmp = bitmap->bitmap;
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
bitmap->bitmap = backup;
|
bitmap->bitmap = in;
|
||||||
hbitmap_free(tmp);
|
hbitmap_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -745,24 +669,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
|
|||||||
qemu_mutex_unlock(bitmap->mutex);
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
|
||||||
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
|
|
||||||
{
|
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
|
||||||
bitmap->migration = migration;
|
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return bitmap->persistent && !bitmap->migration;
|
return bitmap->persistent;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm;
|
BdrvDirtyBitmap *bm;
|
||||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||||
if (bm->persistent && !bm->readonly && !bm->migration) {
|
if (bm->persistent && !bm->readonly) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -786,43 +702,3 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
|
|||||||
{
|
{
|
||||||
return hbitmap_next_zero(bitmap->bitmap, offset);
|
return hbitmap_next_zero(bitmap->bitmap, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
|
||||||
HBitmap **backup, Error **errp)
|
|
||||||
{
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
/* only bitmaps from one bds are supported */
|
|
||||||
assert(dest->mutex == src->mutex);
|
|
||||||
|
|
||||||
qemu_mutex_lock(dest->mutex);
|
|
||||||
|
|
||||||
if (bdrv_dirty_bitmap_user_locked(dest)) {
|
|
||||||
error_setg(errp, "Bitmap '%s' is currently in use by another"
|
|
||||||
" operation and cannot be modified", dest->name);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bdrv_dirty_bitmap_readonly(dest)) {
|
|
||||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
|
||||||
dest->name);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
|
|
||||||
error_setg(errp, "Bitmaps are incompatible and can't be merged");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backup) {
|
|
||||||
*backup = dest->bitmap;
|
|
||||||
dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
|
|
||||||
ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
|
|
||||||
} else {
|
|
||||||
ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
|
|
||||||
}
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
out:
|
|
||||||
qemu_mutex_unlock(dest->mutex);
|
|
||||||
}
|
|
||||||
|
|||||||
16
block/dmg.c
16
block/dmg.c
@@ -413,17 +413,23 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
int64_t offset;
|
int64_t offset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||||
false, errp);
|
false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!bdrv_is_read_only(bs)) {
|
||||||
|
error_report("Opening dmg images without an explicit read-only=on "
|
||||||
|
"option is deprecated. Future versions will refuse to "
|
||||||
|
"open the image instead of automatically marking the "
|
||||||
|
"image read-only.");
|
||||||
|
ret = bdrv_set_read_only(bs, true, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
block_module_load_one("dmg-bz2");
|
block_module_load_one("dmg-bz2");
|
||||||
|
|
||||||
s->n_chunks = 0;
|
s->n_chunks = 0;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
|||||||
acb->aio_nbytes = count;
|
acb->aio_nbytes = count;
|
||||||
acb->aio_offset = offset;
|
acb->aio_offset = offset;
|
||||||
|
|
||||||
trace_file_paio_submit(acb, opaque, offset, count, type);
|
trace_paio_submit(acb, opaque, offset, count, type);
|
||||||
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
||||||
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
||||||
}
|
}
|
||||||
@@ -251,11 +251,7 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp)
|
|||||||
&dg.Geometry.BytesPerSector,
|
&dg.Geometry.BytesPerSector,
|
||||||
&freeClusters, &totalClusters);
|
&freeClusters, &totalClusters);
|
||||||
bs->bl.request_alignment = dg.Geometry.BytesPerSector;
|
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,
|
static void raw_parse_flags(int flags, bool use_aio, int *access_flags,
|
||||||
@@ -414,32 +410,32 @@ fail:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs,
|
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||||
QEMUIOVector *qiov, int flags,
|
|
||||||
BlockCompletionFunc *cb, void *opaque)
|
BlockCompletionFunc *cb, void *opaque)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
if (s->aio) {
|
if (s->aio) {
|
||||||
return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
|
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||||
cb, opaque, QEMU_AIO_READ);
|
nb_sectors, cb, opaque, QEMU_AIO_READ);
|
||||||
} else {
|
} 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);
|
cb, opaque, QEMU_AIO_READ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs,
|
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||||
QEMUIOVector *qiov, int flags,
|
|
||||||
BlockCompletionFunc *cb, void *opaque)
|
BlockCompletionFunc *cb, void *opaque)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
if (s->aio) {
|
if (s->aio) {
|
||||||
return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
|
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||||
cb, opaque, QEMU_AIO_WRITE);
|
nb_sectors, cb, opaque, QEMU_AIO_WRITE);
|
||||||
} else {
|
} 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);
|
cb, opaque, QEMU_AIO_WRITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -467,7 +463,7 @@ static void raw_close(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
|
static int raw_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
@@ -636,11 +632,11 @@ BlockDriver bdrv_file = {
|
|||||||
.bdrv_co_create_opts = raw_co_create_opts,
|
.bdrv_co_create_opts = raw_co_create_opts,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
|
|
||||||
.bdrv_aio_preadv = raw_aio_preadv,
|
.bdrv_aio_readv = raw_aio_readv,
|
||||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
|
|
||||||
.bdrv_co_truncate = raw_co_truncate,
|
.bdrv_truncate = raw_truncate,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_getlength = raw_getlength,
|
||||||
.bdrv_get_allocated_file_size
|
.bdrv_get_allocated_file_size
|
||||||
= raw_get_allocated_file_size,
|
= raw_get_allocated_file_size,
|
||||||
@@ -712,12 +708,6 @@ static void hdev_parse_filename(const char *filename, QDict *options,
|
|||||||
bdrv_parse_filename_strip_prefix(filename, "host_device:", 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,
|
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = {
|
|||||||
.bdrv_probe_device = hdev_probe_device,
|
.bdrv_probe_device = hdev_probe_device,
|
||||||
.bdrv_file_open = hdev_open,
|
.bdrv_file_open = hdev_open,
|
||||||
.bdrv_close = raw_close,
|
.bdrv_close = raw_close,
|
||||||
.bdrv_refresh_limits = hdev_refresh_limits,
|
|
||||||
|
|
||||||
.bdrv_aio_preadv = raw_aio_preadv,
|
.bdrv_aio_readv = raw_aio_readv,
|
||||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
|
|
||||||
.bdrv_detach_aio_context = raw_detach_aio_context,
|
.bdrv_detach_aio_context = raw_detach_aio_context,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include <glusterfs/api/glfs.h>
|
#include <glusterfs/api/glfs.h>
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
@@ -168,12 +167,7 @@ static QemuOptsList runtime_unix_opts = {
|
|||||||
{
|
{
|
||||||
.name = GLUSTER_OPT_SOCKET,
|
.name = GLUSTER_OPT_SOCKET,
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "socket file path (legacy)",
|
.help = "socket file path)",
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = GLUSTER_OPT_PATH,
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "socket file path (QAPI)",
|
|
||||||
},
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
@@ -621,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH);
|
|
||||||
if (!ptr) {
|
|
||||||
ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET);
|
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;
|
|
||||||
}
|
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
error_setg(&local_err, QERR_MISSING_PARAMETER,
|
error_setg(&local_err, QERR_MISSING_PARAMETER,
|
||||||
GLUSTER_OPT_PATH);
|
GLUSTER_OPT_SOCKET);
|
||||||
error_append_hint(&local_err, GERR_INDEX_HINT, i);
|
error_append_hint(&local_err, GERR_INDEX_HINT, i);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -651,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
}
|
}
|
||||||
gsconf = NULL;
|
gsconf = NULL;
|
||||||
|
|
||||||
qobject_unref(backing_options);
|
QDECREF(backing_options);
|
||||||
backing_options = NULL;
|
backing_options = NULL;
|
||||||
g_free(str);
|
g_free(str);
|
||||||
str = NULL;
|
str = NULL;
|
||||||
@@ -664,7 +650,7 @@ out:
|
|||||||
qapi_free_SocketAddress(gsconf);
|
qapi_free_SocketAddress(gsconf);
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
g_free(str);
|
g_free(str);
|
||||||
qobject_unref(backing_options);
|
QDECREF(backing_options);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
@@ -679,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
|
|||||||
if (filename) {
|
if (filename) {
|
||||||
ret = qemu_gluster_parse_uri(gconf, filename);
|
ret = qemu_gluster_parse_uri(gconf, filename);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "invalid URI %s", filename);
|
error_setg(errp, "invalid URI");
|
||||||
error_append_hint(errp, "Usage: file=gluster[+transport]://"
|
error_append_hint(errp, "Usage: file=gluster[+transport]://"
|
||||||
"[host[:port]]volume/path[?socket=...]"
|
"[host[:port]]volume/path[?socket=...]"
|
||||||
"[,file.debug=N]"
|
"[,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.host=1.2.3.4,"
|
||||||
"file.server.0.port=24007,"
|
"file.server.0.port=24007,"
|
||||||
"file.server.1.transport=unix,"
|
"file.server.1.transport=unix,"
|
||||||
"file.server.1.path=/var/run/glusterd.socket ..."
|
"file.server.1.socket=/var/run/glusterd.socket ..."
|
||||||
"\n");
|
"\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -849,16 +835,8 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
|||||||
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
||||||
|
|
||||||
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
||||||
ret = s->fd ? 0 : -errno;
|
if (!s->fd) {
|
||||||
|
ret = -errno;
|
||||||
if (ret == -EACCES || ret == -EROFS) {
|
|
||||||
/* Try to degrade to read-only, but if it doesn't work, still use the
|
|
||||||
* normal error message. */
|
|
||||||
if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
|
|
||||||
open_flags = (open_flags & ~O_RDWR) | O_RDONLY;
|
|
||||||
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
|
||||||
ret = s->fd ? 0 : -errno;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
|
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
|
||||||
@@ -1185,10 +1163,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
|||||||
return acb.ret;
|
return acb.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
|
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
int64_t offset,
|
PreallocMode prealloc, Error **errp)
|
||||||
PreallocMode prealloc,
|
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
|
return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
|
||||||
@@ -1205,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
|
|||||||
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
|
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num,
|
||||||
int nb_sectors,
|
int nb_sectors,
|
||||||
QEMUIOVector *qiov,
|
QEMUIOVector *qiov)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
assert(!flags);
|
|
||||||
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
|
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1334,7 +1308,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs)
|
|||||||
* If @start is in a trailing hole or beyond EOF, return -ENXIO.
|
* If @start is in a trailing hole or beyond EOF, return -ENXIO.
|
||||||
* If we can't find out, return a negative errno other than -ENXIO.
|
* If we can't find out, return a negative errno other than -ENXIO.
|
||||||
*
|
*
|
||||||
* (Shamefully copied from file-posix.c, only minuscule adaptions.)
|
* (Shamefully copied from file-posix.c, only miniscule adaptions.)
|
||||||
*/
|
*/
|
||||||
static int find_allocation(BlockDriverState *bs, off_t start,
|
static int find_allocation(BlockDriverState *bs, off_t start,
|
||||||
off_t *data, off_t *hole)
|
off_t *data, off_t *hole)
|
||||||
@@ -1509,7 +1483,7 @@ static BlockDriver bdrv_gluster = {
|
|||||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_co_truncate = qemu_gluster_co_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
@@ -1538,7 +1512,7 @@ static BlockDriver bdrv_gluster_tcp = {
|
|||||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_co_truncate = qemu_gluster_co_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
@@ -1567,7 +1541,7 @@ static BlockDriver bdrv_gluster_unix = {
|
|||||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_co_truncate = qemu_gluster_co_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
@@ -1602,7 +1576,7 @@ static BlockDriver bdrv_gluster_rdma = {
|
|||||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_co_truncate = qemu_gluster_co_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
|
|||||||
893
block/io.c
893
block/io.c
File diff suppressed because it is too large
Load Diff
370
block/iscsi.c
370
block/iscsi.c
@@ -33,7 +33,6 @@
|
|||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
#include "qemu/bitmap.h"
|
#include "qemu/bitmap.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "scsi/constants.h"
|
#include "scsi/constants.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
@@ -44,7 +43,6 @@
|
|||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "crypto/secret.h"
|
#include "crypto/secret.h"
|
||||||
#include "scsi/utils.h"
|
#include "scsi/utils.h"
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
/* Conflict between scsi/utils.h and libiscsi! :( */
|
/* Conflict between scsi/utils.h and libiscsi! :( */
|
||||||
#define SCSI_XFER_NONE ISCSI_XFER_NONE
|
#define SCSI_XFER_NONE ISCSI_XFER_NONE
|
||||||
@@ -70,7 +68,6 @@ typedef struct IscsiLun {
|
|||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||||
struct scsi_inquiry_block_limits bl;
|
struct scsi_inquiry_block_limits bl;
|
||||||
struct scsi_inquiry_device_designator *dd;
|
|
||||||
unsigned char *zeroblock;
|
unsigned char *zeroblock;
|
||||||
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
||||||
* allocated and which are not. In case a target returns zeros for
|
* allocated and which are not. In case a target returns zeros for
|
||||||
@@ -558,19 +555,8 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
|
|||||||
offset / iscsilun->cluster_size) == size);
|
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
|
static int coroutine_fn
|
||||||
iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *iov, int flags)
|
QEMUIOVector *iov, int flags)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = bs->opaque;
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
@@ -630,7 +616,12 @@ retry:
|
|||||||
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
||||||
iov->niov);
|
iov->niov);
|
||||||
#endif
|
#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) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
@@ -701,7 +692,13 @@ retry:
|
|||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_unlock;
|
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.do_retry) {
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
@@ -735,7 +732,7 @@ retry:
|
|||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
*pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size;
|
*pnum = lbasd->num_blocks * iscsilun->block_size;
|
||||||
|
|
||||||
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
||||||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
||||||
@@ -865,8 +862,13 @@ retry:
|
|||||||
#if LIBISCSI_API_VERSION < (20160603)
|
#if LIBISCSI_API_VERSION < (20160603)
|
||||||
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
||||||
#endif
|
#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) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
iTask.task = NULL;
|
iTask.task = NULL;
|
||||||
@@ -903,7 +905,12 @@ retry:
|
|||||||
return -ENOMEM;
|
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) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
@@ -1135,7 +1142,12 @@ retry:
|
|||||||
goto out_unlock;
|
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) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
@@ -1231,7 +1243,12 @@ retry:
|
|||||||
return -ENOMEM;
|
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 &&
|
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
||||||
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
||||||
@@ -1715,34 +1732,14 @@ static QemuOptsList runtime_opts = {
|
|||||||
.name = "timeout",
|
.name = "timeout",
|
||||||
.type = QEMU_OPT_NUMBER,
|
.type = QEMU_OPT_NUMBER,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "filename",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* 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,
|
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@@ -1754,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
char *initiator_name = NULL;
|
char *initiator_name = NULL;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *transport_name, *portal, *target;
|
const char *transport_name, *portal, *target, *filename;
|
||||||
#if LIBISCSI_API_VERSION >= (20160603)
|
#if LIBISCSI_API_VERSION >= (20160603)
|
||||||
enum iscsi_transport_type transport;
|
enum iscsi_transport_type transport;
|
||||||
#endif
|
#endif
|
||||||
int i, ret = 0, timeout = 0, lun;
|
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);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
@@ -1844,7 +1856,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
iscsi_set_timeout(iscsi, timeout);
|
iscsi_set_timeout(iscsi, timeout);
|
||||||
#else
|
#else
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1878,12 +1890,10 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
/* Check the write protect flag of the LUN if we want to write */
|
/* Check the write protect flag of the LUN if we want to write */
|
||||||
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
|
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
|
||||||
iscsilun->write_protected) {
|
iscsilun->write_protected) {
|
||||||
ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp);
|
error_setg(errp, "Cannot open a write protected LUN as read-write");
|
||||||
if (ret < 0) {
|
ret = -EACCES;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
flags &= ~BDRV_O_RDWR;
|
|
||||||
}
|
|
||||||
|
|
||||||
iscsi_readcapacity_sync(iscsilun, &local_err);
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
@@ -1912,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
struct scsi_task *inq_task;
|
struct scsi_task *inq_task;
|
||||||
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
||||||
struct scsi_inquiry_block_limits *inq_bl;
|
struct scsi_inquiry_block_limits *inq_bl;
|
||||||
struct scsi_inquiry_device_identification *inq_di;
|
|
||||||
switch (inq_vpd->pages[i]) {
|
switch (inq_vpd->pages[i]) {
|
||||||
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
||||||
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||||
@@ -1938,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
sizeof(struct scsi_inquiry_block_limits));
|
sizeof(struct scsi_inquiry_block_limits));
|
||||||
scsi_free_scsi_task(inq_task);
|
scsi_free_scsi_task(inq_task);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1991,7 +1989,7 @@ out:
|
|||||||
}
|
}
|
||||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||||
}
|
}
|
||||||
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2005,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs)
|
|||||||
iscsi_logout_sync(iscsi);
|
iscsi_logout_sync(iscsi);
|
||||||
}
|
}
|
||||||
iscsi_destroy_context(iscsi);
|
iscsi_destroy_context(iscsi);
|
||||||
if (iscsilun->dd) {
|
|
||||||
g_free(iscsilun->dd->designator);
|
|
||||||
g_free(iscsilun->dd);
|
|
||||||
}
|
|
||||||
g_free(iscsilun->zeroblock);
|
g_free(iscsilun->zeroblock);
|
||||||
iscsi_allocmap_free(iscsilun);
|
iscsi_allocmap_free(iscsilun);
|
||||||
qemu_mutex_destroy(&iscsilun->mutex);
|
qemu_mutex_destroy(&iscsilun->mutex);
|
||||||
@@ -2088,7 +2082,7 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
|
static int iscsi_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = bs->opaque;
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
@@ -2149,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt
|
|||||||
} else {
|
} else {
|
||||||
ret = iscsi_open(bs, bs_options, 0, NULL);
|
ret = iscsi_open(bs, bs_options, 0, NULL);
|
||||||
}
|
}
|
||||||
qobject_unref(bs_options);
|
QDECREF(bs_options);
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2190,226 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
|
|||||||
iscsi_allocmap_invalidate(iscsilun);
|
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 read_flags,
|
|
||||||
BdrvRequestFlags write_flags)
|
|
||||||
{
|
|
||||||
return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes,
|
|
||||||
read_flags, write_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, MIN(dd->designator_length, 20));
|
|
||||||
|
|
||||||
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 read_flags,
|
|
||||||
BdrvRequestFlags write_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:
|
|
||||||
|
|
||||||
trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r);
|
|
||||||
g_free(iscsi_task.task);
|
|
||||||
qemu_mutex_unlock(&dst_lun->mutex);
|
|
||||||
g_free(iscsi_task.err_str);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QemuOptsList iscsi_create_opts = {
|
static QemuOptsList iscsi_create_opts = {
|
||||||
.name = "iscsi-create-opts",
|
.name = "iscsi-create-opts",
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
||||||
@@ -2439,16 +2213,14 @@ static BlockDriver bdrv_iscsi = {
|
|||||||
|
|
||||||
.bdrv_getlength = iscsi_getlength,
|
.bdrv_getlength = iscsi_getlength,
|
||||||
.bdrv_get_info = iscsi_get_info,
|
.bdrv_get_info = iscsi_get_info,
|
||||||
.bdrv_co_truncate = iscsi_co_truncate,
|
.bdrv_truncate = iscsi_truncate,
|
||||||
.bdrv_refresh_limits = iscsi_refresh_limits,
|
.bdrv_refresh_limits = iscsi_refresh_limits,
|
||||||
|
|
||||||
.bdrv_co_block_status = iscsi_co_block_status,
|
.bdrv_co_block_status = iscsi_co_block_status,
|
||||||
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
.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_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
||||||
.bdrv_co_readv = iscsi_co_readv,
|
.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,
|
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@@ -2472,20 +2244,18 @@ static BlockDriver bdrv_iser = {
|
|||||||
.create_opts = &iscsi_create_opts,
|
.create_opts = &iscsi_create_opts,
|
||||||
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
||||||
.bdrv_reopen_commit = iscsi_reopen_commit,
|
.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_getlength = iscsi_getlength,
|
||||||
.bdrv_get_info = iscsi_get_info,
|
.bdrv_get_info = iscsi_get_info,
|
||||||
.bdrv_co_truncate = iscsi_co_truncate,
|
.bdrv_truncate = iscsi_truncate,
|
||||||
.bdrv_refresh_limits = iscsi_refresh_limits,
|
.bdrv_refresh_limits = iscsi_refresh_limits,
|
||||||
|
|
||||||
.bdrv_co_block_status = iscsi_co_block_status,
|
.bdrv_co_block_status = iscsi_co_block_status,
|
||||||
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
.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_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
||||||
.bdrv_co_readv = iscsi_co_readv,
|
.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,
|
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "block/raw-aio.h"
|
#include "block/raw-aio.h"
|
||||||
#include "qemu/event_notifier.h"
|
#include "qemu/event_notifier.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
#include "qapi/error.h"
|
|
||||||
|
|
||||||
#include <libaio.h>
|
#include <libaio.h>
|
||||||
|
|
||||||
@@ -234,9 +233,9 @@ static void qemu_laio_process_completions(LinuxAioState *s)
|
|||||||
|
|
||||||
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
|
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
|
||||||
{
|
{
|
||||||
aio_context_acquire(s->aio_context);
|
|
||||||
qemu_laio_process_completions(s);
|
qemu_laio_process_completions(s);
|
||||||
|
|
||||||
|
aio_context_acquire(s->aio_context);
|
||||||
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
|
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
|
||||||
ioq_submit(s);
|
ioq_submit(s);
|
||||||
}
|
}
|
||||||
@@ -471,21 +470,16 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
|
|||||||
qemu_laio_poll_cb);
|
qemu_laio_poll_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinuxAioState *laio_init(Error **errp)
|
LinuxAioState *laio_init(void)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
LinuxAioState *s;
|
LinuxAioState *s;
|
||||||
|
|
||||||
s = g_malloc0(sizeof(*s));
|
s = g_malloc0(sizeof(*s));
|
||||||
rc = event_notifier_init(&s->e, false);
|
if (event_notifier_init(&s->e, false) < 0) {
|
||||||
if (rc < 0) {
|
|
||||||
error_setg_errno(errp, -rc, "failed to to initialize event notifier");
|
|
||||||
goto out_free_state;
|
goto out_free_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = io_setup(MAX_EVENTS, &s->ctx);
|
if (io_setup(MAX_EVENTS, &s->ctx) != 0) {
|
||||||
if (rc < 0) {
|
|
||||||
error_setg_errno(errp, -rc, "failed to create linux AIO context");
|
|
||||||
goto out_close_efd;
|
goto out_close_efd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
828
block/mirror.c
828
block/mirror.c
File diff suppressed because it is too large
Load Diff
@@ -228,52 +228,6 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
|||||||
return 0;
|
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
|
/* nbd_parse_error_payload
|
||||||
* on success @errp contains message describing nbd error reply
|
* 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 {
|
typedef struct NBDReplyChunkIter {
|
||||||
int ret;
|
int ret;
|
||||||
bool fatal;
|
|
||||||
Error *err;
|
Error *err;
|
||||||
bool done, only_structured;
|
bool done, only_structured;
|
||||||
} NBDReplyChunkIter;
|
} NBDReplyChunkIter;
|
||||||
@@ -537,12 +490,11 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
|
|||||||
{
|
{
|
||||||
assert(ret < 0);
|
assert(ret < 0);
|
||||||
|
|
||||||
if ((fatal && !iter->fatal) || iter->ret == 0) {
|
if (fatal || iter->ret == 0) {
|
||||||
if (iter->ret != 0) {
|
if (iter->ret != 0) {
|
||||||
error_free(iter->err);
|
error_free(iter->err);
|
||||||
iter->err = NULL;
|
iter->err = NULL;
|
||||||
}
|
}
|
||||||
iter->fatal = fatal;
|
|
||||||
iter->ret = ret;
|
iter->ret = ret;
|
||||||
error_propagate(&iter->err, *local_err);
|
error_propagate(&iter->err, *local_err);
|
||||||
} else {
|
} else {
|
||||||
@@ -688,68 +640,6 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
|||||||
return iter.ret;
|
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,
|
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
|
||||||
QEMUIOVector *write_qiov)
|
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);
|
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)
|
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
@@ -970,7 +815,6 @@ int nbd_client_init(BlockDriverState *bs,
|
|||||||
const char *export,
|
const char *export,
|
||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
const char *x_dirty_bitmap,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
@@ -982,21 +826,18 @@ int nbd_client_init(BlockDriverState *bs,
|
|||||||
|
|
||||||
client->info.request_sizes = true;
|
client->info.request_sizes = true;
|
||||||
client->info.structured_reply = true;
|
client->info.structured_reply = true;
|
||||||
client->info.base_allocation = true;
|
|
||||||
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
|
|
||||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
||||||
tlscreds, hostname,
|
tlscreds, hostname,
|
||||||
&client->ioc, &client->info, errp);
|
&client->ioc, &client->info, errp);
|
||||||
g_free(client->info.x_dirty_bitmap);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
logout("Failed to negotiate with the NBD server\n");
|
logout("Failed to negotiate with the NBD server\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (client->info.flags & NBD_FLAG_READ_ONLY) {
|
if (client->info.flags & NBD_FLAG_READ_ONLY &&
|
||||||
ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
|
!bdrv_is_read_only(bs)) {
|
||||||
if (ret < 0) {
|
error_setg(errp,
|
||||||
return ret;
|
"request for write access conflicts with read-only export");
|
||||||
}
|
return -EACCES;
|
||||||
}
|
}
|
||||||
if (client->info.flags & NBD_FLAG_SEND_FUA) {
|
if (client->info.flags & NBD_FLAG_SEND_FUA) {
|
||||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ int nbd_client_init(BlockDriverState *bs,
|
|||||||
const char *export_name,
|
const char *export_name,
|
||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
const char *x_dirty_bitmap,
|
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void nbd_client_close(BlockDriverState *bs);
|
void nbd_client_close(BlockDriverState *bs);
|
||||||
|
|
||||||
@@ -62,10 +61,4 @@ void nbd_client_detach_aio_context(BlockDriverState *bs);
|
|||||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||||
AioContext *new_context);
|
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 */
|
#endif /* NBD_CLIENT_H */
|
||||||
|
|||||||
35
block/nbd.c
35
block/nbd.c
@@ -27,8 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "nbd-client.h"
|
#include "block/nbd-client.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/uri.h"
|
#include "qemu/uri.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
@@ -109,7 +108,7 @@ static int nbd_parse_uri(const char *filename, QDict *options)
|
|||||||
/* strip braces from literal IPv6 address */
|
/* strip braces from literal IPv6 address */
|
||||||
if (uri->server[0] == '[') {
|
if (uri->server[0] == '[') {
|
||||||
host = qstring_from_substr(uri->server, 1,
|
host = qstring_from_substr(uri->server, 1,
|
||||||
strlen(uri->server) - 1);
|
strlen(uri->server) - 2);
|
||||||
} else {
|
} else {
|
||||||
host = qstring_from_str(uri->server);
|
host = qstring_from_str(uri->server);
|
||||||
}
|
}
|
||||||
@@ -263,6 +262,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
|||||||
{
|
{
|
||||||
SocketAddress *saddr = NULL;
|
SocketAddress *saddr = NULL;
|
||||||
QDict *addr = NULL;
|
QDict *addr = NULL;
|
||||||
|
QObject *crumpled_addr = NULL;
|
||||||
Visitor *iv = NULL;
|
Visitor *iv = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
@@ -272,11 +272,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv = qobject_input_visitor_new_flat_confused(addr, errp);
|
crumpled_addr = qdict_crumple(addr, errp);
|
||||||
if (!iv) {
|
if (!crumpled_addr) {
|
||||||
goto done;
|
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);
|
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@@ -284,7 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
qobject_unref(addr);
|
QDECREF(addr);
|
||||||
|
qobject_decref(crumpled_addr);
|
||||||
visit_free(iv);
|
visit_free(iv);
|
||||||
return saddr;
|
return saddr;
|
||||||
}
|
}
|
||||||
@@ -378,12 +388,6 @@ static QemuOptsList nbd_runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "ID of the TLS credentials to use",
|
.help = "ID of the TLS credentials to use",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.name = "x-dirty-bitmap",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "experimental: expose named dirty bitmap in place of "
|
|
||||||
"block status",
|
|
||||||
},
|
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -444,8 +448,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* NBD handshake */
|
/* NBD handshake */
|
||||||
ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname,
|
ret = nbd_client_init(bs, sioc, s->export,
|
||||||
qemu_opt_get(opts, "x-dirty-bitmap"), errp);
|
tlscreds, hostname, errp);
|
||||||
error:
|
error:
|
||||||
if (sioc) {
|
if (sioc) {
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
@@ -581,7 +585,6 @@ static BlockDriver bdrv_nbd = {
|
|||||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd_tcp = {
|
static BlockDriver bdrv_nbd_tcp = {
|
||||||
@@ -601,7 +604,6 @@ static BlockDriver bdrv_nbd_tcp = {
|
|||||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd_unix = {
|
static BlockDriver bdrv_nbd_unix = {
|
||||||
@@ -621,7 +623,6 @@ static BlockDriver bdrv_nbd_unix = {
|
|||||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_nbd_init(void)
|
static void bdrv_nbd_init(void)
|
||||||
|
|||||||
23
block/nfs.c
23
block/nfs.c
@@ -29,7 +29,6 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
@@ -556,29 +555,24 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevOptionsNfs *opts = NULL;
|
BlockdevOptionsNfs *opts = NULL;
|
||||||
|
QObject *crumpled = NULL;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
const QDictEntry *e;
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
crumpled = qdict_crumple(options, errp);
|
||||||
if (!v) {
|
if (crumpled == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v = qobject_input_visitor_new_keyval(crumpled);
|
||||||
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
|
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
|
qobject_decref(crumpled);
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return NULL;
|
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;
|
return opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
qobject_unref(options);
|
QDECREF(options);
|
||||||
qapi_free_BlockdevCreateOptions(create_options);
|
qapi_free_BlockdevCreateOptions(create_options);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -743,8 +737,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
|
|||||||
return (task.ret < 0 ? task.ret : st.st_blocks * 512);
|
return (task.ret < 0 ? task.ret : st.st_blocks * 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn
|
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
nfs_file_co_truncate(BlockDriverState *bs, int64_t offset,
|
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
NFSClient *client = bs->opaque;
|
NFSClient *client = bs->opaque;
|
||||||
@@ -874,7 +867,7 @@ static BlockDriver bdrv_nfs = {
|
|||||||
|
|
||||||
.bdrv_has_zero_init = nfs_has_zero_init,
|
.bdrv_has_zero_init = nfs_has_zero_init,
|
||||||
.bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
|
||||||
.bdrv_co_truncate = nfs_file_co_truncate,
|
.bdrv_truncate = nfs_file_truncate,
|
||||||
|
|
||||||
.bdrv_file_open = nfs_file_open,
|
.bdrv_file_open = nfs_file_open,
|
||||||
.bdrv_close = nfs_file_close,
|
.bdrv_close = nfs_file_close,
|
||||||
|
|||||||
46
block/null.c
46
block/null.c
@@ -93,10 +93,13 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
|
s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void null_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t null_getlength(BlockDriverState *bs)
|
static int64_t null_getlength(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVNullState *s = bs->opaque;
|
BDRVNullState *s = bs->opaque;
|
||||||
@@ -113,22 +116,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int null_co_preadv(BlockDriverState *bs,
|
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *qiov, int flags)
|
QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
BDRVNullState *s = bs->opaque;
|
BDRVNullState *s = bs->opaque;
|
||||||
|
|
||||||
if (s->read_zeroes) {
|
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);
|
return null_co_common(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int null_co_pwritev(BlockDriverState *bs,
|
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *qiov, int flags)
|
QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
return null_co_common(bs);
|
return null_co_common(bs);
|
||||||
}
|
}
|
||||||
@@ -183,24 +186,24 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
|||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
|
static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num, QEMUIOVector *qiov,
|
||||||
QEMUIOVector *qiov, int flags,
|
int nb_sectors,
|
||||||
BlockCompletionFunc *cb,
|
BlockCompletionFunc *cb,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
BDRVNullState *s = bs->opaque;
|
BDRVNullState *s = bs->opaque;
|
||||||
|
|
||||||
if (s->read_zeroes) {
|
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);
|
return null_aio_common(bs, cb, opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs,
|
static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num, QEMUIOVector *qiov,
|
||||||
QEMUIOVector *qiov, int flags,
|
int nb_sectors,
|
||||||
BlockCompletionFunc *cb,
|
BlockCompletionFunc *cb,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
@@ -241,6 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
|
|||||||
|
|
||||||
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||||
{
|
{
|
||||||
|
QINCREF(opts);
|
||||||
qdict_del(opts, "filename");
|
qdict_del(opts, "filename");
|
||||||
|
|
||||||
if (!qdict_size(opts)) {
|
if (!qdict_size(opts)) {
|
||||||
@@ -249,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qdict_put_str(opts, "driver", bs->drv->format_name);
|
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 = {
|
static BlockDriver bdrv_null_co = {
|
||||||
@@ -259,10 +263,11 @@ static BlockDriver bdrv_null_co = {
|
|||||||
|
|
||||||
.bdrv_file_open = null_file_open,
|
.bdrv_file_open = null_file_open,
|
||||||
.bdrv_parse_filename = null_co_parse_filename,
|
.bdrv_parse_filename = null_co_parse_filename,
|
||||||
|
.bdrv_close = null_close,
|
||||||
.bdrv_getlength = null_getlength,
|
.bdrv_getlength = null_getlength,
|
||||||
|
|
||||||
.bdrv_co_preadv = null_co_preadv,
|
.bdrv_co_readv = null_co_readv,
|
||||||
.bdrv_co_pwritev = null_co_pwritev,
|
.bdrv_co_writev = null_co_writev,
|
||||||
.bdrv_co_flush_to_disk = null_co_flush,
|
.bdrv_co_flush_to_disk = null_co_flush,
|
||||||
.bdrv_reopen_prepare = null_reopen_prepare,
|
.bdrv_reopen_prepare = null_reopen_prepare,
|
||||||
|
|
||||||
@@ -278,10 +283,11 @@ static BlockDriver bdrv_null_aio = {
|
|||||||
|
|
||||||
.bdrv_file_open = null_file_open,
|
.bdrv_file_open = null_file_open,
|
||||||
.bdrv_parse_filename = null_aio_parse_filename,
|
.bdrv_parse_filename = null_aio_parse_filename,
|
||||||
|
.bdrv_close = null_close,
|
||||||
.bdrv_getlength = null_getlength,
|
.bdrv_getlength = null_getlength,
|
||||||
|
|
||||||
.bdrv_aio_preadv = null_aio_preadv,
|
.bdrv_aio_readv = null_aio_readv,
|
||||||
.bdrv_aio_pwritev = null_aio_pwritev,
|
.bdrv_aio_writev = null_aio_writev,
|
||||||
.bdrv_aio_flush = null_aio_flush,
|
.bdrv_aio_flush = null_aio_flush,
|
||||||
.bdrv_reopen_prepare = null_reopen_prepare,
|
.bdrv_reopen_prepare = null_reopen_prepare,
|
||||||
|
|
||||||
|
|||||||
61
block/nvme.c
61
block/nvme.c
@@ -104,7 +104,7 @@ typedef struct {
|
|||||||
uint64_t nsze; /* Namespace size reported by identify command */
|
uint64_t nsze; /* Namespace size reported by identify command */
|
||||||
int nsid; /* The namespace id to read/write data. */
|
int nsid; /* The namespace id to read/write data. */
|
||||||
uint64_t max_transfer;
|
uint64_t max_transfer;
|
||||||
bool plugged;
|
int plugged;
|
||||||
|
|
||||||
CoMutex dma_map_lock;
|
CoMutex dma_map_lock;
|
||||||
CoQueue dma_flush_queue;
|
CoQueue dma_flush_queue;
|
||||||
@@ -489,8 +489,10 @@ static void nvme_handle_event(EventNotifier *n)
|
|||||||
BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier);
|
BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier);
|
||||||
|
|
||||||
trace_nvme_handle_event(s);
|
trace_nvme_handle_event(s);
|
||||||
|
aio_context_acquire(s->aio_context);
|
||||||
event_notifier_test_and_clear(n);
|
event_notifier_test_and_clear(n);
|
||||||
nvme_poll_queues(s);
|
nvme_poll_queues(s);
|
||||||
|
aio_context_release(s->aio_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
|
static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
|
||||||
@@ -567,13 +569,13 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
|||||||
s->vfio = qemu_vfio_open_pci(device, errp);
|
s->vfio = qemu_vfio_open_pci(device, errp);
|
||||||
if (!s->vfio) {
|
if (!s->vfio) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp);
|
s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp);
|
||||||
if (!s->regs) {
|
if (!s->regs) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform initialize sequence as described in NVMe spec "7.6.1
|
/* Perform initialize sequence as described in NVMe spec "7.6.1
|
||||||
@@ -583,7 +585,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
|||||||
if (!(cap & (1ULL << 37))) {
|
if (!(cap & (1ULL << 37))) {
|
||||||
error_setg(errp, "Device doesn't support NVMe command set");
|
error_setg(errp, "Device doesn't support NVMe command set");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF)));
|
s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF)));
|
||||||
@@ -601,7 +603,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
|||||||
PRId64 " ms)",
|
PRId64 " ms)",
|
||||||
timeout_ms);
|
timeout_ms);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
goto out;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +613,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
|||||||
s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp);
|
s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp);
|
||||||
if (!s->queues[0]) {
|
if (!s->queues[0]) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto fail;
|
||||||
}
|
}
|
||||||
QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000);
|
QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000);
|
||||||
s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE);
|
s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE);
|
||||||
@@ -631,14 +633,14 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
|||||||
PRId64 " ms)",
|
PRId64 " ms)",
|
||||||
timeout_ms);
|
timeout_ms);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
goto out;
|
goto fail_queue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier,
|
ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier,
|
||||||
VFIO_PCI_MSIX_IRQ_INDEX, errp);
|
VFIO_PCI_MSIX_IRQ_INDEX, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto out;
|
goto fail_queue;
|
||||||
}
|
}
|
||||||
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
||||||
false, nvme_handle_event, nvme_poll_cb);
|
false, nvme_handle_event, nvme_poll_cb);
|
||||||
@@ -647,15 +649,30 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
|||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out;
|
goto fail_handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up command queues. */
|
/* Set up command queues. */
|
||||||
if (!nvme_add_io_queue(bs, errp)) {
|
if (!nvme_add_io_queue(bs, errp)) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
goto fail_handler;
|
||||||
}
|
}
|
||||||
out:
|
return 0;
|
||||||
/* Cleaning up is done in nvme_file_open() upon error. */
|
|
||||||
|
fail_handler:
|
||||||
|
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
||||||
|
false, NULL, NULL);
|
||||||
|
fail_queue:
|
||||||
|
nvme_free_queue_pair(bs, s->queues[0]);
|
||||||
|
fail:
|
||||||
|
g_free(s->queues);
|
||||||
|
if (s->regs) {
|
||||||
|
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
|
||||||
|
}
|
||||||
|
if (s->vfio) {
|
||||||
|
qemu_vfio_close(s->vfio);
|
||||||
|
}
|
||||||
|
event_notifier_cleanup(&s->irq_notifier);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,11 +695,12 @@ static void nvme_parse_filename(const char *filename, QDict *options,
|
|||||||
unsigned long ns;
|
unsigned long ns;
|
||||||
const char *slash = strchr(tmp, '/');
|
const char *slash = strchr(tmp, '/');
|
||||||
if (!slash) {
|
if (!slash) {
|
||||||
qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp);
|
qdict_put(options, NVME_BLOCK_OPT_DEVICE,
|
||||||
|
qstring_from_str(tmp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
device = g_strndup(tmp, slash - tmp);
|
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);
|
g_free(device);
|
||||||
namespace = slash + 1;
|
namespace = slash + 1;
|
||||||
if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) {
|
if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) {
|
||||||
@@ -690,8 +708,8 @@ static void nvme_parse_filename(const char *filename, QDict *options,
|
|||||||
namespace);
|
namespace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE,
|
qdict_put(options, NVME_BLOCK_OPT_NAMESPACE,
|
||||||
*namespace ? namespace : "1");
|
qstring_from_str(*namespace ? namespace : "1"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,10 +740,8 @@ static void nvme_close(BlockDriverState *bs)
|
|||||||
for (i = 0; i < s->nr_queues; ++i) {
|
for (i = 0; i < s->nr_queues; ++i) {
|
||||||
nvme_free_queue_pair(bs, s->queues[i]);
|
nvme_free_queue_pair(bs, s->queues[i]);
|
||||||
}
|
}
|
||||||
g_free(s->queues);
|
|
||||||
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
||||||
false, NULL, NULL);
|
false, NULL, NULL);
|
||||||
event_notifier_cleanup(&s->irq_notifier);
|
|
||||||
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
|
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
|
||||||
qemu_vfio_close(s->vfio);
|
qemu_vfio_close(s->vfio);
|
||||||
}
|
}
|
||||||
@@ -1058,6 +1074,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
|
|||||||
|
|
||||||
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||||
{
|
{
|
||||||
|
QINCREF(opts);
|
||||||
qdict_del(opts, "filename");
|
qdict_del(opts, "filename");
|
||||||
|
|
||||||
if (!qdict_size(opts)) {
|
if (!qdict_size(opts)) {
|
||||||
@@ -1065,8 +1082,8 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
|||||||
bs->drv->format_name);
|
bs->drv->format_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put_str(opts, "driver", bs->drv->format_name);
|
qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
|
||||||
bs->full_open_options = qobject_ref(opts);
|
bs->full_open_options = opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
|
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||||
@@ -1099,8 +1116,7 @@ static void nvme_attach_aio_context(BlockDriverState *bs,
|
|||||||
static void nvme_aio_plug(BlockDriverState *bs)
|
static void nvme_aio_plug(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVNVMeState *s = bs->opaque;
|
BDRVNVMeState *s = bs->opaque;
|
||||||
assert(!s->plugged);
|
s->plugged++;
|
||||||
s->plugged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_aio_unplug(BlockDriverState *bs)
|
static void nvme_aio_unplug(BlockDriverState *bs)
|
||||||
@@ -1108,7 +1124,7 @@ static void nvme_aio_unplug(BlockDriverState *bs)
|
|||||||
int i;
|
int i;
|
||||||
BDRVNVMeState *s = bs->opaque;
|
BDRVNVMeState *s = bs->opaque;
|
||||||
assert(s->plugged);
|
assert(s->plugged);
|
||||||
s->plugged = false;
|
if (!--s->plugged) {
|
||||||
for (i = 1; i < s->nr_queues; i++) {
|
for (i = 1; i < s->nr_queues; i++) {
|
||||||
NVMeQueuePair *q = s->queues[i];
|
NVMeQueuePair *q = s->queues[i];
|
||||||
qemu_mutex_lock(&q->lock);
|
qemu_mutex_lock(&q->lock);
|
||||||
@@ -1116,6 +1132,7 @@ static void nvme_aio_unplug(BlockDriverState *bs)
|
|||||||
nvme_process_completion(s, q);
|
nvme_process_completion(s, q);
|
||||||
qemu_mutex_unlock(&q->lock);
|
qemu_mutex_unlock(&q->lock);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size)
|
static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size)
|
||||||
|
|||||||
@@ -31,13 +31,9 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.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/bswap.h"
|
||||||
#include "qemu/bitmap.h"
|
#include "qemu/bitmap.h"
|
||||||
#include "migration/blocker.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)
|
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
|
||||||
{
|
{
|
||||||
@@ -227,15 +204,14 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
|||||||
};
|
};
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||||
|
|
||||||
ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors,
|
||||||
nb_cow_bytes, &qiov, 0);
|
&qiov);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qemu_vfree(iov.iov_base);
|
qemu_vfree(iov.iov_base);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov);
|
||||||
nb_cow_bytes, &qiov, 0);
|
|
||||||
qemu_vfree(iov.iov_base);
|
qemu_vfree(iov.iov_base);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
@@ -313,15 +289,13 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
|
static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||||
QEMUIOVector *qiov, int flags)
|
|
||||||
{
|
{
|
||||||
BDRVParallelsState *s = bs->opaque;
|
BDRVParallelsState *s = bs->opaque;
|
||||||
uint64_t bytes_done = 0;
|
uint64_t bytes_done = 0;
|
||||||
QEMUIOVector hd_qiov;
|
QEMUIOVector hd_qiov;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
assert(!flags);
|
|
||||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||||
|
|
||||||
while (nb_sectors > 0) {
|
while (nb_sectors > 0) {
|
||||||
@@ -341,8 +315,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
|
|||||||
qemu_iovec_reset(&hd_qiov);
|
qemu_iovec_reset(&hd_qiov);
|
||||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
|
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
|
||||||
|
|
||||||
ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
|
ret = bdrv_co_writev(bs->file, position, n, &hd_qiov);
|
||||||
&hd_qiov, 0);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -381,8 +354,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (position < 0) {
|
if (position < 0) {
|
||||||
if (bs->backing) {
|
if (bs->backing) {
|
||||||
ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
|
||||||
nbytes, &hd_qiov, 0);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -390,8 +362,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
|
|||||||
qemu_iovec_memset(&hd_qiov, 0, 0, nbytes);
|
qemu_iovec_memset(&hd_qiov, 0, 0, nbytes);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
|
ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
|
||||||
&hd_qiov, 0);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -509,67 +480,46 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
static int coroutine_fn parallels_co_create_opts(const char *filename,
|
||||||
|
QemuOpts *opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevCreateOptionsParallels *parallels_opts;
|
|
||||||
BlockDriverState *bs;
|
|
||||||
BlockBackend *blk;
|
|
||||||
int64_t total_size, cl_size;
|
int64_t total_size, cl_size;
|
||||||
|
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||||
|
Error *local_err = NULL;
|
||||||
|
BlockBackend *file;
|
||||||
uint32_t bat_entries, bat_sectors;
|
uint32_t bat_entries, bat_sectors;
|
||||||
ParallelsHeader header;
|
ParallelsHeader header;
|
||||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
parallels_opts = &opts->u.parallels;
|
BDRV_SECTOR_SIZE);
|
||||||
|
cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||||
/* Sanity checks */
|
DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_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;
|
return -E2BIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
|
ret = bdrv_create_file(filename, opts, &local_err);
|
||||||
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
if (ret < 0) {
|
||||||
return -EINVAL;
|
error_propagate(errp, local_err);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
|
file = blk_new_open(filename, NULL, NULL,
|
||||||
error_setg(errp, "Cluster size must be a multiple of 512 bytes");
|
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||||
return -EINVAL;
|
&local_err);
|
||||||
}
|
if (file == NULL) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
/* Create BlockBackend to write to the image */
|
|
||||||
bs = bdrv_open_blockdev_ref(parallels_opts->file, errp);
|
|
||||||
if (bs == NULL) {
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
blk_set_allow_write_beyond_eof(file, true);
|
||||||
ret = blk_insert_bs(blk, bs, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
blk_set_allow_write_beyond_eof(blk, true);
|
|
||||||
|
|
||||||
/* Create image format */
|
ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
|
||||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bat_entries = DIV_ROUND_UP(total_size, cl_size);
|
bat_entries = DIV_ROUND_UP(total_size, cl_size);
|
||||||
@@ -592,103 +542,24 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
|||||||
memset(tmp, 0, sizeof(tmp));
|
memset(tmp, 0, sizeof(tmp));
|
||||||
memcpy(tmp, &header, sizeof(header));
|
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) {
|
if (ret < 0) {
|
||||||
goto exit;
|
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);
|
(bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
|
||||||
blk_unref(blk);
|
done:
|
||||||
bdrv_unref(bs);
|
blk_unref(file);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
error_setg_errno(errp, -ret, "Failed to create Parallels image");
|
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;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -900,6 +771,25 @@ static void parallels_close(BlockDriverState *bs)
|
|||||||
error_free(s->migration_blocker);
|
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 = {
|
static BlockDriver bdrv_parallels = {
|
||||||
.format_name = "parallels",
|
.format_name = "parallels",
|
||||||
.instance_size = sizeof(BDRVParallelsState),
|
.instance_size = sizeof(BDRVParallelsState),
|
||||||
@@ -913,7 +803,6 @@ static BlockDriver bdrv_parallels = {
|
|||||||
.bdrv_co_readv = parallels_co_readv,
|
.bdrv_co_readv = parallels_co_readv,
|
||||||
.bdrv_co_writev = parallels_co_writev,
|
.bdrv_co_writev = parallels_co_writev,
|
||||||
.supports_backing = true,
|
.supports_backing = true,
|
||||||
.bdrv_co_create = parallels_co_create,
|
|
||||||
.bdrv_co_create_opts = parallels_co_create_opts,
|
.bdrv_co_create_opts = parallels_co_create_opts,
|
||||||
.bdrv_co_check = parallels_co_check,
|
.bdrv_co_check = parallels_co_check,
|
||||||
.create_opts = ¶llels_create_opts,
|
.create_opts = ¶llels_create_opts,
|
||||||
|
|||||||
74
block/qapi.c
74
block/qapi.c
@@ -394,37 +394,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
|||||||
qapi_free_BlockInfo(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)
|
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockAcctStats *stats = blk_get_stats(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 =
|
dev_stats->avg_wr_queue_depth =
|
||||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
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,
|
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
|
||||||
@@ -593,33 +552,18 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
|||||||
p_next = &info->next;
|
p_next = &info->next;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
||||||
BlockStatsList *info;
|
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||||
AioContext *ctx = blk_get_aio_context(blk);
|
AioContext *ctx = blk_get_aio_context(blk);
|
||||||
BlockStats *s;
|
BlockStats *s;
|
||||||
char *qdev;
|
|
||||||
|
|
||||||
if (!*blk_name(blk) && !blk_get_attached_dev(blk)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
aio_context_acquire(ctx);
|
aio_context_acquire(ctx);
|
||||||
s = bdrv_query_bds_stats(blk_bs(blk), true);
|
s = bdrv_query_bds_stats(blk_bs(blk), true);
|
||||||
s->has_device = true;
|
s->has_device = true;
|
||||||
s->device = g_strdup(blk_name(blk));
|
s->device = g_strdup(blk_name(blk));
|
||||||
|
|
||||||
qdev = blk_get_attached_dev_id(blk);
|
|
||||||
if (qdev && *qdev) {
|
|
||||||
s->has_qdev = true;
|
|
||||||
s->qdev = qdev;
|
|
||||||
} else {
|
|
||||||
g_free(qdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
bdrv_query_blk_stats(s->stats, blk);
|
bdrv_query_blk_stats(s->stats, blk);
|
||||||
aio_context_release(ctx);
|
aio_context_release(ctx);
|
||||||
|
|
||||||
info = g_malloc0(sizeof(*info));
|
|
||||||
info->value = s;
|
info->value = s;
|
||||||
*p_next = info;
|
*p_next = info;
|
||||||
p_next = &info->next;
|
p_next = &info->next;
|
||||||
@@ -703,29 +647,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
|||||||
{
|
{
|
||||||
switch (qobject_type(obj)) {
|
switch (qobject_type(obj)) {
|
||||||
case QTYPE_QNUM: {
|
case QTYPE_QNUM: {
|
||||||
QNum *value = qobject_to(QNum, obj);
|
QNum *value = qobject_to_qnum(obj);
|
||||||
char *tmp = qnum_to_string(value);
|
char *tmp = qnum_to_string(value);
|
||||||
func_fprintf(f, "%s", tmp);
|
func_fprintf(f, "%s", tmp);
|
||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_QSTRING: {
|
case QTYPE_QSTRING: {
|
||||||
QString *value = qobject_to(QString, obj);
|
QString *value = qobject_to_qstring(obj);
|
||||||
func_fprintf(f, "%s", qstring_get_str(value));
|
func_fprintf(f, "%s", qstring_get_str(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_QDICT: {
|
case QTYPE_QDICT: {
|
||||||
QDict *value = qobject_to(QDict, obj);
|
QDict *value = qobject_to_qdict(obj);
|
||||||
dump_qdict(func_fprintf, f, comp_indent, value);
|
dump_qdict(func_fprintf, f, comp_indent, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_QLIST: {
|
case QTYPE_QLIST: {
|
||||||
QList *value = qobject_to(QList, obj);
|
QList *value = qobject_to_qlist(obj);
|
||||||
dump_qlist(func_fprintf, f, comp_indent, value);
|
dump_qlist(func_fprintf, f, comp_indent, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_QBOOL: {
|
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");
|
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -786,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_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
|
||||||
visit_complete(v, &obj);
|
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);
|
dump_qobject(func_fprintf, f, 1, data);
|
||||||
qobject_unref(obj);
|
qobject_decref(obj);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
353
block/qcow.c
353
block/qcow.c
@@ -26,7 +26,6 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
@@ -34,11 +33,9 @@
|
|||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "qapi/qobject-input-visitor.h"
|
|
||||||
#include "qapi/qapi-visit-block-core.h"
|
|
||||||
#include "crypto/block.h"
|
#include "crypto/block.h"
|
||||||
#include "migration/blocker.h"
|
#include "migration/blocker.h"
|
||||||
#include "crypto.h"
|
#include "block/crypto.h"
|
||||||
|
|
||||||
/**************************************************************/
|
/**************************************************************/
|
||||||
/* QEMU COW block driver with compression and encryption support */
|
/* QEMU COW block driver with compression and encryption support */
|
||||||
@@ -70,6 +67,7 @@ typedef struct QCowHeader {
|
|||||||
typedef struct BDRVQcowState {
|
typedef struct BDRVQcowState {
|
||||||
int cluster_bits;
|
int cluster_bits;
|
||||||
int cluster_size;
|
int cluster_size;
|
||||||
|
int cluster_sectors;
|
||||||
int l2_bits;
|
int l2_bits;
|
||||||
int l2_size;
|
int l2_size;
|
||||||
unsigned int l1_size;
|
unsigned int l1_size;
|
||||||
@@ -88,8 +86,6 @@ typedef struct BDRVQcowState {
|
|||||||
Error *migration_blocker;
|
Error *migration_blocker;
|
||||||
} BDRVQcowState;
|
} BDRVQcowState;
|
||||||
|
|
||||||
static QemuOptsList qcow_create_opts;
|
|
||||||
|
|
||||||
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||||
|
|
||||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
@@ -140,14 +136,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
header.magic = be32_to_cpu(header.magic);
|
be32_to_cpus(&header.magic);
|
||||||
header.version = be32_to_cpu(header.version);
|
be32_to_cpus(&header.version);
|
||||||
header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
|
be64_to_cpus(&header.backing_file_offset);
|
||||||
header.backing_file_size = be32_to_cpu(header.backing_file_size);
|
be32_to_cpus(&header.backing_file_size);
|
||||||
header.mtime = be32_to_cpu(header.mtime);
|
be32_to_cpus(&header.mtime);
|
||||||
header.size = be64_to_cpu(header.size);
|
be64_to_cpus(&header.size);
|
||||||
header.crypt_method = be32_to_cpu(header.crypt_method);
|
be32_to_cpus(&header.crypt_method);
|
||||||
header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
|
be64_to_cpus(&header.l1_table_offset);
|
||||||
|
|
||||||
if (header.magic != QCOW_MAGIC) {
|
if (header.magic != QCOW_MAGIC) {
|
||||||
error_setg(errp, "Image not in qcow format");
|
error_setg(errp, "Image not in qcow format");
|
||||||
@@ -202,8 +198,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
qdict_put_str(encryptopts, "format", "qcow");
|
qdict_del(encryptopts, "format");
|
||||||
crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
|
crypto_opts = block_crypto_open_opts_init(
|
||||||
|
Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
|
||||||
if (!crypto_opts) {
|
if (!crypto_opts) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -234,6 +231,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
s->cluster_bits = header.cluster_bits;
|
s->cluster_bits = header.cluster_bits;
|
||||||
s->cluster_size = 1 << s->cluster_bits;
|
s->cluster_size = 1 << s->cluster_bits;
|
||||||
|
s->cluster_sectors = 1 << (s->cluster_bits - 9);
|
||||||
s->l2_bits = header.l2_bits;
|
s->l2_bits = header.l2_bits;
|
||||||
s->l2_size = 1 << s->l2_bits;
|
s->l2_size = 1 << s->l2_bits;
|
||||||
bs->total_sectors = header.size / 512;
|
bs->total_sectors = header.size / 512;
|
||||||
@@ -270,7 +268,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0;i < s->l1_size; i++) {
|
for(i = 0;i < s->l1_size; i++) {
|
||||||
s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
|
be64_to_cpus(&s->l1_table[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
||||||
@@ -313,7 +311,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
qobject_unref(encryptopts);
|
QDECREF(encryptopts);
|
||||||
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
||||||
qemu_co_mutex_init(&s->lock);
|
qemu_co_mutex_init(&s->lock);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -324,7 +322,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
g_free(s->cluster_cache);
|
g_free(s->cluster_cache);
|
||||||
g_free(s->cluster_data);
|
g_free(s->cluster_data);
|
||||||
qcrypto_block_free(s->crypto);
|
qcrypto_block_free(s->crypto);
|
||||||
qobject_unref(encryptopts);
|
QDECREF(encryptopts);
|
||||||
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -343,8 +341,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
|
|||||||
*
|
*
|
||||||
* 0 to not allocate.
|
* 0 to not allocate.
|
||||||
*
|
*
|
||||||
* 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start'
|
* 1 to allocate a normal cluster (for sector indexes 'n_start' to
|
||||||
* to 'n_end' within the cluster)
|
* 'n_end')
|
||||||
*
|
*
|
||||||
* 2 to allocate a compressed cluster of size
|
* 2 to allocate a compressed cluster of size
|
||||||
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
||||||
@@ -438,10 +436,9 @@ static int get_cluster_offset(BlockDriverState *bs,
|
|||||||
if (!allocate)
|
if (!allocate)
|
||||||
return 0;
|
return 0;
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
||||||
assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE));
|
|
||||||
/* allocate a new cluster */
|
/* allocate a new cluster */
|
||||||
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
|
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
|
||||||
(n_end - n_start) < s->cluster_size) {
|
(n_end - n_start) < s->cluster_sectors) {
|
||||||
/* if the cluster is already compressed, we must
|
/* if the cluster is already compressed, we must
|
||||||
decompress it in the case it is not completely
|
decompress it in the case it is not completely
|
||||||
overwritten */
|
overwritten */
|
||||||
@@ -479,15 +476,16 @@ static int get_cluster_offset(BlockDriverState *bs,
|
|||||||
/* if encrypted, we must initialize the cluster
|
/* if encrypted, we must initialize the cluster
|
||||||
content which won't be written */
|
content which won't be written */
|
||||||
if (bs->encrypted &&
|
if (bs->encrypted &&
|
||||||
(n_end - n_start) < s->cluster_size) {
|
(n_end - n_start) < s->cluster_sectors) {
|
||||||
uint64_t start_offset;
|
uint64_t start_sect;
|
||||||
assert(s->crypto);
|
assert(s->crypto);
|
||||||
start_offset = offset & ~(s->cluster_size - 1);
|
start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
|
||||||
for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) {
|
for(i = 0; i < s->cluster_sectors; i++) {
|
||||||
if (i < n_start || i >= n_end) {
|
if (i < n_start || i >= n_end) {
|
||||||
memset(s->cluster_data, 0x00, BDRV_SECTOR_SIZE);
|
memset(s->cluster_data, 0x00, 512);
|
||||||
if (qcrypto_block_encrypt(s->crypto,
|
if (qcrypto_block_encrypt(s->crypto,
|
||||||
start_offset + i,
|
(start_sect + i) *
|
||||||
|
BDRV_SECTOR_SIZE,
|
||||||
s->cluster_data,
|
s->cluster_data,
|
||||||
BDRV_SECTOR_SIZE,
|
BDRV_SECTOR_SIZE,
|
||||||
NULL) < 0) {
|
NULL) < 0) {
|
||||||
@@ -495,9 +493,8 @@ static int get_cluster_offset(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||||
ret = bdrv_pwrite(bs->file,
|
ret = bdrv_pwrite(bs->file,
|
||||||
cluster_offset + i,
|
cluster_offset + i * 512,
|
||||||
s->cluster_data,
|
s->cluster_data, 512);
|
||||||
BDRV_SECTOR_SIZE);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -611,21 +608,11 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcow_refresh_limits(BlockDriverState *bs, Error **errp)
|
static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||||
{
|
int nb_sectors, QEMUIOVector *qiov)
|
||||||
/* At least encrypted images require 512-byte alignment. Apply the
|
|
||||||
* limit universally, rather than just on encrypted images, as
|
|
||||||
* it's easier to let the block layer handle rounding than to
|
|
||||||
* audit this code further. */
|
|
||||||
bs->bl.request_alignment = BDRV_SECTOR_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|
||||||
uint64_t bytes, QEMUIOVector *qiov,
|
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
int offset_in_cluster;
|
int index_in_cluster;
|
||||||
int ret = 0, n;
|
int ret = 0, n;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
struct iovec hd_iov;
|
struct iovec hd_iov;
|
||||||
@@ -633,7 +620,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
void *orig_buf;
|
void *orig_buf;
|
||||||
|
|
||||||
assert(!flags);
|
|
||||||
if (qiov->niov > 1) {
|
if (qiov->niov > 1) {
|
||||||
buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
|
buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
@@ -646,35 +632,36 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
|
||||||
while (bytes != 0) {
|
while (nb_sectors != 0) {
|
||||||
/* prepare next request */
|
/* prepare next request */
|
||||||
ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset);
|
ret = get_cluster_offset(bs, sector_num << 9,
|
||||||
|
0, 0, 0, 0, &cluster_offset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
offset_in_cluster = offset & (s->cluster_size - 1);
|
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||||
n = s->cluster_size - offset_in_cluster;
|
n = s->cluster_sectors - index_in_cluster;
|
||||||
if (n > bytes) {
|
if (n > nb_sectors) {
|
||||||
n = bytes;
|
n = nb_sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cluster_offset) {
|
if (!cluster_offset) {
|
||||||
if (bs->backing) {
|
if (bs->backing) {
|
||||||
/* read from the base image */
|
/* read from the base image */
|
||||||
hd_iov.iov_base = (void *)buf;
|
hd_iov.iov_base = (void *)buf;
|
||||||
hd_iov.iov_len = n;
|
hd_iov.iov_len = n * 512;
|
||||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
/* qcow2 emits this on bs->file instead of bs->backing */
|
/* qcow2 emits this on bs->file instead of bs->backing */
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||||
ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0);
|
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Note: in this case, no need to wait */
|
/* Note: in this case, no need to wait */
|
||||||
memset(buf, 0, n);
|
memset(buf, 0, 512 * n);
|
||||||
}
|
}
|
||||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||||
/* add AIO support for compressed blocks ? */
|
/* add AIO support for compressed blocks ? */
|
||||||
@@ -682,19 +669,21 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
memcpy(buf, s->cluster_cache + offset_in_cluster, n);
|
memcpy(buf,
|
||||||
|
s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||||
} else {
|
} else {
|
||||||
if ((cluster_offset & 511) != 0) {
|
if ((cluster_offset & 511) != 0) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hd_iov.iov_base = (void *)buf;
|
hd_iov.iov_base = (void *)buf;
|
||||||
hd_iov.iov_len = n;
|
hd_iov.iov_len = n * 512;
|
||||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||||
ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
|
ret = bdrv_co_readv(bs->file,
|
||||||
n, &hd_qiov, 0);
|
(cluster_offset >> 9) + index_in_cluster,
|
||||||
|
n, &hd_qiov);
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
@@ -702,7 +691,8 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
if (bs->encrypted) {
|
if (bs->encrypted) {
|
||||||
assert(s->crypto);
|
assert(s->crypto);
|
||||||
if (qcrypto_block_decrypt(s->crypto,
|
if (qcrypto_block_decrypt(s->crypto,
|
||||||
offset, buf, n, NULL) < 0) {
|
sector_num * BDRV_SECTOR_SIZE, buf,
|
||||||
|
n * BDRV_SECTOR_SIZE, NULL) < 0) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -710,9 +700,9 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
bytes -= n;
|
nb_sectors -= n;
|
||||||
offset += n;
|
sector_num += n;
|
||||||
buf += n;
|
buf += n * 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
@@ -725,12 +715,11 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||||
uint64_t bytes, QEMUIOVector *qiov,
|
int nb_sectors, QEMUIOVector *qiov)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
int offset_in_cluster;
|
int index_in_cluster;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
int ret = 0, n;
|
int ret = 0, n;
|
||||||
struct iovec hd_iov;
|
struct iovec hd_iov;
|
||||||
@@ -738,7 +727,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
void *orig_buf;
|
void *orig_buf;
|
||||||
|
|
||||||
assert(!flags);
|
|
||||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||||
|
|
||||||
/* We must always copy the iov when encrypting, so we
|
/* We must always copy the iov when encrypting, so we
|
||||||
@@ -756,14 +744,16 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
|
||||||
while (bytes != 0) {
|
while (nb_sectors != 0) {
|
||||||
offset_in_cluster = offset & (s->cluster_size - 1);
|
|
||||||
n = s->cluster_size - offset_in_cluster;
|
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||||
if (n > bytes) {
|
n = s->cluster_sectors - index_in_cluster;
|
||||||
n = bytes;
|
if (n > nb_sectors) {
|
||||||
|
n = nb_sectors;
|
||||||
}
|
}
|
||||||
ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster,
|
ret = get_cluster_offset(bs, sector_num << 9, 1, 0,
|
||||||
offset_in_cluster + n, &cluster_offset);
|
index_in_cluster,
|
||||||
|
index_in_cluster + n, &cluster_offset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -773,28 +763,30 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||||||
}
|
}
|
||||||
if (bs->encrypted) {
|
if (bs->encrypted) {
|
||||||
assert(s->crypto);
|
assert(s->crypto);
|
||||||
if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) {
|
if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE,
|
||||||
|
buf, n * BDRV_SECTOR_SIZE, NULL) < 0) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hd_iov.iov_base = (void *)buf;
|
hd_iov.iov_base = (void *)buf;
|
||||||
hd_iov.iov_len = n;
|
hd_iov.iov_len = n * 512;
|
||||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||||
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
|
ret = bdrv_co_writev(bs->file,
|
||||||
n, &hd_qiov, 0);
|
(cluster_offset >> 9) + index_in_cluster,
|
||||||
|
n, &hd_qiov);
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
bytes -= n;
|
nb_sectors -= n;
|
||||||
offset += n;
|
sector_num += n;
|
||||||
buf += n;
|
buf += n * 512;
|
||||||
}
|
}
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
|
||||||
@@ -818,50 +810,62 @@ static void qcow_close(BlockDriverState *bs)
|
|||||||
error_free(s->migration_blocker);
|
error_free(s->migration_blocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevCreateOptionsQcow *qcow_opts;
|
|
||||||
int header_size, backing_filename_len, l1_size, shift, i;
|
int header_size, backing_filename_len, l1_size, shift, i;
|
||||||
QCowHeader header;
|
QCowHeader header;
|
||||||
uint8_t *tmp;
|
uint8_t *tmp;
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
|
char *backing_file = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
BlockDriverState *bs;
|
|
||||||
BlockBackend *qcow_blk;
|
BlockBackend *qcow_blk;
|
||||||
|
char *encryptfmt = NULL;
|
||||||
|
QDict *options;
|
||||||
|
QDict *encryptopts = NULL;
|
||||||
|
QCryptoBlockCreateOptions *crypto_opts = NULL;
|
||||||
QCryptoBlock *crypto = NULL;
|
QCryptoBlock *crypto = NULL;
|
||||||
|
|
||||||
assert(opts->driver == BLOCKDEV_DRIVER_QCOW);
|
/* Read out options */
|
||||||
qcow_opts = &opts->u.qcow;
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
|
BDRV_SECTOR_SIZE);
|
||||||
/* Sanity checks */
|
|
||||||
total_size = qcow_opts->size;
|
|
||||||
if (total_size == 0) {
|
if (total_size == 0) {
|
||||||
error_setg(errp, "Image size is too small, cannot be zero length");
|
error_setg(errp, "Image size is too small, cannot be zero length");
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qcow_opts->has_encrypt &&
|
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||||
qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW)
|
encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||||
{
|
if (encryptfmt) {
|
||||||
error_setg(errp, "Unsupported encryption format");
|
if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
|
||||||
return -EINVAL;
|
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 */
|
ret = bdrv_create_file(filename, opts, &local_err);
|
||||||
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);
|
|
||||||
if (ret < 0) {
|
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);
|
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||||
|
|
||||||
/* Create image format */
|
|
||||||
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
|
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -873,15 +877,16 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
|||||||
header.size = cpu_to_be64(total_size);
|
header.size = cpu_to_be64(total_size);
|
||||||
header_size = sizeof(header);
|
header_size = sizeof(header);
|
||||||
backing_filename_len = 0;
|
backing_filename_len = 0;
|
||||||
if (qcow_opts->has_backing_file) {
|
if (backing_file) {
|
||||||
if (strcmp(qcow_opts->backing_file, "fat:")) {
|
if (strcmp(backing_file, "fat:")) {
|
||||||
header.backing_file_offset = cpu_to_be64(header_size);
|
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.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||||
header_size += backing_filename_len;
|
header_size += backing_filename_len;
|
||||||
} else {
|
} else {
|
||||||
/* special backing file for vvfat */
|
/* 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
|
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||||
unmodified sectors */
|
unmodified sectors */
|
||||||
@@ -896,10 +901,26 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
|||||||
|
|
||||||
header.l1_table_offset = cpu_to_be64(header_size);
|
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);
|
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);
|
NULL, NULL, NULL, errp);
|
||||||
if (!crypto) {
|
if (!crypto) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
@@ -915,9 +936,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qcow_opts->has_backing_file) {
|
if (backing_file) {
|
||||||
ret = blk_pwrite(qcow_blk, sizeof(header),
|
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) {
|
if (ret != backing_filename_len) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -938,97 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
exit:
|
exit:
|
||||||
blk_unref(qcow_blk);
|
blk_unref(qcow_blk);
|
||||||
bdrv_unref(bs);
|
cleanup:
|
||||||
|
QDECREF(encryptopts);
|
||||||
|
g_free(encryptfmt);
|
||||||
qcrypto_block_free(crypto);
|
qcrypto_block_free(crypto);
|
||||||
return ret;
|
qapi_free_QCryptoBlockCreateOptions(crypto_opts);
|
||||||
}
|
g_free(backing_file);
|
||||||
|
|
||||||
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);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1110,7 +1046,8 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||||
/* could not compress: write normal cluster */
|
/* could not compress: write normal cluster */
|
||||||
ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0);
|
ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS,
|
||||||
|
bytes >> BDRV_SECTOR_BITS, qiov);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1191,14 +1128,12 @@ static BlockDriver bdrv_qcow = {
|
|||||||
.bdrv_close = qcow_close,
|
.bdrv_close = qcow_close,
|
||||||
.bdrv_child_perm = bdrv_format_default_perms,
|
.bdrv_child_perm = bdrv_format_default_perms,
|
||||||
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
||||||
.bdrv_co_create = qcow_co_create,
|
|
||||||
.bdrv_co_create_opts = qcow_co_create_opts,
|
.bdrv_co_create_opts = qcow_co_create_opts,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.supports_backing = true,
|
.supports_backing = true,
|
||||||
.bdrv_refresh_limits = qcow_refresh_limits,
|
|
||||||
|
|
||||||
.bdrv_co_preadv = qcow_co_preadv,
|
.bdrv_co_readv = qcow_co_readv,
|
||||||
.bdrv_co_pwritev = qcow_co_pwritev,
|
.bdrv_co_writev = qcow_co_writev,
|
||||||
.bdrv_co_block_status = qcow_co_block_status,
|
.bdrv_co_block_status = qcow_co_block_status,
|
||||||
|
|
||||||
.bdrv_make_empty = qcow_make_empty,
|
.bdrv_make_empty = qcow_make_empty,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
#include "block/block_int.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
|
/* NOTICE: BME here means Bitmaps Extension and used as a namespace for
|
||||||
* _internal_ constants. Please do not use this _internal_ abbreviation for
|
* _internal_ constants. Please do not use this _internal_ abbreviation for
|
||||||
@@ -118,7 +118,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < size; ++i) {
|
for (i = 0; i < size; ++i) {
|
||||||
bitmap_table[i] = cpu_to_be64(bitmap_table[i]);
|
cpu_to_be64s(&bitmap_table[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < tb->size; ++i) {
|
for (i = 0; i < tb->size; ++i) {
|
||||||
table[i] = be64_to_cpu(table[i]);
|
be64_to_cpus(&table[i]);
|
||||||
ret = check_table_entry(table[i], s->cluster_size);
|
ret = check_table_entry(table[i], s->cluster_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -254,6 +254,7 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
|
|||||||
|
|
||||||
ret = bitmap_table_load(bs, tb, &bitmap_table);
|
ret = bitmap_table_load(bs, tb, &bitmap_table);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
assert(bitmap_table == NULL);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,20 +395,20 @@ fail:
|
|||||||
|
|
||||||
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
|
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
|
||||||
{
|
{
|
||||||
entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset);
|
be64_to_cpus(&entry->bitmap_table_offset);
|
||||||
entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size);
|
be32_to_cpus(&entry->bitmap_table_size);
|
||||||
entry->flags = be32_to_cpu(entry->flags);
|
be32_to_cpus(&entry->flags);
|
||||||
entry->name_size = be16_to_cpu(entry->name_size);
|
be16_to_cpus(&entry->name_size);
|
||||||
entry->extra_data_size = be32_to_cpu(entry->extra_data_size);
|
be32_to_cpus(&entry->extra_data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
|
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
|
||||||
{
|
{
|
||||||
entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset);
|
cpu_to_be64s(&entry->bitmap_table_offset);
|
||||||
entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size);
|
cpu_to_be32s(&entry->bitmap_table_size);
|
||||||
entry->flags = cpu_to_be32(entry->flags);
|
cpu_to_be32s(&entry->flags);
|
||||||
entry->name_size = cpu_to_be16(entry->name_size);
|
cpu_to_be16s(&entry->name_size);
|
||||||
entry->extra_data_size = cpu_to_be32(entry->extra_data_size);
|
cpu_to_be32s(&entry->extra_data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
|
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
|
||||||
@@ -775,12 +776,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not
|
ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size);
|
||||||
* necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap
|
|
||||||
* directory in-place (actually, turn-off the extension), which is checked
|
|
||||||
* in qcow2_check_metadata_overlap() */
|
|
||||||
ret = qcow2_pre_write_overlap_check(
|
|
||||||
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1008,8 +1004,7 @@ fail:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2BitmapList *bm_list;
|
Qcow2BitmapList *bm_list;
|
||||||
@@ -1017,10 +1012,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
|||||||
GSList *ro_dirty_bitmaps = NULL;
|
GSList *ro_dirty_bitmaps = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (header_updated != NULL) {
|
|
||||||
*header_updated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->nb_bitmaps == 0) {
|
if (s->nb_bitmaps == 0) {
|
||||||
/* No bitmaps - nothing to do */
|
/* No bitmaps - nothing to do */
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1064,9 +1055,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
|||||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (header_updated != NULL) {
|
|
||||||
*header_updated = true;
|
|
||||||
}
|
|
||||||
g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
|
g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,11 +1065,6 @@ out:
|
|||||||
return ret;
|
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_data()
|
||||||
* Store bitmap to image, filling bitmap table accordingly.
|
* Store bitmap to image, filling bitmap table accordingly.
|
||||||
*/
|
*/
|
||||||
@@ -1418,22 +1401,6 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
|||||||
g_free(tb);
|
g_free(tb);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
|
||||||
/* For safety, we remove bitmap after storing.
|
|
||||||
* We may be here in two cases:
|
|
||||||
* 1. bdrv_close. It's ok to drop bitmap.
|
|
||||||
* 2. inactivation. It means migration without 'dirty-bitmaps'
|
|
||||||
* capability, so bitmaps are not marked with
|
|
||||||
* BdrvDirtyBitmap.migration flags. It's not bad to drop them too,
|
|
||||||
* and reload on invalidation.
|
|
||||||
*/
|
|
||||||
if (bm->dirty_bitmap == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap_list_free(bm_list);
|
bitmap_list_free(bm_list);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qcow2.h"
|
#include "block/qcow2.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
@@ -994,17 +994,6 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees the allocated clusters because the request failed and they won't
|
|
||||||
* actually be linked.
|
|
||||||
*/
|
|
||||||
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
|
|
||||||
{
|
|
||||||
BDRVQcow2State *s = bs->opaque;
|
|
||||||
qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits,
|
|
||||||
QCOW2_DISCARD_NEVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the number of contiguous clusters that can be used for an allocating
|
* Returns the number of contiguous clusters that can be used for an allocating
|
||||||
* write, but require COW to be performed (this includes yet unallocated space,
|
* write, but require COW to be performed (this includes yet unallocated space,
|
||||||
|
|||||||
@@ -26,13 +26,12 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qcow2.h"
|
#include "block/qcow2.h"
|
||||||
#include "qemu/range.h"
|
#include "qemu/range.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
|
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
|
||||||
uint64_t max);
|
|
||||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
int64_t offset, int64_t length, uint64_t addend,
|
int64_t offset, int64_t length, uint64_t addend,
|
||||||
bool decrease, enum qcow2_discard_type type);
|
bool decrease, enum qcow2_discard_type type);
|
||||||
@@ -363,7 +362,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate the refcount block itself and mark it as used */
|
/* Allocate the refcount block itself and mark it as used */
|
||||||
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX);
|
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
|
||||||
if (new_block < 0) {
|
if (new_block < 0) {
|
||||||
return new_block;
|
return new_block;
|
||||||
}
|
}
|
||||||
@@ -735,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
|
|||||||
|
|
||||||
/* Discard is optional, ignore the return value */
|
/* Discard is optional, ignore the return value */
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
bdrv_pdiscard(bs->file, d->offset, d->bytes);
|
bdrv_pdiscard(bs->file->bs, d->offset, d->bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(d);
|
g_free(d);
|
||||||
@@ -840,13 +839,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
|||||||
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
|
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
|
||||||
}
|
}
|
||||||
ret = alloc_refcount_block(bs, cluster_index, &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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -955,8 +947,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
|||||||
|
|
||||||
|
|
||||||
/* return < 0 if error */
|
/* return < 0 if error */
|
||||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
|
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
||||||
uint64_t max)
|
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t i, nb_clusters, refcount;
|
uint64_t i, nb_clusters, refcount;
|
||||||
@@ -981,9 +972,9 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure that all offsets in the "allocated" range are representable
|
/* Make sure that all offsets in the "allocated" range are representable
|
||||||
* in the requested max */
|
* in an int64_t */
|
||||||
if (s->free_cluster_index > 0 &&
|
if (s->free_cluster_index > 0 &&
|
||||||
s->free_cluster_index - 1 > (max >> s->cluster_bits))
|
s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits))
|
||||||
{
|
{
|
||||||
return -EFBIG;
|
return -EFBIG;
|
||||||
}
|
}
|
||||||
@@ -1003,7 +994,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
|
|||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
||||||
do {
|
do {
|
||||||
offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET);
|
offset = alloc_clusters_noref(bs, size);
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
@@ -1085,11 +1076,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
|||||||
free_in_cluster = s->cluster_size - offset_into_cluster(s, offset);
|
free_in_cluster = s->cluster_size - offset_into_cluster(s, offset);
|
||||||
do {
|
do {
|
||||||
if (!offset || free_in_cluster < size) {
|
if (!offset || free_in_cluster < size) {
|
||||||
int64_t new_cluster;
|
int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size);
|
||||||
|
|
||||||
new_cluster = alloc_clusters_noref(bs, s->cluster_size,
|
|
||||||
MIN(s->cluster_offset_mask,
|
|
||||||
QCOW_MAX_CLUSTER_OFFSET));
|
|
||||||
if (new_cluster < 0) {
|
if (new_cluster < 0) {
|
||||||
return new_cluster;
|
return new_cluster;
|
||||||
}
|
}
|
||||||
@@ -1583,9 +1570,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
|
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
|
||||||
if (l2_entry & 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 "
|
"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;
|
l2_entry &= ~QCOW_OFLAG_COPIED;
|
||||||
res->corruptions++;
|
res->corruptions++;
|
||||||
}
|
}
|
||||||
@@ -1805,19 +1792,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
int ret;
|
int ret;
|
||||||
uint64_t refcount;
|
uint64_t refcount;
|
||||||
int i, j;
|
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++) {
|
for (i = 0; i < s->l1_size; i++) {
|
||||||
uint64_t l1_entry = s->l1_table[i];
|
uint64_t l1_entry = s->l1_table[i];
|
||||||
@@ -1837,8 +1811,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
|
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||||
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
|
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
|
||||||
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||||
repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
|
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||||
if (repair) {
|
"ERROR",
|
||||||
|
i, l1_entry, refcount);
|
||||||
|
if (fix & BDRV_FIX_ERRORS) {
|
||||||
s->l1_table[i] = refcount == 1
|
s->l1_table[i] = refcount == 1
|
||||||
? l1_entry | QCOW_OFLAG_COPIED
|
? l1_entry | QCOW_OFLAG_COPIED
|
||||||
: l1_entry & ~QCOW_OFLAG_COPIED;
|
: l1_entry & ~QCOW_OFLAG_COPIED;
|
||||||
@@ -1879,8 +1855,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
|
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||||
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
|
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
|
||||||
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||||
repair ? "Repairing" : "ERROR", l2_entry, refcount);
|
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||||
if (repair) {
|
"ERROR",
|
||||||
|
l2_entry, refcount);
|
||||||
|
if (fix & BDRV_FIX_ERRORS) {
|
||||||
l2_table[j] = cpu_to_be64(refcount == 1
|
l2_table[j] = cpu_to_be64(refcount == 1
|
||||||
? l2_entry | QCOW_OFLAG_COPIED
|
? l2_entry | QCOW_OFLAG_COPIED
|
||||||
: l2_entry & ~QCOW_OFLAG_COPIED);
|
: l2_entry & ~QCOW_OFLAG_COPIED);
|
||||||
@@ -2711,16 +2689,6 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((chk & QCOW2_OL_BITMAP_DIRECTORY) &&
|
|
||||||
(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS))
|
|
||||||
{
|
|
||||||
if (overlaps_with(s->bitmap_directory_offset,
|
|
||||||
s->bitmap_directory_size))
|
|
||||||
{
|
|
||||||
return QCOW2_OL_BITMAP_DIRECTORY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2733,9 +2701,7 @@ static const char *metadata_ol_names[] = {
|
|||||||
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
|
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
|
||||||
[QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
|
[QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
|
||||||
[QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
|
[QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
|
||||||
[QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory",
|
|
||||||
};
|
};
|
||||||
QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First performs a check for metadata overlaps (through
|
* First performs a check for metadata overlaps (through
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qcow2.h"
|
#include "block/qcow2.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
|||||||
792
block/qcow2.c
792
block/qcow2.c
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
#include "crypto/block.h"
|
#include "crypto/block.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
#include "qemu/units.h"
|
|
||||||
|
|
||||||
//#define DEBUG_ALLOC
|
//#define DEBUG_ALLOC
|
||||||
//#define DEBUG_ALLOC2
|
//#define DEBUG_ALLOC2
|
||||||
@@ -42,19 +41,13 @@
|
|||||||
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
||||||
#define QCOW_MAX_SNAPSHOTS 65536
|
#define QCOW_MAX_SNAPSHOTS 65536
|
||||||
|
|
||||||
/* Field widths in qcow2 mean normal cluster offsets cannot reach
|
|
||||||
* 64PB; depending on cluster size, compressed clusters can have a
|
|
||||||
* smaller limit (64PB for up to 16k clusters, then ramps down to
|
|
||||||
* 512TB for 2M clusters). */
|
|
||||||
#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1)
|
|
||||||
|
|
||||||
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
|
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
|
||||||
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
#define QCOW_MAX_REFTABLE_SIZE S_8MiB
|
#define QCOW_MAX_REFTABLE_SIZE 0x800000
|
||||||
|
|
||||||
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
||||||
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
#define QCOW_MAX_L1_SIZE S_32MiB
|
#define QCOW_MAX_L1_SIZE 0x2000000
|
||||||
|
|
||||||
/* Allow for an average of 1k per snapshot table entry, should be plenty of
|
/* Allow for an average of 1k per snapshot table entry, should be plenty of
|
||||||
* space for snapshot names and IDs */
|
* space for snapshot names and IDs */
|
||||||
@@ -80,16 +73,16 @@
|
|||||||
/* Must be at least 4 to cover all cases of refcount table growth */
|
/* Must be at least 4 to cover all cases of refcount table growth */
|
||||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
||||||
|
|
||||||
#ifdef CONFIG_LINUX
|
/* Whichever is more */
|
||||||
#define DEFAULT_L2_CACHE_MAX_SIZE S_32MiB
|
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
|
||||||
#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */
|
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
|
||||||
#else
|
|
||||||
#define DEFAULT_L2_CACHE_MAX_SIZE S_8MiB
|
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
|
||||||
/* Cache clean interval is currently available only on Linux, so must be 0 */
|
* clusters */
|
||||||
#define DEFAULT_CACHE_CLEAN_INTERVAL 0
|
#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4
|
||||||
#endif
|
|
||||||
|
#define DEFAULT_CLUSTER_SIZE 65536
|
||||||
|
|
||||||
#define DEFAULT_CLUSTER_SIZE S_64KiB
|
|
||||||
|
|
||||||
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
|
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
|
||||||
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
|
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
|
||||||
@@ -105,7 +98,6 @@
|
|||||||
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
|
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
|
||||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
|
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
|
||||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
|
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
|
||||||
#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory"
|
|
||||||
#define QCOW2_OPT_CACHE_SIZE "cache-size"
|
#define QCOW2_OPT_CACHE_SIZE "cache-size"
|
||||||
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
|
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
|
||||||
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
|
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
|
||||||
@@ -337,9 +329,6 @@ typedef struct BDRVQcow2State {
|
|||||||
* override) */
|
* override) */
|
||||||
char *image_backing_file;
|
char *image_backing_file;
|
||||||
char *image_backing_format;
|
char *image_backing_format;
|
||||||
|
|
||||||
CoQueue compress_wait_queue;
|
|
||||||
int nb_compress_threads;
|
|
||||||
} BDRVQcow2State;
|
} BDRVQcow2State;
|
||||||
|
|
||||||
typedef struct Qcow2COWRegion {
|
typedef struct Qcow2COWRegion {
|
||||||
@@ -419,9 +408,8 @@ typedef enum QCow2MetadataOverlap {
|
|||||||
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
|
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
|
||||||
QCOW2_OL_INACTIVE_L1_BITNR = 6,
|
QCOW2_OL_INACTIVE_L1_BITNR = 6,
|
||||||
QCOW2_OL_INACTIVE_L2_BITNR = 7,
|
QCOW2_OL_INACTIVE_L2_BITNR = 7,
|
||||||
QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8,
|
|
||||||
|
|
||||||
QCOW2_OL_MAX_BITNR = 9,
|
QCOW2_OL_MAX_BITNR = 8,
|
||||||
|
|
||||||
QCOW2_OL_NONE = 0,
|
QCOW2_OL_NONE = 0,
|
||||||
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
|
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
|
||||||
@@ -434,13 +422,12 @@ typedef enum QCow2MetadataOverlap {
|
|||||||
/* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
|
/* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
|
||||||
* reads. */
|
* reads. */
|
||||||
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
|
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
|
||||||
QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR),
|
|
||||||
} QCow2MetadataOverlap;
|
} QCow2MetadataOverlap;
|
||||||
|
|
||||||
/* Perform all overlap checks which can be done in constant time */
|
/* Perform all overlap checks which can be done in constant time */
|
||||||
#define QCOW2_OL_CONSTANT \
|
#define QCOW2_OL_CONSTANT \
|
||||||
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
|
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
|
||||||
QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY)
|
QCOW2_OL_SNAPSHOT_TABLE)
|
||||||
|
|
||||||
/* Perform all overlap checks which don't require disk access */
|
/* Perform all overlap checks which don't require disk access */
|
||||||
#define QCOW2_OL_CACHED \
|
#define QCOW2_OL_CACHED \
|
||||||
@@ -630,7 +617,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
|||||||
int compressed_size);
|
int compressed_size);
|
||||||
|
|
||||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||||
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m);
|
|
||||||
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
|
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t bytes, enum qcow2_discard_type type,
|
uint64_t bytes, enum qcow2_discard_type type,
|
||||||
bool full_discard);
|
bool full_discard);
|
||||||
@@ -685,8 +671,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
void **refcount_table,
|
void **refcount_table,
|
||||||
int64_t *refcount_table_size);
|
int64_t *refcount_table_size);
|
||||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
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);
|
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
|
||||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
||||||
|
|||||||
218
block/qed.c
218
block/qed.c
@@ -13,7 +13,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
@@ -21,11 +20,6 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qed.h"
|
#include "qed.h"
|
||||||
#include "sysemu/block-backend.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,
|
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
|
||||||
const char *filename)
|
const char *filename)
|
||||||
@@ -600,78 +594,43 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
|||||||
qemu_vfree(s->l1_table);
|
qemu_vfree(s->l1_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
static int qed_create(const char *filename, uint32_t cluster_size,
|
||||||
Error **errp)
|
uint64_t image_size, uint32_t table_size,
|
||||||
|
const char *backing_file, const char *backing_fmt,
|
||||||
|
QemuOpts *opts, Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevCreateOptionsQed *qed_opts;
|
QEDHeader header = {
|
||||||
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) {
|
|
||||||
.magic = QED_MAGIC,
|
.magic = QED_MAGIC,
|
||||||
.cluster_size = qed_opts->cluster_size,
|
.cluster_size = cluster_size,
|
||||||
.table_size = qed_opts->table_size,
|
.table_size = table_size,
|
||||||
.header_size = 1,
|
.header_size = 1,
|
||||||
.features = 0,
|
.features = 0,
|
||||||
.compat_features = 0,
|
.compat_features = 0,
|
||||||
.l1_table_offset = qed_opts->cluster_size,
|
.l1_table_offset = cluster_size,
|
||||||
.image_size = qed_opts->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 */
|
/* File must start empty and grow, check truncate is supported */
|
||||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
||||||
@@ -679,25 +638,22 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qed_opts->has_backing_file) {
|
if (backing_file) {
|
||||||
header.features |= QED_F_BACKING_FILE;
|
header.features |= QED_F_BACKING_FILE;
|
||||||
header.backing_filename_offset = sizeof(le_header);
|
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)) {
|
if (qed_fmt_is_raw(backing_fmt)) {
|
||||||
header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
|
header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
qed_header_cpu_to_le(&header, &le_header);
|
qed_header_cpu_to_le(&header, &le_header);
|
||||||
ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0);
|
ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
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);
|
header.backing_filename_size, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
@@ -713,7 +669,6 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
|||||||
out:
|
out:
|
||||||
g_free(l1_table);
|
g_free(l1_table);
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
bdrv_unref(bs);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,74 +676,51 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
|
|||||||
QemuOpts *opts,
|
QemuOpts *opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevCreateOptions *create_options = NULL;
|
uint64_t image_size = 0;
|
||||||
QDict *qdict;
|
uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
|
||||||
Visitor *v;
|
uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
|
||||||
BlockDriverState *bs = NULL;
|
char *backing_file = NULL;
|
||||||
Error *local_err = NULL;
|
char *backing_fmt = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
static const QDictRenames opt_renames[] = {
|
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
BDRV_SECTOR_SIZE);
|
||||||
{ BLOCK_OPT_BACKING_FMT, "backing-fmt" },
|
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||||
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
|
||||||
{ BLOCK_OPT_TABLE_SIZE, "table-size" },
|
cluster_size = qemu_opt_get_size_del(opts,
|
||||||
{ NULL, NULL },
|
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 */
|
if (!qed_is_cluster_size_valid(cluster_size)) {
|
||||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true);
|
error_setg(errp, "QED cluster size must be within range [%u, %u] "
|
||||||
|
"and power of 2",
|
||||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
if (!qed_is_table_size_valid(table_size)) {
|
||||||
/* Create and open the file (protocol layer) */
|
error_setg(errp, "QED table size must be within range [%u, %u] "
|
||||||
ret = bdrv_create_file(filename, opts, &local_err);
|
"and power of 2",
|
||||||
if (ret < 0) {
|
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||||
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) {
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
|
||||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
error_setg(errp, "QED image size must be a non-zero multiple of "
|
||||||
visit_free(v);
|
"cluster size and less than %" PRIu64 " bytes",
|
||||||
|
qed_max_image_size(cluster_size, table_size));
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Silently round up size */
|
ret = qed_create(filename, cluster_size, image_size, table_size,
|
||||||
assert(create_options->driver == BLOCKDEV_DRIVER_QED);
|
backing_file, backing_fmt, opts, errp);
|
||||||
create_options->u.qed.size =
|
|
||||||
ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE);
|
|
||||||
|
|
||||||
/* Create the qed image (format layer) */
|
finish:
|
||||||
ret = bdrv_qed_co_create(create_options, errp);
|
g_free(backing_file);
|
||||||
|
g_free(backing_fmt);
|
||||||
fail:
|
|
||||||
qobject_unref(qdict);
|
|
||||||
bdrv_unref(bs);
|
|
||||||
qapi_free_BlockdevCreateOptions(create_options);
|
|
||||||
return ret;
|
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,
|
static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
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);
|
return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1467,10 +1398,8 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
QED_AIOCB_WRITE | QED_AIOCB_ZERO);
|
QED_AIOCB_WRITE | QED_AIOCB_ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
|
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
int64_t offset,
|
PreallocMode prealloc, Error **errp)
|
||||||
PreallocMode prealloc,
|
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BDRVQEDState *s = bs->opaque;
|
BDRVQEDState *s = bs->opaque;
|
||||||
uint64_t old_image_size;
|
uint64_t old_image_size;
|
||||||
@@ -1606,8 +1535,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
|
|||||||
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
|
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
|
||||||
qemu_co_mutex_unlock(&s->table_lock);
|
qemu_co_mutex_unlock(&s->table_lock);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate_prepend(errp, local_err,
|
error_propagate(errp, local_err);
|
||||||
"Could not reopen qed layer: ");
|
error_prepend(errp, "Could not reopen qed layer: ");
|
||||||
return;
|
return;
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
||||||
@@ -1673,14 +1602,13 @@ static BlockDriver bdrv_qed = {
|
|||||||
.bdrv_close = bdrv_qed_close,
|
.bdrv_close = bdrv_qed_close,
|
||||||
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
||||||
.bdrv_child_perm = bdrv_format_default_perms,
|
.bdrv_child_perm = bdrv_format_default_perms,
|
||||||
.bdrv_co_create = bdrv_qed_co_create,
|
|
||||||
.bdrv_co_create_opts = bdrv_qed_co_create_opts,
|
.bdrv_co_create_opts = bdrv_qed_co_create_opts,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_block_status = bdrv_qed_co_block_status,
|
.bdrv_co_block_status = bdrv_qed_co_block_status,
|
||||||
.bdrv_co_readv = bdrv_qed_co_readv,
|
.bdrv_co_readv = bdrv_qed_co_readv,
|
||||||
.bdrv_co_writev = bdrv_qed_co_writev,
|
.bdrv_co_writev = bdrv_qed_co_writev,
|
||||||
.bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes,
|
||||||
.bdrv_co_truncate = bdrv_qed_co_truncate,
|
.bdrv_truncate = bdrv_qed_truncate,
|
||||||
.bdrv_getlength = bdrv_qed_getlength,
|
.bdrv_getlength = bdrv_qed_getlength,
|
||||||
.bdrv_get_info = bdrv_qed_get_info,
|
.bdrv_get_info = bdrv_qed_get_info,
|
||||||
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
|
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-events-block.h"
|
#include "qapi/qapi-events-block.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
@@ -116,7 +115,6 @@ struct QuorumAIOCB {
|
|||||||
/* Request metadata */
|
/* Request metadata */
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
uint64_t bytes;
|
uint64_t bytes;
|
||||||
int flags;
|
|
||||||
|
|
||||||
QEMUIOVector *qiov; /* calling IOV */
|
QEMUIOVector *qiov; /* calling IOV */
|
||||||
|
|
||||||
@@ -159,8 +157,7 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
|||||||
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
||||||
QEMUIOVector *qiov,
|
QEMUIOVector *qiov,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t bytes,
|
uint64_t bytes)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
BDRVQuorumState *s = bs->opaque;
|
BDRVQuorumState *s = bs->opaque;
|
||||||
QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
|
QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
|
||||||
@@ -171,7 +168,6 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
|||||||
.bs = bs,
|
.bs = bs,
|
||||||
.offset = offset,
|
.offset = offset,
|
||||||
.bytes = bytes,
|
.bytes = bytes,
|
||||||
.flags = flags,
|
|
||||||
.qiov = qiov,
|
.qiov = qiov,
|
||||||
.votes.compare = quorum_sha256_compare,
|
.votes.compare = quorum_sha256_compare,
|
||||||
.votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list),
|
.votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list),
|
||||||
@@ -199,7 +195,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector,
|
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector,
|
||||||
end_sector - start_sector);
|
end_sector - start_sector, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||||
@@ -210,7 +206,7 @@ static void quorum_report_failure(QuorumAIOCB *acb)
|
|||||||
BDRV_SECTOR_SIZE);
|
BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
qapi_event_send_quorum_failure(reference, start_sector,
|
qapi_event_send_quorum_failure(reference, start_sector,
|
||||||
end_sector - start_sector);
|
end_sector - start_sector, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int quorum_vote_error(QuorumAIOCB *acb);
|
static int quorum_vote_error(QuorumAIOCB *acb);
|
||||||
@@ -275,11 +271,9 @@ static void quorum_rewrite_entry(void *opaque)
|
|||||||
BDRVQuorumState *s = acb->bs->opaque;
|
BDRVQuorumState *s = acb->bs->opaque;
|
||||||
|
|
||||||
/* Ignore any errors, it's just a correction attempt for already
|
/* Ignore any errors, it's just a correction attempt for already
|
||||||
* corrupted data.
|
* corrupted data. */
|
||||||
* Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the
|
|
||||||
* area with different data from the other children. */
|
|
||||||
bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes,
|
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 */
|
/* Wake up the caller after the last rewrite */
|
||||||
acb->rewrite_count--;
|
acb->rewrite_count--;
|
||||||
@@ -437,7 +431,23 @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
|
static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ",
|
||||||
|
acb->offset, acb->bytes);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
va_end(ap);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool quorum_compare(QuorumAIOCB *acb,
|
||||||
|
QEMUIOVector *a,
|
||||||
|
QEMUIOVector *b)
|
||||||
{
|
{
|
||||||
BDRVQuorumState *s = acb->bs->opaque;
|
BDRVQuorumState *s = acb->bs->opaque;
|
||||||
ssize_t offset;
|
ssize_t offset;
|
||||||
@@ -446,10 +456,8 @@ static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
|
|||||||
if (s->is_blkverify) {
|
if (s->is_blkverify) {
|
||||||
offset = qemu_iovec_compare(a, b);
|
offset = qemu_iovec_compare(a, b);
|
||||||
if (offset != -1) {
|
if (offset != -1) {
|
||||||
fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64
|
quorum_err(acb, "contents mismatch at offset %" PRIu64,
|
||||||
" contents mismatch at offset %" PRIu64 "\n",
|
acb->offset + offset);
|
||||||
acb->offset, acb->bytes, acb->offset + offset);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -600,7 +608,7 @@ static void read_quorum_children_entry(void *opaque)
|
|||||||
static int read_quorum_children(QuorumAIOCB *acb)
|
static int read_quorum_children(QuorumAIOCB *acb)
|
||||||
{
|
{
|
||||||
BDRVQuorumState *s = acb->bs->opaque;
|
BDRVQuorumState *s = acb->bs->opaque;
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
acb->children_read = s->num_children;
|
acb->children_read = s->num_children;
|
||||||
for (i = 0; i < s->num_children; i++) {
|
for (i = 0; i < s->num_children; i++) {
|
||||||
@@ -635,7 +643,9 @@ static int read_quorum_children(QuorumAIOCB *acb)
|
|||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
return acb->vote_ret;
|
ret = acb->vote_ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_fifo_child(QuorumAIOCB *acb)
|
static int read_fifo_child(QuorumAIOCB *acb)
|
||||||
@@ -663,7 +673,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||||
{
|
{
|
||||||
BDRVQuorumState *s = bs->opaque;
|
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;
|
int ret;
|
||||||
|
|
||||||
acb->is_read = true;
|
acb->is_read = true;
|
||||||
@@ -689,7 +699,7 @@ static void write_quorum_entry(void *opaque)
|
|||||||
|
|
||||||
sacb->bs = s->children[i]->bs;
|
sacb->bs = s->children[i]->bs;
|
||||||
sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
|
sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
|
||||||
acb->qiov, acb->flags);
|
acb->qiov, 0);
|
||||||
if (sacb->ret == 0) {
|
if (sacb->ret == 0) {
|
||||||
acb->success_count++;
|
acb->success_count++;
|
||||||
} else {
|
} else {
|
||||||
@@ -709,7 +719,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||||
{
|
{
|
||||||
BDRVQuorumState *s = bs->opaque;
|
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;
|
int i, ret;
|
||||||
|
|
||||||
for (i = 0; i < s->num_children; i++) {
|
for (i = 0; i < s->num_children; i++) {
|
||||||
@@ -912,12 +922,13 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
s->read_pattern = ret;
|
s->read_pattern = ret;
|
||||||
|
|
||||||
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
||||||
s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false);
|
/* is the driver in blkverify mode */
|
||||||
if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) {
|
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||||
error_setg(&local_err, "blkverify=on can only be set if there are "
|
s->num_children == 2 && s->threshold == 2) {
|
||||||
"exactly two files and vote-threshold is 2");
|
s->is_blkverify = true;
|
||||||
ret = -EINVAL;
|
} else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
|
||||||
goto exit;
|
fprintf(stderr, "blkverify mode is set by setting blkverify=on "
|
||||||
|
"and using two files with vote_threshold=2\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE,
|
s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE,
|
||||||
@@ -950,8 +961,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
s->next_child_index = s->num_children;
|
s->next_child_index = s->num_children;
|
||||||
|
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
|
||||||
|
|
||||||
g_free(opened);
|
g_free(opened);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
@@ -992,11 +1001,6 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
|
|||||||
char indexstr[32];
|
char indexstr[32];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (s->is_blkverify) {
|
|
||||||
error_setg(errp, "Cannot add a child to a quorum in blkverify mode");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(s->num_children <= INT_MAX / sizeof(BdrvChild *));
|
assert(s->num_children <= INT_MAX / sizeof(BdrvChild *));
|
||||||
if (s->num_children == INT_MAX / sizeof(BdrvChild *) ||
|
if (s->num_children == INT_MAX / sizeof(BdrvChild *) ||
|
||||||
s->next_child_index == UINT_MAX) {
|
s->next_child_index == UINT_MAX) {
|
||||||
@@ -1051,9 +1055,6 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We know now that num_children > threshold, so blkverify must be false */
|
|
||||||
assert(!s->is_blkverify);
|
|
||||||
|
|
||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
|
|
||||||
/* We can safely remove this child now */
|
/* We can safely remove this child now */
|
||||||
@@ -1081,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
|
|
||||||
children = qlist_new();
|
children = qlist_new();
|
||||||
for (i = 0; i < s->num_children; i++) {
|
for (i = 0; i < s->num_children; i++) {
|
||||||
qlist_append(children,
|
QINCREF(s->children[i]->bs->full_open_options);
|
||||||
qobject_ref(s->children[i]->bs->full_open_options));
|
qlist_append(children, s->children[i]->bs->full_open_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qdict_new();
|
opts = qdict_new();
|
||||||
|
|||||||
@@ -167,37 +167,16 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
|||||||
state->opaque = NULL;
|
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,
|
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t bytes, QEMUIOVector *qiov,
|
uint64_t bytes, QEMUIOVector *qiov,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
int ret;
|
BDRVRawState *s = bs->opaque;
|
||||||
|
|
||||||
ret = raw_adjust_offset(bs, &offset, bytes, false);
|
if (offset > UINT64_MAX - s->offset) {
|
||||||
if (ret) {
|
return -EINVAL;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
offset += s->offset;
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
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,
|
uint64_t bytes, QEMUIOVector *qiov,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
QEMUIOVector local_qiov;
|
QEMUIOVector local_qiov;
|
||||||
int ret;
|
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) {
|
if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
|
||||||
/* Handling partial writes would be a pain - so we just
|
/* Handling partial writes would be a pain - so we just
|
||||||
* require that guests have 512-byte request alignment if
|
* 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;
|
qiov = &local_qiov;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = raw_adjust_offset(bs, &offset, bytes, true);
|
offset += s->offset;
|
||||||
if (ret) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||||
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||||
@@ -279,25 +267,23 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
int64_t offset, int bytes,
|
int64_t offset, int bytes,
|
||||||
BdrvRequestFlags flags)
|
BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
int ret;
|
BDRVRawState *s = bs->opaque;
|
||||||
|
if (offset > UINT64_MAX - s->offset) {
|
||||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
return -EINVAL;
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
offset += s->offset;
|
||||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
||||||
int64_t offset, int bytes)
|
int64_t offset, int bytes)
|
||||||
{
|
{
|
||||||
int ret;
|
BDRVRawState *s = bs->opaque;
|
||||||
|
if (offset > UINT64_MAX - s->offset) {
|
||||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
return -EINVAL;
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
offset += s->offset;
|
||||||
|
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t raw_getlength(BlockDriverState *bs)
|
static int64_t raw_getlength(BlockDriverState *bs)
|
||||||
@@ -366,7 +352,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
|
static int raw_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
@@ -383,7 +369,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
|
|||||||
|
|
||||||
s->size = offset;
|
s->size = offset;
|
||||||
offset += s->offset;
|
offset += s->offset;
|
||||||
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
|
return bdrv_truncate(bs->file, offset, prealloc, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void raw_eject(BlockDriverState *bs, bool eject_flag)
|
static void raw_eject(BlockDriverState *bs, bool eject_flag)
|
||||||
@@ -429,11 +415,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bs->sg = bs->file->bs->sg;
|
bs->sg = bs->file->bs->sg;
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
bs->file->bs->supported_write_flags;
|
||||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
bs->file->bs->supported_zero_flags;
|
||||||
bs->file->bs->supported_zero_flags);
|
|
||||||
|
|
||||||
if (bs->probed && !bdrv_is_read_only(bs)) {
|
if (bs->probed && !bdrv_is_read_only(bs)) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@@ -459,6 +444,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void raw_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
{
|
{
|
||||||
/* smallest possible positive score so that raw is used if and only if no
|
/* smallest possible positive score so that raw is used if and only if no
|
||||||
@@ -493,44 +482,6 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
|
|||||||
return bdrv_probe_geometry(bs->file->bs, 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 read_flags,
|
|
||||||
BdrvRequestFlags write_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, read_flags, write_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 read_flags,
|
|
||||||
BdrvRequestFlags write_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,
|
|
||||||
read_flags, write_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockDriver bdrv_raw = {
|
BlockDriver bdrv_raw = {
|
||||||
.format_name = "raw",
|
.format_name = "raw",
|
||||||
.instance_size = sizeof(BDRVRawState),
|
.instance_size = sizeof(BDRVRawState),
|
||||||
@@ -539,6 +490,7 @@ BlockDriver bdrv_raw = {
|
|||||||
.bdrv_reopen_commit = &raw_reopen_commit,
|
.bdrv_reopen_commit = &raw_reopen_commit,
|
||||||
.bdrv_reopen_abort = &raw_reopen_abort,
|
.bdrv_reopen_abort = &raw_reopen_abort,
|
||||||
.bdrv_open = &raw_open,
|
.bdrv_open = &raw_open,
|
||||||
|
.bdrv_close = &raw_close,
|
||||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||||
.bdrv_co_create_opts = &raw_co_create_opts,
|
.bdrv_co_create_opts = &raw_co_create_opts,
|
||||||
.bdrv_co_preadv = &raw_co_preadv,
|
.bdrv_co_preadv = &raw_co_preadv,
|
||||||
@@ -546,9 +498,7 @@ BlockDriver bdrv_raw = {
|
|||||||
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
|
||||||
.bdrv_co_pdiscard = &raw_co_pdiscard,
|
.bdrv_co_pdiscard = &raw_co_pdiscard,
|
||||||
.bdrv_co_block_status = &raw_co_block_status,
|
.bdrv_co_block_status = &raw_co_block_status,
|
||||||
.bdrv_co_copy_range_from = &raw_co_copy_range_from,
|
.bdrv_truncate = &raw_truncate,
|
||||||
.bdrv_co_copy_range_to = &raw_co_copy_range_to,
|
|
||||||
.bdrv_co_truncate = &raw_co_truncate,
|
|
||||||
.bdrv_getlength = &raw_getlength,
|
.bdrv_getlength = &raw_getlength,
|
||||||
.has_variable_length = true,
|
.has_variable_length = true,
|
||||||
.bdrv_measure = &raw_measure,
|
.bdrv_measure = &raw_measure,
|
||||||
|
|||||||
244
block/rbd.c
244
block/rbd.c
@@ -18,7 +18,6 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "crypto/secret.h"
|
#include "crypto/secret.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
@@ -227,57 +226,27 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
qobject_unref(keypairs);
|
QDECREF(keypairs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp)
|
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
|
||||||
{
|
|
||||||
/* 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,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
char *key, *acr;
|
if (secretid == 0) {
|
||||||
int r;
|
return 0;
|
||||||
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 (opts->has_auth_client_required) {
|
gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
|
||||||
accu = g_string_new("");
|
errp);
|
||||||
for (auth = opts->auth_client_required; auth; auth = auth->next) {
|
if (!secret) {
|
||||||
if (accu->str[0]) {
|
return -1;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rados_conf_set(cluster, "key", secret);
|
||||||
|
g_free(secret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,29 +263,29 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
|
|||||||
if (!keypairs_json) {
|
if (!keypairs_json) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
keypairs = qobject_to(QList,
|
keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
|
||||||
qobject_from_json(keypairs_json, &error_abort));
|
&error_abort));
|
||||||
remaining = qlist_size(keypairs) / 2;
|
remaining = qlist_size(keypairs) / 2;
|
||||||
assert(remaining);
|
assert(remaining);
|
||||||
|
|
||||||
while (remaining--) {
|
while (remaining--) {
|
||||||
name = qobject_to(QString, qlist_pop(keypairs));
|
name = qobject_to_qstring(qlist_pop(keypairs));
|
||||||
value = qobject_to(QString, qlist_pop(keypairs));
|
value = qobject_to_qstring(qlist_pop(keypairs));
|
||||||
assert(name && value);
|
assert(name && value);
|
||||||
key = qstring_get_str(name);
|
key = qstring_get_str(name);
|
||||||
|
|
||||||
ret = rados_conf_set(cluster, key, qstring_get_str(value));
|
ret = rados_conf_set(cluster, key, qstring_get_str(value));
|
||||||
qobject_unref(value);
|
QDECREF(value);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "invalid conf option %s", key);
|
error_setg_errno(errp, -ret, "invalid conf option %s", key);
|
||||||
qobject_unref(name);
|
QDECREF(name);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
qobject_unref(name);
|
QDECREF(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
qobject_unref(keypairs);
|
QDECREF(keypairs);
|
||||||
return ret;
|
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,
|
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
|
||||||
const char *keypairs, const char *password_secret,
|
const char *keypairs, const char *password_secret,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
@@ -478,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
qobject_unref(options);
|
QDECREF(options);
|
||||||
qapi_free_BlockdevCreateOptions(create_options);
|
qapi_free_BlockdevCreateOptions(create_options);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -574,16 +545,6 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int r;
|
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);
|
mon_host = qemu_rbd_mon_host(opts, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, 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 (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
|
||||||
if (r < 0) {
|
r = -EIO;
|
||||||
goto failed_shutdown;
|
goto failed_shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,66 +616,33 @@ failed_opts:
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
Visitor *v;
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
/* Convert the remaining options into a QAPI object */
|
|
||||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
|
||||||
if (!v) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit_type_BlockdevOptionsRbd(v, NULL, opts, &local_err);
|
|
||||||
visit_free(v);
|
|
||||||
|
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qemu_rbd_attempt_legacy_options(QDict *options,
|
|
||||||
BlockdevOptionsRbd **opts,
|
|
||||||
char **keypairs)
|
|
||||||
{
|
|
||||||
char *filename;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
filename = g_strdup(qdict_get_try_str(options, "filename"));
|
|
||||||
if (!filename) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
qdict_del(options, "filename");
|
|
||||||
|
|
||||||
qemu_rbd_parse_filename(filename, options, NULL);
|
|
||||||
|
|
||||||
/* keypairs freed by caller */
|
|
||||||
*keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
|
|
||||||
if (*keypairs) {
|
|
||||||
qdict_del(options, "=keyvalue-pairs");
|
|
||||||
}
|
|
||||||
|
|
||||||
r = qemu_rbd_convert_options(options, opts, NULL);
|
|
||||||
|
|
||||||
g_free(filename);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
BlockdevOptionsRbd *opts = NULL;
|
BlockdevOptionsRbd *opts = NULL;
|
||||||
const QDictEntry *e;
|
Visitor *v;
|
||||||
|
QObject *crumpled = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
const char *filename;
|
||||||
char *keypairs, *secretid;
|
char *keypairs, *secretid;
|
||||||
int r;
|
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"));
|
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
|
||||||
if (keypairs) {
|
if (keypairs) {
|
||||||
qdict_del(options, "=keyvalue-pairs");
|
qdict_del(options, "=keyvalue-pairs");
|
||||||
@@ -725,41 +653,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
qdict_del(options, "password-secret");
|
qdict_del(options, "password-secret");
|
||||||
}
|
}
|
||||||
|
|
||||||
r = qemu_rbd_convert_options(options, &opts, &local_err);
|
/* Convert the remaining options into a QAPI object */
|
||||||
|
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) {
|
if (local_err) {
|
||||||
/* If keypairs are present, that means some options are present in
|
|
||||||
* the modern option format. Don't attempt to parse legacy option
|
|
||||||
* formats, as we won't support mixed usage. */
|
|
||||||
if (keypairs) {
|
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
r = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the initial attempt to convert and process the options failed,
|
|
||||||
* we may be attempting to open an image file that has the rbd options
|
|
||||||
* specified in the older format consisting of all key/value pairs
|
|
||||||
* encoded in the filename. Go ahead and attempt to parse the
|
|
||||||
* filename, and see if we can pull out the required options. */
|
|
||||||
r = qemu_rbd_attempt_legacy_options(options, &opts, &keypairs);
|
|
||||||
if (r < 0) {
|
|
||||||
/* Propagate the original error, not the legacy parsing fallback
|
|
||||||
* error, as the latter was just a best-effort attempt. */
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* Take care whenever deciding to actually deprecate; once this ability
|
|
||||||
* is removed, we will not be able to open any images with legacy-styled
|
|
||||||
* backing image strings. */
|
|
||||||
warn_report("RBD options encoded in the filename as keyvalue pairs "
|
|
||||||
"is deprecated");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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,
|
r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
|
||||||
!(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
|
!(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@@ -780,12 +691,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
/* If we are using an rbd snapshot, we must be r/o, otherwise
|
/* If we are using an rbd snapshot, we must be r/o, otherwise
|
||||||
* leave as-is */
|
* leave as-is */
|
||||||
if (s->snap != NULL) {
|
if (s->snap != NULL) {
|
||||||
r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp);
|
if (!bdrv_is_read_only(bs)) {
|
||||||
|
error_report("Opening rbd snapshots without an explicit "
|
||||||
|
"read-only=on option is deprecated. Future versions "
|
||||||
|
"will refuse to open the image instead of "
|
||||||
|
"automatically marking the image read-only.");
|
||||||
|
r = bdrv_set_read_only(bs, true, &local_err);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
rbd_close(s->image);
|
error_propagate(errp, local_err);
|
||||||
goto failed_open;
|
goto failed_open;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
r = 0;
|
r = 0;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -975,23 +892,27 @@ failed:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs,
|
static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num,
|
||||||
QEMUIOVector *qiov, int flags,
|
QEMUIOVector *qiov,
|
||||||
|
int nb_sectors,
|
||||||
BlockCompletionFunc *cb,
|
BlockCompletionFunc *cb,
|
||||||
void *opaque)
|
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);
|
RBD_AIO_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs,
|
static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes,
|
int64_t sector_num,
|
||||||
QEMUIOVector *qiov, int flags,
|
QEMUIOVector *qiov,
|
||||||
|
int nb_sectors,
|
||||||
BlockCompletionFunc *cb,
|
BlockCompletionFunc *cb,
|
||||||
void *opaque)
|
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);
|
RBD_AIO_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,10 +967,8 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
|
|||||||
return info.size;
|
return info.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
|
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
int64_t offset,
|
PreallocMode prealloc, Error **errp)
|
||||||
PreallocMode prealloc,
|
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
int r;
|
int r;
|
||||||
@@ -1232,7 +1151,6 @@ static BlockDriver bdrv_rbd = {
|
|||||||
.format_name = "rbd",
|
.format_name = "rbd",
|
||||||
.instance_size = sizeof(BDRVRBDState),
|
.instance_size = sizeof(BDRVRBDState),
|
||||||
.bdrv_parse_filename = qemu_rbd_parse_filename,
|
.bdrv_parse_filename = qemu_rbd_parse_filename,
|
||||||
.bdrv_refresh_limits = qemu_rbd_refresh_limits,
|
|
||||||
.bdrv_file_open = qemu_rbd_open,
|
.bdrv_file_open = qemu_rbd_open,
|
||||||
.bdrv_close = qemu_rbd_close,
|
.bdrv_close = qemu_rbd_close,
|
||||||
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
|
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
|
||||||
@@ -1242,11 +1160,11 @@ static BlockDriver bdrv_rbd = {
|
|||||||
.bdrv_get_info = qemu_rbd_getinfo,
|
.bdrv_get_info = qemu_rbd_getinfo,
|
||||||
.create_opts = &qemu_rbd_create_opts,
|
.create_opts = &qemu_rbd_create_opts,
|
||||||
.bdrv_getlength = qemu_rbd_getlength,
|
.bdrv_getlength = qemu_rbd_getlength,
|
||||||
.bdrv_co_truncate = qemu_rbd_co_truncate,
|
.bdrv_truncate = qemu_rbd_truncate,
|
||||||
.protocol_name = "rbd",
|
.protocol_name = "rbd",
|
||||||
|
|
||||||
.bdrv_aio_preadv = qemu_rbd_aio_preadv,
|
.bdrv_aio_readv = qemu_rbd_aio_readv,
|
||||||
.bdrv_aio_pwritev = qemu_rbd_aio_pwritev,
|
.bdrv_aio_writev = qemu_rbd_aio_writev,
|
||||||
|
|
||||||
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
|
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
|
||||||
.bdrv_aio_flush = qemu_rbd_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);
|
replication_stop(s->rs, false, NULL);
|
||||||
}
|
}
|
||||||
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
|
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) {
|
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||||
@@ -246,14 +246,13 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
|
|||||||
backup_cow_request_begin(&req, child->bs->job,
|
backup_cow_request_begin(&req, child->bs->job,
|
||||||
sector_num * BDRV_SECTOR_SIZE,
|
sector_num * BDRV_SECTOR_SIZE,
|
||||||
remaining_bytes);
|
remaining_bytes);
|
||||||
ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors,
|
||||||
remaining_bytes, qiov, 0);
|
qiov);
|
||||||
backup_cow_request_end(&req);
|
backup_cow_request_end(&req);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov);
|
||||||
remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
|
|
||||||
out:
|
out:
|
||||||
return replication_return_value(s, ret);
|
return replication_return_value(s, ret);
|
||||||
}
|
}
|
||||||
@@ -261,8 +260,7 @@ out:
|
|||||||
static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num,
|
||||||
int remaining_sectors,
|
int remaining_sectors,
|
||||||
QEMUIOVector *qiov,
|
QEMUIOVector *qiov)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
BDRVReplicationState *s = bs->opaque;
|
BDRVReplicationState *s = bs->opaque;
|
||||||
QEMUIOVector hd_qiov;
|
QEMUIOVector hd_qiov;
|
||||||
@@ -273,15 +271,14 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
|||||||
int ret;
|
int ret;
|
||||||
int64_t n;
|
int64_t n;
|
||||||
|
|
||||||
assert(!flags);
|
|
||||||
ret = replication_get_io_status(s);
|
ret = replication_get_io_status(s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_writev(top, sector_num,
|
||||||
remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
|
remaining_sectors, qiov);
|
||||||
return replication_return_value(s, ret);
|
return replication_return_value(s, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,8 +304,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
|||||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count);
|
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count);
|
||||||
|
|
||||||
target = ret ? top : base;
|
target = ret ? top : base;
|
||||||
ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_writev(target, sector_num, n, &hd_qiov);
|
||||||
n * BDRV_SECTOR_SIZE, &hd_qiov, 0);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out1;
|
goto out1;
|
||||||
}
|
}
|
||||||
@@ -570,7 +566,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||||||
job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
||||||
0, MIRROR_SYNC_MODE_NONE, NULL, false,
|
0, MIRROR_SYNC_MODE_NONE, NULL, false,
|
||||||
BLOCKDEV_ON_ERROR_REPORT,
|
BLOCKDEV_ON_ERROR_REPORT,
|
||||||
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
|
||||||
backup_job_completed, bs, NULL, &local_err);
|
backup_job_completed, bs, NULL, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@@ -578,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
job_start(&job->job);
|
block_job_start(job);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
@@ -683,7 +679,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||||||
* disk, secondary disk in backup_job_completed().
|
* disk, secondary disk in backup_job_completed().
|
||||||
*/
|
*/
|
||||||
if (s->secondary_disk->bs->job) {
|
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) {
|
if (!failover) {
|
||||||
@@ -695,7 +691,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||||||
|
|
||||||
s->stage = BLOCK_REPLICATION_FAILOVER;
|
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||||
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
|
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);
|
NULL, replication_done, bs, true, errp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
#include "qemu/cutils.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)
|
static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
||||||
{
|
{
|
||||||
QDict *server = NULL;
|
QDict *server = NULL;
|
||||||
|
QObject *crumpled_server = NULL;
|
||||||
Visitor *iv = NULL;
|
Visitor *iv = NULL;
|
||||||
SocketAddress *saddr = NULL;
|
SocketAddress *saddr = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
qdict_extract_subqdict(options, &server, "server.");
|
qdict_extract_subqdict(options, &server, "server.");
|
||||||
|
|
||||||
iv = qobject_input_visitor_new_flat_confused(server, errp);
|
crumpled_server = qdict_crumple(server, errp);
|
||||||
if (!iv) {
|
if (!crumpled_server) {
|
||||||
goto done;
|
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);
|
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@@ -558,7 +567,8 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
visit_free(iv);
|
visit_free(iv);
|
||||||
qobject_unref(server);
|
qobject_decref(crumpled_server);
|
||||||
|
QDECREF(server);
|
||||||
return saddr;
|
return saddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,7 +582,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
|
|||||||
if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) {
|
if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) {
|
||||||
int ret = socket_set_nodelay(fd);
|
int ret = socket_set_nodelay(fd);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
warn_report("can't set TCP_NODELAY: %s", strerror(errno));
|
error_report("%s", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1026,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
|
|||||||
|
|
||||||
cfg->uri = uri = uri_parse(filename);
|
cfg->uri = uri = uri_parse(filename);
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
error_setg(&err, "invalid URI '%s'", filename);
|
error_setg(&err, "invalid URI");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1849,7 +1859,9 @@ out:
|
|||||||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||||
}
|
}
|
||||||
out_with_err_set:
|
out_with_err_set:
|
||||||
|
if (blk) {
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
|
}
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1871,11 +1883,11 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
|||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
qobject_unref(obj);
|
qobject_decref(obj);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict = qobject_to(QDict, obj);
|
qdict = qobject_to_qdict(obj);
|
||||||
qdict_flatten(qdict);
|
qdict_flatten(qdict);
|
||||||
|
|
||||||
qdict_put_str(qdict, "driver", "sheepdog");
|
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);
|
ret = sd_prealloc(bs, 0, size, errp);
|
||||||
fail:
|
fail:
|
||||||
bdrv_unref(bs);
|
bdrv_unref(bs);
|
||||||
qobject_unref(qdict);
|
QDECREF(qdict);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1975,7 +1987,6 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt)
|
|||||||
} else {
|
} else {
|
||||||
ret = qemu_strtol(n2, NULL, 10, &parity);
|
ret = qemu_strtol(n2, NULL, 10, &parity);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_free(redundancy);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2170,8 +2181,9 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
|||||||
{
|
{
|
||||||
BlockdevCreateOptions *create_options = NULL;
|
BlockdevCreateOptions *create_options = NULL;
|
||||||
QDict *qdict, *location_qdict;
|
QDict *qdict, *location_qdict;
|
||||||
|
QObject *crumpled;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
char *redundancy;
|
const char *redundancy;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -2205,14 +2217,16 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get the QAPI object */
|
/* Get the QAPI object */
|
||||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
crumpled = qdict_crumple(qdict, errp);
|
||||||
if (!v) {
|
if (crumpled == NULL) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v = qobject_input_visitor_new_keyval(crumpled);
|
||||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
|
qobject_decref(crumpled);
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, 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);
|
ret = sd_co_create(create_options, errp);
|
||||||
fail:
|
fail:
|
||||||
qapi_free_BlockdevCreateOptions(create_options);
|
qapi_free_BlockdevCreateOptions(create_options);
|
||||||
qobject_unref(qdict);
|
QDECREF(qdict);
|
||||||
g_free(redundancy);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2292,7 +2305,7 @@ static int64_t sd_getlength(BlockDriverState *bs)
|
|||||||
return s->inode.vdi_size;
|
return s->inode.vdi_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
|
static int sd_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVSheepdogState *s = bs->opaque;
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
@@ -2322,7 +2335,7 @@ static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* we don't need to update entire object */
|
/* 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;
|
s->inode.vdi_size = offset;
|
||||||
ret = write_object(fd, s->bs, (char *)&s->inode,
|
ret = write_object(fd, s->bs, (char *)&s->inode,
|
||||||
vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
|
vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
|
||||||
@@ -2599,17 +2612,15 @@ static void sd_aio_complete(SheepdogAIOCB *acb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||||
int nb_sectors, QEMUIOVector *qiov,
|
int nb_sectors, QEMUIOVector *qiov)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
SheepdogAIOCB acb;
|
SheepdogAIOCB acb;
|
||||||
int ret;
|
int ret;
|
||||||
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
|
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
|
||||||
BDRVSheepdogState *s = bs->opaque;
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
|
|
||||||
assert(!flags);
|
|
||||||
if (offset > s->inode.vdi_size) {
|
if (offset > s->inode.vdi_size) {
|
||||||
ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
|
ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -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));
|
strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
|
||||||
/* we don't need to update entire object */
|
/* 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);
|
inode = g_malloc(datalen);
|
||||||
|
|
||||||
/* refresh inode. */
|
/* refresh inode. */
|
||||||
@@ -2925,14 +2936,13 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||||||
QEMUSnapshotInfo *sn_tab = NULL;
|
QEMUSnapshotInfo *sn_tab = NULL;
|
||||||
unsigned wlen, rlen;
|
unsigned wlen, rlen;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
SheepdogInode *inode;
|
static SheepdogInode inode;
|
||||||
unsigned long *vdi_inuse;
|
unsigned long *vdi_inuse;
|
||||||
unsigned int start_nr;
|
unsigned int start_nr;
|
||||||
uint64_t hval;
|
uint64_t hval;
|
||||||
uint32_t vid;
|
uint32_t vid;
|
||||||
|
|
||||||
vdi_inuse = g_malloc(max);
|
vdi_inuse = g_malloc(max);
|
||||||
inode = g_malloc(SD_INODE_HEADER_SIZE);
|
|
||||||
|
|
||||||
fd = connect_to_sdog(s, &local_err);
|
fd = connect_to_sdog(s, &local_err);
|
||||||
if (fd < 0) {
|
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 */
|
/* 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),
|
vid_to_vdi_oid(vid),
|
||||||
0, SD_INODE_HEADER_SIZE, 0,
|
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
|
||||||
s->cache_flags);
|
s->cache_flags);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(inode->name, s->name) && is_snapshot(inode)) {
|
if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) {
|
||||||
sn_tab[found].date_sec = inode->snap_ctime >> 32;
|
sn_tab[found].date_sec = inode.snap_ctime >> 32;
|
||||||
sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff;
|
sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff;
|
||||||
sn_tab[found].vm_state_size = inode->vm_state_size;
|
sn_tab[found].vm_state_size = inode.vm_state_size;
|
||||||
sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec;
|
sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec;
|
||||||
|
|
||||||
snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str),
|
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,
|
pstrcpy(sn_tab[found].name,
|
||||||
MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)),
|
MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)),
|
||||||
inode->tag);
|
inode.tag);
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3004,7 +3014,6 @@ out:
|
|||||||
*psn_tab = sn_tab;
|
*psn_tab = sn_tab;
|
||||||
|
|
||||||
g_free(vdi_inuse);
|
g_free(vdi_inuse);
|
||||||
g_free(inode);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
@@ -3231,7 +3240,7 @@ static BlockDriver bdrv_sheepdog = {
|
|||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_getlength = sd_getlength,
|
.bdrv_getlength = sd_getlength,
|
||||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||||
.bdrv_co_truncate = sd_co_truncate,
|
.bdrv_truncate = sd_truncate,
|
||||||
|
|
||||||
.bdrv_co_readv = sd_co_readv,
|
.bdrv_co_readv = sd_co_readv,
|
||||||
.bdrv_co_writev = sd_co_writev,
|
.bdrv_co_writev = sd_co_writev,
|
||||||
@@ -3268,7 +3277,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
|
|||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_getlength = sd_getlength,
|
.bdrv_getlength = sd_getlength,
|
||||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||||
.bdrv_co_truncate = sd_co_truncate,
|
.bdrv_truncate = sd_truncate,
|
||||||
|
|
||||||
.bdrv_co_readv = sd_co_readv,
|
.bdrv_co_readv = sd_co_readv,
|
||||||
.bdrv_co_writev = sd_co_writev,
|
.bdrv_co_writev = sd_co_writev,
|
||||||
@@ -3305,7 +3314,7 @@ static BlockDriver bdrv_sheepdog_unix = {
|
|||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_getlength = sd_getlength,
|
.bdrv_getlength = sd_getlength,
|
||||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||||
.bdrv_co_truncate = sd_co_truncate,
|
.bdrv_truncate = sd_truncate,
|
||||||
|
|
||||||
.bdrv_co_readv = sd_co_readv,
|
.bdrv_co_readv = sd_co_readv,
|
||||||
.bdrv_co_writev = sd_co_writev,
|
.bdrv_co_writev = sd_co_writev,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "block/snapshot.h"
|
#include "block/snapshot.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
@@ -215,18 +214,16 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||||||
bdrv_ref(file);
|
bdrv_ref(file);
|
||||||
|
|
||||||
qdict_extract_subqdict(options, &file_options, "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));
|
qdict_put_str(options, "file", bdrv_get_node_name(file));
|
||||||
|
|
||||||
if (drv->bdrv_close) {
|
|
||||||
drv->bdrv_close(bs);
|
drv->bdrv_close(bs);
|
||||||
}
|
|
||||||
bdrv_unref_child(bs, bs->file);
|
bdrv_unref_child(bs, bs->file);
|
||||||
bs->file = NULL;
|
bs->file = NULL;
|
||||||
|
|
||||||
ret = bdrv_snapshot_goto(file, snapshot_id, errp);
|
ret = bdrv_snapshot_goto(file, snapshot_id, errp);
|
||||||
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
|
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
|
||||||
qobject_unref(options);
|
QDECREF(options);
|
||||||
if (open_ret < 0) {
|
if (open_ret < 0) {
|
||||||
bdrv_unref(file);
|
bdrv_unref(file);
|
||||||
bs->drv = NULL;
|
bs->drv = NULL;
|
||||||
|
|||||||
26
block/ssh.c
26
block/ssh.c
@@ -28,7 +28,6 @@
|
|||||||
#include <libssh2_sftp.h>
|
#include <libssh2_sftp.h>
|
||||||
|
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
@@ -606,6 +605,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
|||||||
BlockdevOptionsSsh *result = NULL;
|
BlockdevOptionsSsh *result = NULL;
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
QObject *crumpled;
|
||||||
const QDictEntry *e;
|
const QDictEntry *e;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
@@ -622,13 +622,23 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create the QAPI object */
|
/* Create the QAPI object */
|
||||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
crumpled = qdict_crumple(options, errp);
|
||||||
if (!v) {
|
if (crumpled == NULL) {
|
||||||
goto fail;
|
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_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
|
qobject_decref(crumpled);
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, 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);
|
ret = ssh_co_create(create_options, errp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qobject_unref(uri_options);
|
QDECREF(uri_options);
|
||||||
qapi_free_BlockdevCreateOptions(create_options);
|
qapi_free_BlockdevCreateOptions(create_options);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1154,13 +1164,11 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
|||||||
|
|
||||||
static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
|
static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num,
|
||||||
int nb_sectors, QEMUIOVector *qiov,
|
int nb_sectors, QEMUIOVector *qiov)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
BDRVSSHState *s = bs->opaque;
|
BDRVSSHState *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
assert(!flags);
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
|
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
|
||||||
nb_sectors * BDRV_SECTOR_SIZE, qiov);
|
nb_sectors * BDRV_SECTOR_SIZE, qiov);
|
||||||
@@ -1243,7 +1251,7 @@ static int64_t ssh_getlength(BlockDriverState *bs)
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
|
static int ssh_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVSSHState *s = bs->opaque;
|
BDRVSSHState *s = bs->opaque;
|
||||||
@@ -1279,7 +1287,7 @@ static BlockDriver bdrv_ssh = {
|
|||||||
.bdrv_co_readv = ssh_co_readv,
|
.bdrv_co_readv = ssh_co_readv,
|
||||||
.bdrv_co_writev = ssh_co_writev,
|
.bdrv_co_writev = ssh_co_writev,
|
||||||
.bdrv_getlength = ssh_getlength,
|
.bdrv_getlength = ssh_getlength,
|
||||||
.bdrv_co_truncate = ssh_co_truncate,
|
.bdrv_truncate = ssh_truncate,
|
||||||
.bdrv_co_flush_to_disk = ssh_co_flush,
|
.bdrv_co_flush_to_disk = ssh_co_flush,
|
||||||
.create_opts = &ssh_create_opts,
|
.create_opts = &ssh_create_opts,
|
||||||
};
|
};
|
||||||
|
|||||||
103
block/stream.c
103
block/stream.c
@@ -29,8 +29,11 @@ enum {
|
|||||||
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
|
|
||||||
typedef struct StreamBlockJob {
|
typedef struct StreamBlockJob {
|
||||||
BlockJob common;
|
BlockJob common;
|
||||||
|
RateLimit limit;
|
||||||
BlockDriverState *base;
|
BlockDriverState *base;
|
||||||
BlockdevOnError on_error;
|
BlockdevOnError on_error;
|
||||||
char *backing_file_str;
|
char *backing_file_str;
|
||||||
@@ -54,16 +57,20 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
|
|||||||
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
|
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stream_prepare(Job *job)
|
typedef struct {
|
||||||
|
int ret;
|
||||||
|
} StreamCompleteData;
|
||||||
|
|
||||||
|
static void stream_complete(BlockJob *job, void *opaque)
|
||||||
{
|
{
|
||||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
|
||||||
BlockJob *bjob = &s->common;
|
StreamCompleteData *data = opaque;
|
||||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
BlockDriverState *bs = blk_bs(job->blk);
|
||||||
BlockDriverState *base = s->base;
|
BlockDriverState *base = s->base;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (bs->backing) {
|
if (!block_job_is_cancelled(&s->common) && bs->backing &&
|
||||||
|
data->ret == 0) {
|
||||||
const char *base_id = NULL, *base_fmt = NULL;
|
const char *base_id = NULL, *base_fmt = NULL;
|
||||||
if (base) {
|
if (base) {
|
||||||
base_id = s->backing_file_str;
|
base_id = s->backing_file_str;
|
||||||
@@ -71,40 +78,35 @@ static int stream_prepare(Job *job)
|
|||||||
base_fmt = base->drv->format_name;
|
base_fmt = base->drv->format_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = bdrv_change_backing_file(bs, base_id, base_fmt);
|
data->ret = bdrv_change_backing_file(bs, base_id, base_fmt);
|
||||||
bdrv_set_backing_hd(bs, base, &local_err);
|
bdrv_set_backing_hd(bs, base, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
return -EPERM;
|
data->ret = -EPERM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
out:
|
||||||
}
|
|
||||||
|
|
||||||
static void stream_clean(Job *job)
|
|
||||||
{
|
|
||||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
|
||||||
BlockJob *bjob = &s->common;
|
|
||||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
|
||||||
|
|
||||||
/* Reopen the image back in read-only mode if necessary */
|
/* Reopen the image back in read-only mode if necessary */
|
||||||
if (s->bs_flags != bdrv_get_flags(bs)) {
|
if (s->bs_flags != bdrv_get_flags(bs)) {
|
||||||
/* Give up write permissions before making it read-only */
|
/* 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);
|
bdrv_reopen(bs, s->bs_flags, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(s->backing_file_str);
|
g_free(s->backing_file_str);
|
||||||
|
block_job_completed(&s->common, data->ret);
|
||||||
|
g_free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn stream_run(Job *job, Error **errp)
|
static void coroutine_fn stream_run(void *opaque)
|
||||||
{
|
{
|
||||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
StreamBlockJob *s = opaque;
|
||||||
|
StreamCompleteData *data;
|
||||||
BlockBackend *blk = s->common.blk;
|
BlockBackend *blk = s->common.blk;
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
BlockDriverState *base = s->base;
|
BlockDriverState *base = s->base;
|
||||||
int64_t len;
|
|
||||||
int64_t offset = 0;
|
int64_t offset = 0;
|
||||||
uint64_t delay_ns = 0;
|
uint64_t delay_ns = 0;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
@@ -116,12 +118,11 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = bdrv_getlength(bs);
|
s->common.len = bdrv_getlength(bs);
|
||||||
if (len < 0) {
|
if (s->common.len < 0) {
|
||||||
ret = len;
|
ret = s->common.len;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
job_progress_set_remaining(&s->common.job, len);
|
|
||||||
|
|
||||||
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
|
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
|
||||||
|
|
||||||
@@ -134,14 +135,14 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
bdrv_enable_copy_on_read(bs);
|
bdrv_enable_copy_on_read(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( ; offset < len; offset += n) {
|
for ( ; offset < s->common.len; offset += n) {
|
||||||
bool copy;
|
bool copy;
|
||||||
|
|
||||||
/* Note that even when no rate limit is applied we need to yield
|
/* 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.
|
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||||
*/
|
*/
|
||||||
job_sleep_ns(&s->common.job, delay_ns);
|
block_job_sleep_ns(&s->common, delay_ns);
|
||||||
if (job_is_cancelled(&s->common.job)) {
|
if (block_job_is_cancelled(&s->common)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +159,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
|
|
||||||
/* Finish early if end of backing file has been reached */
|
/* Finish early if end of backing file has been reached */
|
||||||
if (ret == 0 && n == 0) {
|
if (ret == 0 && n == 0) {
|
||||||
n = len - offset;
|
n = s->common.len - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy = (ret == 1);
|
copy = (ret == 1);
|
||||||
@@ -184,11 +185,9 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
/* Publish progress */
|
/* Publish progress */
|
||||||
job_progress_update(&s->common.job, n);
|
s->common.offset += n;
|
||||||
if (copy) {
|
if (copy && s->common.speed) {
|
||||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||||
} else {
|
|
||||||
delay_ns = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,26 +202,32 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
/* Modify backing chain and close BDSes in main loop */
|
/* Modify backing chain and close BDSes in main loop */
|
||||||
return ret;
|
data = g_malloc(sizeof(*data));
|
||||||
|
data->ret = ret;
|
||||||
|
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 = {
|
static const BlockJobDriver stream_job_driver = {
|
||||||
.job_driver = {
|
|
||||||
.instance_size = sizeof(StreamBlockJob),
|
.instance_size = sizeof(StreamBlockJob),
|
||||||
.job_type = JOB_TYPE_STREAM,
|
.job_type = BLOCK_JOB_TYPE_STREAM,
|
||||||
.free = block_job_free,
|
.set_speed = stream_set_speed,
|
||||||
.run = stream_run,
|
.start = stream_run,
|
||||||
.prepare = stream_prepare,
|
|
||||||
.clean = stream_clean,
|
|
||||||
.user_resume = block_job_user_resume,
|
|
||||||
.drain = block_job_drain,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void stream_start(const char *job_id, BlockDriverState *bs,
|
void stream_start(const char *job_id, BlockDriverState *bs,
|
||||||
BlockDriverState *base, const char *backing_file_str,
|
BlockDriverState *base, const char *backing_file_str,
|
||||||
int creation_flags, int64_t speed,
|
int64_t speed, BlockdevOnError on_error, Error **errp)
|
||||||
BlockdevOnError on_error, Error **errp)
|
|
||||||
{
|
{
|
||||||
StreamBlockJob *s;
|
StreamBlockJob *s;
|
||||||
BlockDriverState *iter;
|
BlockDriverState *iter;
|
||||||
@@ -239,12 +244,12 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||||||
/* Prevent concurrent jobs trying to modify the graph structure here, we
|
/* 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
|
* already have our own plans. Also don't allow resize as the image size is
|
||||||
* queried only at the job start and then cached. */
|
* 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_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||||
BLK_PERM_GRAPH_MOD,
|
BLK_PERM_GRAPH_MOD,
|
||||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||||
BLK_PERM_WRITE,
|
BLK_PERM_WRITE,
|
||||||
speed, creation_flags, NULL, NULL, errp);
|
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -265,7 +270,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||||||
|
|
||||||
s->on_error = on_error;
|
s->on_error = on_error;
|
||||||
trace_stream_start(bs, base, s);
|
trace_stream_start(bs, base, s);
|
||||||
job_start(&s->common.job);
|
block_job_start(&s->common);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
|
|
||||||
static void throttle_group_obj_init(Object *obj);
|
static void throttle_group_obj_init(Object *obj);
|
||||||
static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
|
static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
|
||||||
static void timer_cb(ThrottleGroupMember *tgm, bool is_write);
|
|
||||||
|
|
||||||
/* The ThrottleGroup structure (with its ThrottleState) is shared
|
/* The ThrottleGroup structure (with its ThrottleState) is shared
|
||||||
* among different ThrottleGroupMembers and it's independent from
|
* among different ThrottleGroupMembers and it's independent from
|
||||||
@@ -222,15 +221,6 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
|
|||||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||||
ThrottleGroupMember *token, *start;
|
ThrottleGroupMember *token, *start;
|
||||||
|
|
||||||
/* If this member has its I/O limits disabled then it means that
|
|
||||||
* it's being drained. Skip the round-robin search and return tgm
|
|
||||||
* immediately if it has pending requests. Otherwise we could be
|
|
||||||
* forcing it to wait for other member's throttled requests. */
|
|
||||||
if (tgm_has_pending_reqs(tgm, is_write) &&
|
|
||||||
atomic_read(&tgm->io_limits_disabled)) {
|
|
||||||
return tgm;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = token = tg->tokens[is_write];
|
start = token = tg->tokens[is_write];
|
||||||
|
|
||||||
/* get next bs round in round robin style */
|
/* get next bs round in round robin style */
|
||||||
@@ -425,31 +415,15 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
|
|||||||
rd->tgm = tgm;
|
rd->tgm = tgm;
|
||||||
rd->is_write = is_write;
|
rd->is_write = is_write;
|
||||||
|
|
||||||
/* This function is called when a timer is fired or when
|
|
||||||
* throttle_group_restart_tgm() is called. Either way, there can
|
|
||||||
* be no timer pending on this tgm at this point */
|
|
||||||
assert(!timer_pending(tgm->throttle_timers.timers[is_write]));
|
|
||||||
|
|
||||||
co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd);
|
co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd);
|
||||||
aio_co_enter(tgm->aio_context, co);
|
aio_co_enter(tgm->aio_context, co);
|
||||||
}
|
}
|
||||||
|
|
||||||
void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
|
void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
if (tgm->throttle_state) {
|
if (tgm->throttle_state) {
|
||||||
for (i = 0; i < 2; i++) {
|
throttle_group_restart_queue(tgm, 0);
|
||||||
QEMUTimer *t = tgm->throttle_timers.timers[i];
|
throttle_group_restart_queue(tgm, 1);
|
||||||
if (timer_pending(t)) {
|
|
||||||
/* If there's a pending timer on this tgm, fire it now */
|
|
||||||
timer_del(t);
|
|
||||||
timer_cb(tgm, i);
|
|
||||||
} else {
|
|
||||||
/* Else run the next request from the queue manually */
|
|
||||||
throttle_group_restart_queue(tgm, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,11 +558,12 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
|
||||||
|
assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
|
||||||
|
assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
|
||||||
|
|
||||||
qemu_mutex_lock(&tg->lock);
|
qemu_mutex_lock(&tg->lock);
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
assert(tgm->pending_reqs[i] == 0);
|
|
||||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[i]));
|
|
||||||
assert(!timer_pending(tgm->throttle_timers.timers[i]));
|
|
||||||
if (tg->tokens[i] == tgm) {
|
if (tg->tokens[i] == tgm) {
|
||||||
token = throttle_group_next_tgm(tgm);
|
token = throttle_group_next_tgm(tgm);
|
||||||
/* Take care of the case where this is the last tgm in the group */
|
/* Take care of the case where this is the last tgm in the group */
|
||||||
|
|||||||
@@ -36,12 +36,9 @@ static QemuOptsList throttle_opts = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static int throttle_configure_tgm(BlockDriverState *bs,
|
||||||
* If this function succeeds then the throttle group name is stored in
|
ThrottleGroupMember *tgm,
|
||||||
* @group and must be freed by the caller.
|
QDict *options, Error **errp)
|
||||||
* If there's an error then @group remains unmodified.
|
|
||||||
*/
|
|
||||||
static int throttle_parse_options(QDict *options, char **group, Error **errp)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
const char *group_name;
|
const char *group_name;
|
||||||
@@ -66,7 +63,8 @@ static int throttle_parse_options(QDict *options, char **group, Error **errp)
|
|||||||
goto fin;
|
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;
|
ret = 0;
|
||||||
fin:
|
fin:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
@@ -77,27 +75,16 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
|
|||||||
int flags, Error **errp)
|
int flags, Error **errp)
|
||||||
{
|
{
|
||||||
ThrottleGroupMember *tgm = bs->opaque;
|
ThrottleGroupMember *tgm = bs->opaque;
|
||||||
char *group;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs,
|
bs->file = bdrv_open_child(NULL, options, "file", bs,
|
||||||
&child_file, false, errp);
|
&child_file, false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
bs->supported_write_flags = bs->file->bs->supported_write_flags |
|
bs->supported_write_flags = bs->file->bs->supported_write_flags;
|
||||||
BDRV_REQ_WRITE_UNCHANGED;
|
bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
|
||||||
bs->supported_zero_flags = bs->file->bs->supported_zero_flags |
|
|
||||||
BDRV_REQ_WRITE_UNCHANGED;
|
|
||||||
|
|
||||||
ret = throttle_parse_options(options, &group, errp);
|
return throttle_configure_tgm(bs, tgm, options, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void throttle_close(BlockDriverState *bs)
|
static void throttle_close(BlockDriverState *bs)
|
||||||
@@ -149,7 +136,7 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
|
|||||||
ThrottleGroupMember *tgm = bs->opaque;
|
ThrottleGroupMember *tgm = bs->opaque;
|
||||||
throttle_group_co_io_limits_intercept(tgm, bytes, true);
|
throttle_group_co_io_limits_intercept(tgm, bytes, true);
|
||||||
|
|
||||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int throttle_co_flush(BlockDriverState *bs)
|
static int throttle_co_flush(BlockDriverState *bs)
|
||||||
@@ -173,36 +160,35 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
|
|||||||
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
|
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
BlockReopenQueue *queue, Error **errp)
|
BlockReopenQueue *queue, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
ThrottleGroupMember *tgm;
|
||||||
char *group = NULL;
|
|
||||||
|
|
||||||
assert(reopen_state != NULL);
|
assert(reopen_state != NULL);
|
||||||
assert(reopen_state->bs != NULL);
|
assert(reopen_state->bs != NULL);
|
||||||
|
|
||||||
ret = throttle_parse_options(reopen_state->options, &group, errp);
|
reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
|
||||||
reopen_state->opaque = group;
|
tgm = reopen_state->opaque;
|
||||||
return ret;
|
|
||||||
|
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
|
||||||
|
errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
|
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = reopen_state->bs;
|
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
|
||||||
ThrottleGroupMember *tgm = bs->opaque;
|
ThrottleGroupMember *new_tgm = reopen_state->opaque;
|
||||||
char *group = reopen_state->opaque;
|
|
||||||
|
|
||||||
assert(group);
|
throttle_group_unregister_tgm(old_tgm);
|
||||||
|
g_free(old_tgm);
|
||||||
if (strcmp(group, throttle_group_get_name(tgm))) {
|
reopen_state->bs->opaque = new_tgm;
|
||||||
throttle_group_unregister_tgm(tgm);
|
|
||||||
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
|
||||||
}
|
|
||||||
g_free(reopen_state->opaque);
|
|
||||||
reopen_state->opaque = NULL;
|
reopen_state->opaque = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
|
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;
|
reopen_state->opaque = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,12 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
|
|||||||
# block/block-backend.c
|
# block/block-backend.c
|
||||||
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
|
blk_co_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_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
|
# block/io.c
|
||||||
bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
|
bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
|
||||||
bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
|
bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
|
||||||
bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x"
|
bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x"
|
||||||
bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64
|
bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64
|
||||||
bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x"
|
|
||||||
bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x"
|
|
||||||
|
|
||||||
# block/stream.c
|
# block/stream.c
|
||||||
stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
|
stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
|
||||||
@@ -44,22 +40,18 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
|
|||||||
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
|
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
|
||||||
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||||
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||||
backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
|
||||||
|
|
||||||
# blockdev.c
|
# blockdev.c
|
||||||
qmp_block_job_cancel(void *job) "job %p"
|
qmp_block_job_cancel(void *job) "job %p"
|
||||||
qmp_block_job_pause(void *job) "job %p"
|
qmp_block_job_pause(void *job) "job %p"
|
||||||
qmp_block_job_resume(void *job) "job %p"
|
qmp_block_job_resume(void *job) "job %p"
|
||||||
qmp_block_job_complete(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"
|
qmp_block_stream(void *bs, void *job) "bs %p job %p"
|
||||||
|
|
||||||
# block/file-win32.c
|
# block/file-win32.c
|
||||||
# block/file-posix.c
|
# block/file-posix.c
|
||||||
file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d"
|
paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d"
|
||||||
file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
|
paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
|
||||||
file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64
|
|
||||||
|
|
||||||
# block/qcow2.c
|
# block/qcow2.c
|
||||||
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
|
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
|
||||||
@@ -153,6 +145,3 @@ nvme_free_req_queue_wait(void *q) "q %p"
|
|||||||
nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d"
|
nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d"
|
||||||
nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
|
nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
|
||||||
nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d"
|
nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d"
|
||||||
|
|
||||||
# block/iscsi.c
|
|
||||||
iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d"
|
|
||||||
|
|||||||
265
block/vdi.c
265
block/vdi.c
@@ -50,12 +50,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/units.h"
|
|
||||||
#include "qapi/error.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/block_int.h"
|
||||||
#include "block/qdict.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
@@ -84,8 +80,11 @@
|
|||||||
/* Command line option for static images. */
|
/* Command line option for static images. */
|
||||||
#define BLOCK_OPT_STATIC "static"
|
#define BLOCK_OPT_STATIC "static"
|
||||||
|
|
||||||
|
#define KiB 1024
|
||||||
|
#define MiB (KiB * KiB)
|
||||||
|
|
||||||
#define SECTOR_SIZE 512
|
#define SECTOR_SIZE 512
|
||||||
#define DEFAULT_CLUSTER_SIZE S_1MiB
|
#define DEFAULT_CLUSTER_SIZE (1 * MiB)
|
||||||
|
|
||||||
#if defined(CONFIG_VDI_DEBUG)
|
#if defined(CONFIG_VDI_DEBUG)
|
||||||
#define VDI_DEBUG 1
|
#define VDI_DEBUG 1
|
||||||
@@ -141,8 +140,6 @@
|
|||||||
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
|
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
|
||||||
(uint64_t)DEFAULT_CLUSTER_SIZE)
|
(uint64_t)DEFAULT_CLUSTER_SIZE)
|
||||||
|
|
||||||
static QemuOptsList vdi_create_opts;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char text[0x40];
|
char text[0x40];
|
||||||
uint32_t signature;
|
uint32_t signature;
|
||||||
@@ -187,22 +184,22 @@ typedef struct {
|
|||||||
|
|
||||||
static void vdi_header_to_cpu(VdiHeader *header)
|
static void vdi_header_to_cpu(VdiHeader *header)
|
||||||
{
|
{
|
||||||
header->signature = le32_to_cpu(header->signature);
|
le32_to_cpus(&header->signature);
|
||||||
header->version = le32_to_cpu(header->version);
|
le32_to_cpus(&header->version);
|
||||||
header->header_size = le32_to_cpu(header->header_size);
|
le32_to_cpus(&header->header_size);
|
||||||
header->image_type = le32_to_cpu(header->image_type);
|
le32_to_cpus(&header->image_type);
|
||||||
header->image_flags = le32_to_cpu(header->image_flags);
|
le32_to_cpus(&header->image_flags);
|
||||||
header->offset_bmap = le32_to_cpu(header->offset_bmap);
|
le32_to_cpus(&header->offset_bmap);
|
||||||
header->offset_data = le32_to_cpu(header->offset_data);
|
le32_to_cpus(&header->offset_data);
|
||||||
header->cylinders = le32_to_cpu(header->cylinders);
|
le32_to_cpus(&header->cylinders);
|
||||||
header->heads = le32_to_cpu(header->heads);
|
le32_to_cpus(&header->heads);
|
||||||
header->sectors = le32_to_cpu(header->sectors);
|
le32_to_cpus(&header->sectors);
|
||||||
header->sector_size = le32_to_cpu(header->sector_size);
|
le32_to_cpus(&header->sector_size);
|
||||||
header->disk_size = le64_to_cpu(header->disk_size);
|
le64_to_cpus(&header->disk_size);
|
||||||
header->block_size = le32_to_cpu(header->block_size);
|
le32_to_cpus(&header->block_size);
|
||||||
header->block_extra = le32_to_cpu(header->block_extra);
|
le32_to_cpus(&header->block_extra);
|
||||||
header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
|
le32_to_cpus(&header->blocks_in_image);
|
||||||
header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
|
le32_to_cpus(&header->blocks_allocated);
|
||||||
qemu_uuid_bswap(&header->uuid_image);
|
qemu_uuid_bswap(&header->uuid_image);
|
||||||
qemu_uuid_bswap(&header->uuid_last_snap);
|
qemu_uuid_bswap(&header->uuid_last_snap);
|
||||||
qemu_uuid_bswap(&header->uuid_link);
|
qemu_uuid_bswap(&header->uuid_link);
|
||||||
@@ -211,28 +208,29 @@ static void vdi_header_to_cpu(VdiHeader *header)
|
|||||||
|
|
||||||
static void vdi_header_to_le(VdiHeader *header)
|
static void vdi_header_to_le(VdiHeader *header)
|
||||||
{
|
{
|
||||||
header->signature = cpu_to_le32(header->signature);
|
cpu_to_le32s(&header->signature);
|
||||||
header->version = cpu_to_le32(header->version);
|
cpu_to_le32s(&header->version);
|
||||||
header->header_size = cpu_to_le32(header->header_size);
|
cpu_to_le32s(&header->header_size);
|
||||||
header->image_type = cpu_to_le32(header->image_type);
|
cpu_to_le32s(&header->image_type);
|
||||||
header->image_flags = cpu_to_le32(header->image_flags);
|
cpu_to_le32s(&header->image_flags);
|
||||||
header->offset_bmap = cpu_to_le32(header->offset_bmap);
|
cpu_to_le32s(&header->offset_bmap);
|
||||||
header->offset_data = cpu_to_le32(header->offset_data);
|
cpu_to_le32s(&header->offset_data);
|
||||||
header->cylinders = cpu_to_le32(header->cylinders);
|
cpu_to_le32s(&header->cylinders);
|
||||||
header->heads = cpu_to_le32(header->heads);
|
cpu_to_le32s(&header->heads);
|
||||||
header->sectors = cpu_to_le32(header->sectors);
|
cpu_to_le32s(&header->sectors);
|
||||||
header->sector_size = cpu_to_le32(header->sector_size);
|
cpu_to_le32s(&header->sector_size);
|
||||||
header->disk_size = cpu_to_le64(header->disk_size);
|
cpu_to_le64s(&header->disk_size);
|
||||||
header->block_size = cpu_to_le32(header->block_size);
|
cpu_to_le32s(&header->block_size);
|
||||||
header->block_extra = cpu_to_le32(header->block_extra);
|
cpu_to_le32s(&header->block_extra);
|
||||||
header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
|
cpu_to_le32s(&header->blocks_in_image);
|
||||||
header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
|
cpu_to_le32s(&header->blocks_allocated);
|
||||||
qemu_uuid_bswap(&header->uuid_image);
|
qemu_uuid_bswap(&header->uuid_image);
|
||||||
qemu_uuid_bswap(&header->uuid_last_snap);
|
qemu_uuid_bswap(&header->uuid_last_snap);
|
||||||
qemu_uuid_bswap(&header->uuid_link);
|
qemu_uuid_bswap(&header->uuid_link);
|
||||||
qemu_uuid_bswap(&header->uuid_parent);
|
qemu_uuid_bswap(&header->uuid_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_VDI_DEBUG)
|
||||||
static void vdi_header_print(VdiHeader *header)
|
static void vdi_header_print(VdiHeader *header)
|
||||||
{
|
{
|
||||||
char uuid[37];
|
char uuid[37];
|
||||||
@@ -254,15 +252,16 @@ static void vdi_header_print(VdiHeader *header)
|
|||||||
logout("block extra 0x%04x\n", header->block_extra);
|
logout("block extra 0x%04x\n", header->block_extra);
|
||||||
logout("blocks tot. 0x%04x\n", header->blocks_in_image);
|
logout("blocks tot. 0x%04x\n", header->blocks_in_image);
|
||||||
logout("blocks all. 0x%04x\n", header->blocks_allocated);
|
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);
|
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);
|
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);
|
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);
|
logout("uuid parent %s\n", uuid);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
|
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
BdrvCheckMode fix)
|
BdrvCheckMode fix)
|
||||||
@@ -383,9 +382,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
vdi_header_to_cpu(&header);
|
vdi_header_to_cpu(&header);
|
||||||
if (VDI_DEBUG) {
|
#if defined(CONFIG_VDI_DEBUG)
|
||||||
vdi_header_print(&header);
|
vdi_header_print(&header);
|
||||||
}
|
#endif
|
||||||
|
|
||||||
if (header.disk_size > VDI_DISK_SIZE_MAX) {
|
if (header.disk_size > VDI_DISK_SIZE_MAX) {
|
||||||
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
|
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||||
@@ -432,8 +431,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
|
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
|
||||||
error_setg(errp, "unsupported VDI image (block size %" PRIu32
|
error_setg(errp, "unsupported VDI image (block size %" PRIu32
|
||||||
" is not %" PRIu32 ")",
|
" is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||||
header.block_size, DEFAULT_CLUSTER_SIZE);
|
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.disk_size >
|
} else if (header.disk_size >
|
||||||
@@ -718,59 +716,36 @@ nonallocating_write:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
|
||||||
size_t block_size, Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevCreateOptionsVdi *vdi_opts;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint64_t bytes = 0;
|
uint64_t bytes = 0;
|
||||||
uint32_t blocks;
|
uint32_t blocks;
|
||||||
uint32_t image_type;
|
size_t block_size = DEFAULT_CLUSTER_SIZE;
|
||||||
|
uint32_t image_type = VDI_TYPE_DYNAMIC;
|
||||||
VdiHeader header;
|
VdiHeader header;
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t bmap_size;
|
size_t bmap_size;
|
||||||
int64_t offset = 0;
|
int64_t offset = 0;
|
||||||
BlockDriverState *bs_file = NULL;
|
Error *local_err = NULL;
|
||||||
BlockBackend *blk = NULL;
|
BlockBackend *blk = NULL;
|
||||||
uint32_t *bmap = NULL;
|
uint32_t *bmap = NULL;
|
||||||
|
|
||||||
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
|
|
||||||
vdi_opts = &create_options->u.vdi;
|
|
||||||
|
|
||||||
logout("\n");
|
logout("\n");
|
||||||
|
|
||||||
/* Validate options and set default values */
|
/* Read out options. */
|
||||||
bytes = vdi_opts->size;
|
bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
|
BDRV_SECTOR_SIZE);
|
||||||
if (!vdi_opts->has_preallocation) {
|
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||||
vdi_opts->preallocation = PREALLOC_MODE_OFF;
|
/* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
|
||||||
}
|
block_size = qemu_opt_get_size_del(opts,
|
||||||
switch (vdi_opts->preallocation) {
|
BLOCK_OPT_CLUSTER_SIZE,
|
||||||
case PREALLOC_MODE_OFF:
|
DEFAULT_CLUSTER_SIZE);
|
||||||
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;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifndef CONFIG_VDI_BLOCK_SIZE
|
#if defined(CONFIG_VDI_STATIC_IMAGE)
|
||||||
if (block_size != DEFAULT_CLUSTER_SIZE) {
|
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
|
||||||
ret = -ENOTSUP;
|
image_type = VDI_TYPE_STATIC;
|
||||||
error_setg(errp,
|
|
||||||
"A non-default cluster size is not supported in this build");
|
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -782,16 +757,18 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create BlockBackend to write to the image */
|
ret = bdrv_create_file(filename, opts, &local_err);
|
||||||
bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp);
|
if (ret < 0) {
|
||||||
if (!bs_file) {
|
error_propagate(errp, local_err);
|
||||||
ret = -EIO;
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
blk = blk_new_open(filename, NULL, NULL,
|
||||||
ret = blk_insert_bs(blk, bs_file, errp);
|
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||||
if (ret < 0) {
|
&local_err);
|
||||||
|
if (blk == NULL) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,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_image);
|
||||||
qemu_uuid_generate(&header.uuid_last_snap);
|
qemu_uuid_generate(&header.uuid_last_snap);
|
||||||
/* There is no need to set header.uuid_link or header.uuid_parent here. */
|
/* There is no need to set header.uuid_link or header.uuid_parent here. */
|
||||||
if (VDI_DEBUG) {
|
#if defined(CONFIG_VDI_DEBUG)
|
||||||
vdi_header_print(&header);
|
vdi_header_print(&header);
|
||||||
}
|
#endif
|
||||||
vdi_header_to_le(&header);
|
vdi_header_to_le(&header);
|
||||||
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
|
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Error writing header");
|
error_setg(errp, "Error writing header to %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
offset += sizeof(header);
|
offset += sizeof(header);
|
||||||
@@ -849,7 +826,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
|||||||
}
|
}
|
||||||
ret = blk_pwrite(blk, offset, bmap, bmap_size, 0);
|
ret = blk_pwrite(blk, offset, bmap, bmap_size, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Error writing bmap");
|
error_setg(errp, "Error writing bmap to %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
offset += bmap_size;
|
offset += bmap_size;
|
||||||
@@ -859,108 +836,17 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
|||||||
ret = blk_truncate(blk, offset + blocks * block_size,
|
ret = blk_truncate(blk, offset + blocks * block_size,
|
||||||
PREALLOC_MODE_OFF, errp);
|
PREALLOC_MODE_OFF, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_prepend(errp, "Failed to statically allocate file");
|
error_prepend(errp, "Failed to statically allocate %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
exit:
|
exit:
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
bdrv_unref(bs_file);
|
|
||||||
g_free(bmap);
|
g_free(bmap);
|
||||||
return ret;
|
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)
|
static void vdi_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVVdiState *s = bs->opaque;
|
BDRVVdiState *s = bs->opaque;
|
||||||
@@ -1009,7 +895,6 @@ static BlockDriver bdrv_vdi = {
|
|||||||
.bdrv_close = vdi_close,
|
.bdrv_close = vdi_close,
|
||||||
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
||||||
.bdrv_child_perm = bdrv_format_default_perms,
|
.bdrv_child_perm = bdrv_format_default_perms,
|
||||||
.bdrv_co_create = vdi_co_create,
|
|
||||||
.bdrv_co_create_opts = vdi_co_create_opts,
|
.bdrv_co_create_opts = vdi_co_create_opts,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_block_status = vdi_co_block_status,
|
.bdrv_co_block_status = vdi_co_block_status,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "vhdx.h"
|
#include "block/vhdx.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All the VHDX formats on disk are little endian - the following
|
* All the VHDX formats on disk are little endian - the following
|
||||||
@@ -35,18 +35,18 @@ void vhdx_header_le_import(VHDXHeader *h)
|
|||||||
{
|
{
|
||||||
assert(h != NULL);
|
assert(h != NULL);
|
||||||
|
|
||||||
h->signature = le32_to_cpu(h->signature);
|
le32_to_cpus(&h->signature);
|
||||||
h->checksum = le32_to_cpu(h->checksum);
|
le32_to_cpus(&h->checksum);
|
||||||
h->sequence_number = le64_to_cpu(h->sequence_number);
|
le64_to_cpus(&h->sequence_number);
|
||||||
|
|
||||||
leguid_to_cpus(&h->file_write_guid);
|
leguid_to_cpus(&h->file_write_guid);
|
||||||
leguid_to_cpus(&h->data_write_guid);
|
leguid_to_cpus(&h->data_write_guid);
|
||||||
leguid_to_cpus(&h->log_guid);
|
leguid_to_cpus(&h->log_guid);
|
||||||
|
|
||||||
h->log_version = le16_to_cpu(h->log_version);
|
le16_to_cpus(&h->log_version);
|
||||||
h->version = le16_to_cpu(h->version);
|
le16_to_cpus(&h->version);
|
||||||
h->log_length = le32_to_cpu(h->log_length);
|
le32_to_cpus(&h->log_length);
|
||||||
h->log_offset = le64_to_cpu(h->log_offset);
|
le64_to_cpus(&h->log_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
|
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
|
||||||
@@ -80,68 +80,68 @@ void vhdx_log_desc_le_import(VHDXLogDescriptor *d)
|
|||||||
{
|
{
|
||||||
assert(d != NULL);
|
assert(d != NULL);
|
||||||
|
|
||||||
d->signature = le32_to_cpu(d->signature);
|
le32_to_cpus(&d->signature);
|
||||||
d->file_offset = le64_to_cpu(d->file_offset);
|
le64_to_cpus(&d->file_offset);
|
||||||
d->sequence_number = le64_to_cpu(d->sequence_number);
|
le64_to_cpus(&d->sequence_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
|
void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
|
||||||
{
|
{
|
||||||
assert(d != NULL);
|
assert(d != NULL);
|
||||||
|
|
||||||
d->signature = cpu_to_le32(d->signature);
|
cpu_to_le32s(&d->signature);
|
||||||
d->trailing_bytes = cpu_to_le32(d->trailing_bytes);
|
cpu_to_le32s(&d->trailing_bytes);
|
||||||
d->leading_bytes = cpu_to_le64(d->leading_bytes);
|
cpu_to_le64s(&d->leading_bytes);
|
||||||
d->file_offset = cpu_to_le64(d->file_offset);
|
cpu_to_le64s(&d->file_offset);
|
||||||
d->sequence_number = cpu_to_le64(d->sequence_number);
|
cpu_to_le64s(&d->sequence_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_log_data_le_import(VHDXLogDataSector *d)
|
void vhdx_log_data_le_import(VHDXLogDataSector *d)
|
||||||
{
|
{
|
||||||
assert(d != NULL);
|
assert(d != NULL);
|
||||||
|
|
||||||
d->data_signature = le32_to_cpu(d->data_signature);
|
le32_to_cpus(&d->data_signature);
|
||||||
d->sequence_high = le32_to_cpu(d->sequence_high);
|
le32_to_cpus(&d->sequence_high);
|
||||||
d->sequence_low = le32_to_cpu(d->sequence_low);
|
le32_to_cpus(&d->sequence_low);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_log_data_le_export(VHDXLogDataSector *d)
|
void vhdx_log_data_le_export(VHDXLogDataSector *d)
|
||||||
{
|
{
|
||||||
assert(d != NULL);
|
assert(d != NULL);
|
||||||
|
|
||||||
d->data_signature = cpu_to_le32(d->data_signature);
|
cpu_to_le32s(&d->data_signature);
|
||||||
d->sequence_high = cpu_to_le32(d->sequence_high);
|
cpu_to_le32s(&d->sequence_high);
|
||||||
d->sequence_low = cpu_to_le32(d->sequence_low);
|
cpu_to_le32s(&d->sequence_low);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr)
|
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr)
|
||||||
{
|
{
|
||||||
assert(hdr != NULL);
|
assert(hdr != NULL);
|
||||||
|
|
||||||
hdr->signature = le32_to_cpu(hdr->signature);
|
le32_to_cpus(&hdr->signature);
|
||||||
hdr->checksum = le32_to_cpu(hdr->checksum);
|
le32_to_cpus(&hdr->checksum);
|
||||||
hdr->entry_length = le32_to_cpu(hdr->entry_length);
|
le32_to_cpus(&hdr->entry_length);
|
||||||
hdr->tail = le32_to_cpu(hdr->tail);
|
le32_to_cpus(&hdr->tail);
|
||||||
hdr->sequence_number = le64_to_cpu(hdr->sequence_number);
|
le64_to_cpus(&hdr->sequence_number);
|
||||||
hdr->descriptor_count = le32_to_cpu(hdr->descriptor_count);
|
le32_to_cpus(&hdr->descriptor_count);
|
||||||
leguid_to_cpus(&hdr->log_guid);
|
leguid_to_cpus(&hdr->log_guid);
|
||||||
hdr->flushed_file_offset = le64_to_cpu(hdr->flushed_file_offset);
|
le64_to_cpus(&hdr->flushed_file_offset);
|
||||||
hdr->last_file_offset = le64_to_cpu(hdr->last_file_offset);
|
le64_to_cpus(&hdr->last_file_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
|
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
|
||||||
{
|
{
|
||||||
assert(hdr != NULL);
|
assert(hdr != NULL);
|
||||||
|
|
||||||
hdr->signature = cpu_to_le32(hdr->signature);
|
cpu_to_le32s(&hdr->signature);
|
||||||
hdr->checksum = cpu_to_le32(hdr->checksum);
|
cpu_to_le32s(&hdr->checksum);
|
||||||
hdr->entry_length = cpu_to_le32(hdr->entry_length);
|
cpu_to_le32s(&hdr->entry_length);
|
||||||
hdr->tail = cpu_to_le32(hdr->tail);
|
cpu_to_le32s(&hdr->tail);
|
||||||
hdr->sequence_number = cpu_to_le64(hdr->sequence_number);
|
cpu_to_le64s(&hdr->sequence_number);
|
||||||
hdr->descriptor_count = cpu_to_le32(hdr->descriptor_count);
|
cpu_to_le32s(&hdr->descriptor_count);
|
||||||
cpu_to_leguids(&hdr->log_guid);
|
cpu_to_leguids(&hdr->log_guid);
|
||||||
hdr->flushed_file_offset = cpu_to_le64(hdr->flushed_file_offset);
|
cpu_to_le64s(&hdr->flushed_file_offset);
|
||||||
hdr->last_file_offset = cpu_to_le64(hdr->last_file_offset);
|
cpu_to_le64s(&hdr->last_file_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -150,18 +150,18 @@ void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr)
|
|||||||
{
|
{
|
||||||
assert(hdr != NULL);
|
assert(hdr != NULL);
|
||||||
|
|
||||||
hdr->signature = le32_to_cpu(hdr->signature);
|
le32_to_cpus(&hdr->signature);
|
||||||
hdr->checksum = le32_to_cpu(hdr->checksum);
|
le32_to_cpus(&hdr->checksum);
|
||||||
hdr->entry_count = le32_to_cpu(hdr->entry_count);
|
le32_to_cpus(&hdr->entry_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr)
|
void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr)
|
||||||
{
|
{
|
||||||
assert(hdr != NULL);
|
assert(hdr != NULL);
|
||||||
|
|
||||||
hdr->signature = cpu_to_le32(hdr->signature);
|
cpu_to_le32s(&hdr->signature);
|
||||||
hdr->checksum = cpu_to_le32(hdr->checksum);
|
cpu_to_le32s(&hdr->checksum);
|
||||||
hdr->entry_count = cpu_to_le32(hdr->entry_count);
|
cpu_to_le32s(&hdr->entry_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
|
void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
|
||||||
@@ -169,9 +169,9 @@ void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
|
|||||||
assert(e != NULL);
|
assert(e != NULL);
|
||||||
|
|
||||||
leguid_to_cpus(&e->guid);
|
leguid_to_cpus(&e->guid);
|
||||||
e->file_offset = le64_to_cpu(e->file_offset);
|
le64_to_cpus(&e->file_offset);
|
||||||
e->length = le32_to_cpu(e->length);
|
le32_to_cpus(&e->length);
|
||||||
e->data_bits = le32_to_cpu(e->data_bits);
|
le32_to_cpus(&e->data_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
|
void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
|
||||||
@@ -179,9 +179,9 @@ void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
|
|||||||
assert(e != NULL);
|
assert(e != NULL);
|
||||||
|
|
||||||
cpu_to_leguids(&e->guid);
|
cpu_to_leguids(&e->guid);
|
||||||
e->file_offset = cpu_to_le64(e->file_offset);
|
cpu_to_le64s(&e->file_offset);
|
||||||
e->length = cpu_to_le32(e->length);
|
cpu_to_le32s(&e->length);
|
||||||
e->data_bits = cpu_to_le32(e->data_bits);
|
cpu_to_le32s(&e->data_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -190,16 +190,16 @@ void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr)
|
|||||||
{
|
{
|
||||||
assert(hdr != NULL);
|
assert(hdr != NULL);
|
||||||
|
|
||||||
hdr->signature = le64_to_cpu(hdr->signature);
|
le64_to_cpus(&hdr->signature);
|
||||||
hdr->entry_count = le16_to_cpu(hdr->entry_count);
|
le16_to_cpus(&hdr->entry_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr)
|
void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr)
|
||||||
{
|
{
|
||||||
assert(hdr != NULL);
|
assert(hdr != NULL);
|
||||||
|
|
||||||
hdr->signature = cpu_to_le64(hdr->signature);
|
cpu_to_le64s(&hdr->signature);
|
||||||
hdr->entry_count = cpu_to_le16(hdr->entry_count);
|
cpu_to_le16s(&hdr->entry_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
|
void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
|
||||||
@@ -207,16 +207,16 @@ void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
|
|||||||
assert(e != NULL);
|
assert(e != NULL);
|
||||||
|
|
||||||
leguid_to_cpus(&e->item_id);
|
leguid_to_cpus(&e->item_id);
|
||||||
e->offset = le32_to_cpu(e->offset);
|
le32_to_cpus(&e->offset);
|
||||||
e->length = le32_to_cpu(e->length);
|
le32_to_cpus(&e->length);
|
||||||
e->data_bits = le32_to_cpu(e->data_bits);
|
le32_to_cpus(&e->data_bits);
|
||||||
}
|
}
|
||||||
void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e)
|
void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e)
|
||||||
{
|
{
|
||||||
assert(e != NULL);
|
assert(e != NULL);
|
||||||
|
|
||||||
cpu_to_leguids(&e->item_id);
|
cpu_to_leguids(&e->item_id);
|
||||||
e->offset = cpu_to_le32(e->offset);
|
cpu_to_le32s(&e->offset);
|
||||||
e->length = cpu_to_le32(e->length);
|
cpu_to_le32s(&e->length);
|
||||||
e->data_bits = cpu_to_le32(e->data_bits);
|
cpu_to_le32s(&e->data_bits);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user