Compare commits
59 Commits
machine-pu
...
v2.5.1.1
Author | SHA1 | Date | |
---|---|---|---|
|
db51dfc1fc | ||
|
5b7236f725 | ||
|
0bcdb632f8 | ||
|
706bab670b | ||
|
44b86aa32e | ||
|
a6e5e5dd4b | ||
|
2f2f74e87c | ||
|
46aff2c7e9 | ||
|
4f0323d26c | ||
|
a58047f7fb | ||
|
5f409b108f | ||
|
078de11898 | ||
|
acea76c162 | ||
|
80b6e5723f | ||
|
9bddb45dbc | ||
|
e3a2cdfcb5 | ||
|
4dcd2f13b1 | ||
|
38e09211b6 | ||
|
d0ee85b4e4 | ||
|
4f046a6ba1 | ||
|
b47809c6b3 | ||
|
24fe899c3c | ||
|
aaf4fb6afb | ||
|
a2ae168821 | ||
|
bad094d524 | ||
|
4b0b1ec8e0 | ||
|
cab1cc7245 | ||
|
9ae02175b4 | ||
|
30929793b0 | ||
|
c5c9841ce8 | ||
|
6b62303eb8 | ||
|
c06f342009 | ||
|
cb873eaa6d | ||
|
4853a5a80f | ||
|
a375e0b03e | ||
|
a38a283fc7 | ||
|
225d50fbb1 | ||
|
020282d3e6 | ||
|
091af18104 | ||
|
d98392379a | ||
|
643c8d8ec1 | ||
|
3ede27db32 | ||
|
9849b1912f | ||
|
fe90bdc25b | ||
|
aaa5271327 | ||
|
abda95cb01 | ||
|
6a49a71cc6 | ||
|
e1a8a09124 | ||
|
7a2c1c8e66 | ||
|
702a8d165c | ||
|
3e96d5dcf2 | ||
|
16a2875735 | ||
|
4588b0d856 | ||
|
ff083d3c3b | ||
|
4d59e78dfe | ||
|
52a7b27947 | ||
|
d4aed70099 | ||
|
42ae4a3c61 | ||
|
0d335804e3 |
125
.travis.yml
125
.travis.yml
@@ -1,38 +1,9 @@
|
||||
sudo: false
|
||||
language: c
|
||||
python:
|
||||
- "2.4"
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
cache: ccache
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgnutls-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng12-dev
|
||||
- librados-dev
|
||||
- libsdl1.2-dev
|
||||
- libseccomp-dev
|
||||
- libspice-protocol-dev
|
||||
- libspice-server-dev
|
||||
- libssh2-1-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvte-2.90-dev
|
||||
- sparse
|
||||
- uuid-dev
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
@@ -41,16 +12,29 @@ notifications:
|
||||
on_failure: always
|
||||
env:
|
||||
global:
|
||||
- TEST_CMD="make check"
|
||||
- TEST_CMD=""
|
||||
- EXTRA_CONFIG=""
|
||||
# Development packages, EXTRA_PKGS saved for additional builds
|
||||
- CORE_PKGS="libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev"
|
||||
- NET_PKGS="libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev libspice-protocol-dev libnss3-dev"
|
||||
- GUI_PKGS="libgtk-3-dev libvte-2.90-dev libsdl1.2-dev libpng12-dev libpixman-1-dev"
|
||||
- EXTRA_PKGS=""
|
||||
matrix:
|
||||
# Group major targets together with their linux-user counterparts
|
||||
- TARGETS=alpha-softmmu,alpha-linux-user,cris-softmmu,cris-linux-user,m68k-softmmu,m68k-linux-user,microblaze-softmmu,microblazeel-softmmu,microblaze-linux-user,microblazeel-linux-user
|
||||
- TARGETS=alpha-softmmu,alpha-linux-user
|
||||
- TARGETS=arm-softmmu,arm-linux-user,armeb-linux-user,aarch64-softmmu,aarch64-linux-user
|
||||
- TARGETS=cris-softmmu,cris-linux-user
|
||||
- TARGETS=i386-softmmu,i386-linux-user,x86_64-softmmu,x86_64-linux-user
|
||||
- TARGETS=mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,mipsn32-linux-user,mipsn32el-linux-user
|
||||
- TARGETS=or32-softmmu,or32-linux-user,ppc-softmmu,ppc64-softmmu,ppcemb-softmmu,ppc-linux-user,ppc64-linux-user,ppc64abi32-linux-user,ppc64le-linux-user
|
||||
- TARGETS=s390x-softmmu,s390x-linux-user,sh4-softmmu,sh4eb-softmmu,sh4-linux-user,sh4eb-linux-user,sparc-softmmu,sparc64-softmmu,sparc-linux-user,sparc32plus-linux-user,sparc64-linux-user,unicore32-softmmu,unicore32-linux-user
|
||||
- TARGETS=m68k-softmmu,m68k-linux-user
|
||||
- TARGETS=microblaze-softmmu,microblazeel-softmmu,microblaze-linux-user,microblazeel-linux-user
|
||||
- TARGETS=mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu
|
||||
- TARGETS=mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,mipsn32-linux-user,mipsn32el-linux-user
|
||||
- TARGETS=or32-softmmu,or32-linux-user
|
||||
- TARGETS=ppc-softmmu,ppc64-softmmu,ppcemb-softmmu,ppc-linux-user,ppc64-linux-user,ppc64abi32-linux-user,ppc64le-linux-user
|
||||
- TARGETS=s390x-softmmu,s390x-linux-user
|
||||
- TARGETS=sh4-softmmu,sh4eb-softmmu,sh4-linux-user sh4eb-linux-user
|
||||
- TARGETS=sparc-softmmu,sparc64-softmmu,sparc-linux-user,sparc32plus-linux-user,sparc64-linux-user
|
||||
- TARGETS=unicore32-softmmu,unicore32-linux-user
|
||||
# Group remaining softmmu only targets into one build
|
||||
- TARGETS=lm32-softmmu,moxie-softmmu,tricore-softmmu,xtensa-softmmu,xtensaeb-softmmu
|
||||
git:
|
||||
@@ -59,6 +43,8 @@ git:
|
||||
before_install:
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq ${CORE_PKGS} ${NET_PKGS} ${GUI_PKGS} ${EXTRA_PKGS}
|
||||
before_script:
|
||||
- ./configure --target-list=${TARGETS} --enable-debug-tcg ${EXTRA_CONFIG}
|
||||
script:
|
||||
@@ -66,59 +52,44 @@ script:
|
||||
matrix:
|
||||
# We manually include a number of additional build for non-standard bits
|
||||
include:
|
||||
# Make check target (we only do this once)
|
||||
- env:
|
||||
- TARGETS=alpha-softmmu,arm-softmmu,aarch64-softmmu,cris-softmmu,i386-softmmu,x86_64-softmmu,m68k-softmmu,microblaze-softmmu,microblazeel-softmmu,mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu,or32-softmmu,ppc-softmmu,ppc64-softmmu,ppcemb-softmmu,s390x-softmmu,sh4-softmmu,sh4eb-softmmu,sparc-softmmu,sparc64-softmmu,unicore32-softmmu,unicore32-linux-user,lm32-softmmu,moxie-softmmu,tricore-softmmu,xtensa-softmmu,xtensaeb-softmmu
|
||||
TEST_CMD="make check"
|
||||
compiler: gcc
|
||||
# Debug related options
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-debug"
|
||||
compiler: gcc
|
||||
# We currently disable "make check"
|
||||
- env: TARGETS=alpha-softmmu
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
# Disable a few of the optional features
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
|
||||
# All the extra -dev packages
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_PKGS="libaio-dev libcap-ng-dev libattr1-dev libbrlapi-dev uuid-dev libusb-1.0.0-dev"
|
||||
compiler: gcc
|
||||
# Currently configure doesn't force --disable-pie
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-gprof --enable-gcov --disable-pie"
|
||||
compiler: gcc
|
||||
# Sparse
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_PKGS="sparse"
|
||||
EXTRA_CONFIG="--enable-sparse"
|
||||
compiler: gcc
|
||||
# Modules
|
||||
- env: TARGETS=arm-softmmu,x86_64-softmmu
|
||||
# All the trace backends (apart from dtrace)
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=stderr"
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=simple"
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=ftrace"
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_PKGS="liblttng-ust-dev liburcu-dev"
|
||||
EXTRA_CONFIG="--enable-trace-backends=ust"
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-modules"
|
||||
compiler: gcc
|
||||
# All the trace backends (apart from dtrace)
|
||||
- env: TARGETS=i386-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=log"
|
||||
compiler: gcc
|
||||
# We currently disable "make check" (until 41fc57e44ed regression fixed)
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=simple"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
# We currently disable "make check"
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=ftrace"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
# We currently disable "make check"
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=ust"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
# All the co-routine backends (apart from windows)
|
||||
# We currently disable "make check"
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--with-coroutine=gthread"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--with-coroutine=ucontext"
|
||||
compiler: gcc
|
||||
- env: TARGETS=x86_64-softmmu
|
||||
EXTRA_CONFIG="--with-coroutine=sigaltstack"
|
||||
compiler: gcc
|
||||
|
55
HACKING
55
HACKING
@@ -157,58 +157,3 @@ painful. These are:
|
||||
* you may assume that integers are 2s complement representation
|
||||
* you may assume that right shift of a signed integer duplicates
|
||||
the sign bit (ie it is an arithmetic shift, not a logical shift)
|
||||
|
||||
7. Error handling and reporting
|
||||
|
||||
7.1 Reporting errors to the human user
|
||||
|
||||
Do not use printf(), fprintf() or monitor_printf(). Instead, use
|
||||
error_report() or error_vreport() from error-report.h. This ensures the
|
||||
error is reported in the right place (current monitor or stderr), and in
|
||||
a uniform format.
|
||||
|
||||
Use error_printf() & friends to print additional information.
|
||||
|
||||
error_report() prints the current location. In certain common cases
|
||||
like command line parsing, the current location is tracked
|
||||
automatically. To manipulate it manually, use the loc_*() from
|
||||
error-report.h.
|
||||
|
||||
7.2 Propagating errors
|
||||
|
||||
An error can't always be reported to the user right where it's detected,
|
||||
but often needs to be propagated up the call chain to a place that can
|
||||
handle it. This can be done in various ways.
|
||||
|
||||
The most flexible one is Error objects. See error.h for usage
|
||||
information.
|
||||
|
||||
Use the simplest suitable method to communicate success / failure to
|
||||
callers. Stick to common methods: non-negative on success / -1 on
|
||||
error, non-negative / -errno, non-null / null, or Error objects.
|
||||
|
||||
Example: when a function returns a non-null pointer on success, and it
|
||||
can fail only in one way (as far as the caller is concerned), returning
|
||||
null on failure is just fine, and certainly simpler and a lot easier on
|
||||
the eyes than propagating an Error object through an Error ** parameter.
|
||||
|
||||
Example: when a function's callers need to report details on failure
|
||||
only the function really knows, use Error **, and set suitable errors.
|
||||
|
||||
Do not report an error to the user when you're also returning an error
|
||||
for somebody else to handle. Leave the reporting to the place that
|
||||
consumes the error returned.
|
||||
|
||||
7.3 Handling errors
|
||||
|
||||
Calling exit() is fine when handling configuration errors during
|
||||
startup. It's problematic during normal operation. In particular,
|
||||
monitor commands should never exit().
|
||||
|
||||
Do not call exit() or abort() to handle an error that can be triggered
|
||||
by the guest (e.g., some unimplemented corner case in guest code
|
||||
translation or device emulation). Guests should not be able to
|
||||
terminate QEMU.
|
||||
|
||||
Note that &error_fatal is just another way to exit(1), and &error_abort
|
||||
is just another way to abort().
|
||||
|
95
MAINTAINERS
95
MAINTAINERS
@@ -52,11 +52,6 @@ General Project Administration
|
||||
------------------------------
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
||||
All patches CC here
|
||||
L: qemu-devel@nongnu.org
|
||||
F: *
|
||||
F: */
|
||||
|
||||
Responsible Disclosure, Reporting Security Issues
|
||||
------------------------------
|
||||
W: http://wiki.qemu.org/SecurityProcess
|
||||
@@ -84,13 +79,6 @@ F: include/exec/exec-all.h
|
||||
F: include/exec/helper*.h
|
||||
F: include/exec/tb-hash.h
|
||||
|
||||
FPU emulation
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Odd Fixes
|
||||
F: fpu/
|
||||
F: include/fpu/
|
||||
|
||||
Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
@@ -234,7 +222,6 @@ L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
F: kvm-*
|
||||
F: */kvm.*
|
||||
F: include/sysemu/kvm*.h
|
||||
|
||||
ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -286,12 +273,9 @@ F: */xen*
|
||||
F: hw/char/xen_console.c
|
||||
F: hw/display/xenfb.c
|
||||
F: hw/net/xen_nic.c
|
||||
F: hw/block/xen_*
|
||||
F: hw/xen/
|
||||
F: hw/xenpv/
|
||||
F: hw/i386/xen/
|
||||
F: include/hw/xen/
|
||||
F: include/sysemu/xen-mapcache.h
|
||||
|
||||
Hosts:
|
||||
------
|
||||
@@ -364,7 +348,6 @@ M: Dmitry Solodkiy <d.solodkiy@samsung.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/exynos*
|
||||
F: include/hw/arm/exynos4210.h
|
||||
|
||||
Calxeda Highbank
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
@@ -392,7 +375,6 @@ L: qemu-arm@nongnu.org
|
||||
S: Odd fixes
|
||||
F: hw/*/imx*
|
||||
F: hw/arm/kzm.c
|
||||
F: include/hw/arm/fsl-imx31.h
|
||||
|
||||
Integrator CP
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -435,7 +417,6 @@ F: hw/arm/spitz.c
|
||||
F: hw/arm/tosa.c
|
||||
F: hw/arm/z2.c
|
||||
F: hw/*/pxa2xx*
|
||||
F: include/hw/arm/pxa.h
|
||||
|
||||
Stellaris
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -606,7 +587,7 @@ F: hw/ppc/prep.c
|
||||
F: hw/pci-host/prep.[hc]
|
||||
F: hw/isa/pc87312.[hc]
|
||||
|
||||
sPAPR
|
||||
sPAPR (pseries)
|
||||
M: David Gibson <david@gibson.dropbear.id.au>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
@@ -657,6 +638,12 @@ F: hw/*/grlib*
|
||||
|
||||
S390 Machines
|
||||
-------------
|
||||
S390 Virtio
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: hw/s390x/s390-*.c
|
||||
X: hw/s390x/*pci*.[hc]
|
||||
|
||||
S390 Virtio-ccw
|
||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
@@ -664,6 +651,7 @@ M: Alexander Graf <agraf@suse.de>
|
||||
S: Supported
|
||||
F: hw/char/sclp*.[hc]
|
||||
F: hw/s390x/
|
||||
X: hw/s390x/s390-virtio-bus.[ch]
|
||||
F: include/hw/s390x/
|
||||
F: pc-bios/s390-ccw/
|
||||
F: hw/watchdog/wdt_diag288.c
|
||||
@@ -717,12 +705,6 @@ F: hw/timer/hpet*
|
||||
F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
|
||||
Machine core
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Supported
|
||||
F: hw/core/machine.c
|
||||
F: include/hw/boards.h
|
||||
|
||||
Xtensa Machines
|
||||
---------------
|
||||
@@ -771,7 +753,6 @@ OMAP
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: hw/*/omap*
|
||||
F: include/hw/arm/omap.h
|
||||
|
||||
IPack
|
||||
M: Alberto Garcia <berto@igalia.com>
|
||||
@@ -857,10 +838,6 @@ M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb/*
|
||||
F: tests/usb-*-test.c
|
||||
F: docs/usb2.txt
|
||||
F: docs/usb-storage.txt
|
||||
F: include/hw/usb.h
|
||||
F: include/hw/usb/
|
||||
|
||||
USB (serial adapter)
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
@@ -872,7 +849,6 @@ VFIO
|
||||
M: Alex Williamson <alex.williamson@redhat.com>
|
||||
S: Supported
|
||||
F: hw/vfio/*
|
||||
F: include/hw/vfio/
|
||||
|
||||
vhost
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
@@ -884,7 +860,6 @@ M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: hw/*/virtio*
|
||||
F: net/vhost-user.c
|
||||
F: include/hw/virtio/
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
@@ -930,7 +905,6 @@ M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Supported
|
||||
F: hw/virtio/virtio-rng.c
|
||||
F: include/hw/virtio/virtio-rng.h
|
||||
F: include/sysemu/rng*.h
|
||||
F: backends/rng*.c
|
||||
|
||||
nvme
|
||||
@@ -967,13 +941,6 @@ M: Jiri Pirko <jiri@resnulli.us>
|
||||
S: Maintained
|
||||
F: hw/net/rocker/
|
||||
|
||||
NVDIMM
|
||||
M: Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
S: Maintained
|
||||
F: hw/acpi/nvdimm.c
|
||||
F: hw/mem/nvdimm.c
|
||||
F: include/hw/mem/nvdimm.h
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
@@ -1062,8 +1029,7 @@ Device Tree
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: device_tree.c
|
||||
F: include/sysemu/device_tree.h
|
||||
F: device_tree.[ch]
|
||||
|
||||
Error reporting
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
@@ -1094,7 +1060,6 @@ SPICE
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
F: include/ui/qemu-spice.h
|
||||
F: include/ui/spice-display.h
|
||||
F: ui/spice-*.c
|
||||
F: audio/spiceaudio.c
|
||||
F: hw/display/qxl*
|
||||
@@ -1103,7 +1068,6 @@ Graphics
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: ui/
|
||||
F: include/ui/
|
||||
|
||||
Cocoa graphics
|
||||
M: Andreas Färber <andreas.faerber@web.de>
|
||||
@@ -1131,7 +1095,6 @@ Network device backends
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
F: include/net/
|
||||
T: git git://github.com/jasowang/qemu.git net
|
||||
|
||||
Netmap network backend
|
||||
@@ -1145,9 +1108,8 @@ F: net/netmap.c
|
||||
Network Block Device (NBD)
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: block/nbd*
|
||||
F: nbd/
|
||||
F: include/block/nbd*
|
||||
F: block/nbd.c
|
||||
F: nbd.*
|
||||
F: qemu-nbd.c
|
||||
T: git git://github.com/bonzini/qemu.git nbd-next
|
||||
|
||||
@@ -1227,12 +1189,9 @@ F: scripts/qmp/
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
SLIRP
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||
S: Maintained
|
||||
F: slirp/
|
||||
F: net/slirp.c
|
||||
F: include/net/slirp.h
|
||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Tracing
|
||||
@@ -1257,7 +1216,6 @@ F: include/migration/
|
||||
F: migration/
|
||||
F: scripts/vmstate-static-checker.py
|
||||
F: tests/vmstate-static-checker-data/
|
||||
F: docs/migration.txt
|
||||
|
||||
Seccomp
|
||||
M: Eduardo Otubo <eduardo.otubo@profitbricks.com>
|
||||
@@ -1285,30 +1243,6 @@ S: Odd fixes
|
||||
F: util/buffer.c
|
||||
F: include/qemu/buffer.h
|
||||
|
||||
I/O Channels
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Maintained
|
||||
F: io/
|
||||
F: include/io/
|
||||
F: tests/test-io-*
|
||||
|
||||
Sockets
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: include/qemu/sockets.h
|
||||
F: util/qemu-sockets.c
|
||||
|
||||
Throttling infrastructure
|
||||
M: Alberto Garcia <berto@igalia.com>
|
||||
S: Supported
|
||||
F: block/throttle-groups.c
|
||||
F: include/block/throttle-groups.h
|
||||
F: include/qemu/throttle.h
|
||||
F: util/throttle.c
|
||||
L: qemu-block@nongnu.org
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
@@ -1549,7 +1483,6 @@ M: Denis V. Lunev <den@openvz.org>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/parallels.c
|
||||
F: docs/specs/parallels.txt
|
||||
|
||||
qed
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
@@ -1604,12 +1537,6 @@ L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: tests/image-fuzzer/
|
||||
|
||||
Build and test automation
|
||||
-------------------------
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Supported
|
||||
F: .travis.yml
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
21
Makefile
21
Makefile
@@ -159,7 +159,6 @@ dummy := $(call unnest-vars,, \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
|
||||
@@ -179,7 +178,6 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||
|
||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||
|
||||
subdir-%:
|
||||
@@ -234,13 +232,13 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
@@ -271,9 +269,7 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
|
||||
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
||||
$(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
|
||||
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
|
||||
$(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
|
||||
$(SRC_PATH)/qapi/trace.json
|
||||
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
@@ -329,7 +325,7 @@ ifneq ($(EXESUF),)
|
||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||
endif
|
||||
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) libqemuutil.a libqemustub.a
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y)
|
||||
$(call LINK, $^)
|
||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
@@ -391,7 +387,7 @@ bepo cz
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
acpi-dsdt.aml \
|
||||
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
@@ -400,6 +396,7 @@ efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \
|
||||
qemu-icon.bmp qemu_logo_no_text.svg \
|
||||
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
||||
multiboot.bin linuxboot.bin kvmvapic.bin \
|
||||
s390-zipl.rom \
|
||||
s390-ccw.img \
|
||||
spapr-rtas.bin slof.bin \
|
||||
palcode-clipper \
|
||||
|
@@ -8,8 +8,7 @@ util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y = async.o thread-pool.o
|
||||
block-obj-y += nbd/
|
||||
block-obj-y += block.o blockjob.o
|
||||
block-obj-y += nbd.o block.o blockjob.o
|
||||
block-obj-y += main-loop.o iohandler.o qemu-timer.o
|
||||
block-obj-$(CONFIG_POSIX) += aio-posix.o
|
||||
block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||
@@ -29,11 +28,6 @@ crypto-aes-obj-y = crypto/
|
||||
|
||||
qom-obj-y = qom/
|
||||
|
||||
#######################################################################
|
||||
# io-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
io-obj-y = io/
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
# suppress *all* target specific code in case of system emulation, i.e. a
|
||||
@@ -89,6 +83,7 @@ endif
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
common-obj-y += qemu-log.o
|
||||
common-obj-y += tcg-runtime.o
|
||||
common-obj-y += hw/
|
||||
common-obj-y += qom/
|
||||
|
@@ -176,7 +176,6 @@ dummy := $(call unnest-vars,.., \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
target-obj-y := $(target-obj-y-save)
|
||||
@@ -186,7 +185,6 @@ all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
||||
$(QEMU_PROG_BUILD): config-devices.mak
|
||||
|
||||
|
1
accel.c
1
accel.c
@@ -23,7 +23,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qemu-common.h"
|
||||
|
@@ -13,7 +13,6 @@
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/queue.h"
|
||||
|
@@ -15,7 +15,6 @@
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/queue.h"
|
||||
|
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <stdint.h>
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "hw/pci/pci.h"
|
||||
@@ -258,7 +258,9 @@ void do_acpitable_option(const QemuOpts *opts)
|
||||
|
||||
acpi_table_add(opts, &err);
|
||||
if (err) {
|
||||
error_reportf_err(err, "Wrong acpi table provided: ");
|
||||
error_report("Wrong acpi table provided: %s",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
1
async.c
1
async.c
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/aio.h"
|
||||
#include "block/thread-pool.h"
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "audio.h"
|
||||
#include "monitor/monitor.h"
|
||||
@@ -1807,6 +1806,9 @@ static void audio_init (void)
|
||||
atexit (audio_atexit);
|
||||
|
||||
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
|
||||
if (!s->ts) {
|
||||
hw_error("Could not create audio timer\n");
|
||||
}
|
||||
|
||||
audio_process_options ("AUDIO", audio_options);
|
||||
|
||||
@@ -1857,8 +1859,12 @@ static void audio_init (void)
|
||||
|
||||
if (!done) {
|
||||
done = !audio_driver_init (s, &no_audio_driver);
|
||||
assert(done);
|
||||
dolog("warning: Using timer based audio emulation\n");
|
||||
if (!done) {
|
||||
hw_error("Could not initialize audio subsystem\n");
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Using timer based audio emulation\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.period.hertz <= 0) {
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#ifndef QEMU_AUDIO_H
|
||||
#define QEMU_AUDIO_H
|
||||
|
||||
#include "config-host.h"
|
||||
#include "qemu/queue.h"
|
||||
|
||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* public domain */
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define AUDIO_CAP "win-int"
|
||||
|
@@ -22,8 +22,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <string.h> /* strerror */
|
||||
#include <pthread.h> /* pthread_X */
|
||||
|
||||
#include "qemu-common.h"
|
||||
@@ -32,10 +32,6 @@
|
||||
#define AUDIO_CAP "coreaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_6
|
||||
#define MAC_OS_X_VERSION_10_6 1060
|
||||
#endif
|
||||
|
||||
static int isAtexit;
|
||||
|
||||
typedef struct {
|
||||
@@ -49,233 +45,11 @@ typedef struct coreaudioVoiceOut {
|
||||
AudioDeviceID outputDeviceID;
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
AudioDeviceIOProcID ioprocid;
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
} coreaudioVoiceOut;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
/* The APIs used here only become available from 10.6 */
|
||||
|
||||
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
|
||||
{
|
||||
UInt32 size = sizeof(*id);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
id);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
|
||||
AudioValueRange *framerange)
|
||||
{
|
||||
UInt32 size = sizeof(*framerange);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
framerange);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectSetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectSetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
|
||||
{
|
||||
UInt32 size = sizeof(*result);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyDeviceIsRunning,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
result);
|
||||
}
|
||||
#else
|
||||
/* Legacy versions of functions using deprecated APIs */
|
||||
|
||||
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
|
||||
{
|
||||
UInt32 size = sizeof(*id);
|
||||
|
||||
return AudioHardwareGetProperty(
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
&size,
|
||||
id);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
|
||||
AudioValueRange *framerange)
|
||||
{
|
||||
UInt32 size = sizeof(*framerange);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&size,
|
||||
framerange);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
|
||||
return AudioDeviceSetProperty(
|
||||
id,
|
||||
NULL,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
&size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
|
||||
return AudioDeviceSetProperty(
|
||||
id,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
|
||||
{
|
||||
UInt32 size = sizeof(*result);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyDeviceIsRunning,
|
||||
&size,
|
||||
result);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void coreaudio_logstatus (OSStatus status)
|
||||
{
|
||||
const char *str = "BUG";
|
||||
@@ -370,7 +144,10 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
|
||||
{
|
||||
OSStatus status;
|
||||
UInt32 result = 0;
|
||||
status = coreaudio_get_isrunning(outputDeviceID, &result);
|
||||
UInt32 propertySize = sizeof(outputDeviceID);
|
||||
status = AudioDeviceGetProperty(
|
||||
outputDeviceID, 0, 0,
|
||||
kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr(status,
|
||||
"Could not determine whether Device is playing\n");
|
||||
@@ -511,6 +288,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
UInt32 propertySize;
|
||||
int err;
|
||||
const char *typ = "playback";
|
||||
AudioValueRange frameRange;
|
||||
@@ -525,7 +303,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
status = coreaudio_get_voice(&core->outputDeviceID);
|
||||
/* open default output device */
|
||||
propertySize = sizeof(core->outputDeviceID);
|
||||
status = AudioHardwareGetProperty(
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
&propertySize,
|
||||
&core->outputDeviceID);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get default output Device\n");
|
||||
@@ -537,8 +320,14 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* get minimum and maximum buffer frame sizes */
|
||||
status = coreaudio_get_framesizerange(core->outputDeviceID,
|
||||
&frameRange);
|
||||
propertySize = sizeof(frameRange);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&propertySize,
|
||||
&frameRange);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame range\n");
|
||||
@@ -558,8 +347,15 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* set Buffer Frame Size */
|
||||
status = coreaudio_set_framesize(core->outputDeviceID,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
NULL,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not set device buffer frame size %" PRIu32 "\n",
|
||||
@@ -568,8 +364,14 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* get Buffer Frame Size */
|
||||
status = coreaudio_get_framesize(core->outputDeviceID,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame size\n");
|
||||
@@ -578,8 +380,14 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
status = coreaudio_get_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
&propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get Device Stream properties\n");
|
||||
@@ -589,8 +397,15 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
status = coreaudio_set_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
|
||||
as->freq);
|
||||
@@ -599,12 +414,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* set Callback */
|
||||
core->ioprocid = NULL;
|
||||
status = AudioDeviceCreateIOProcID(core->outputDeviceID,
|
||||
audioDeviceIOProc,
|
||||
hw,
|
||||
&core->ioprocid);
|
||||
if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
|
||||
status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
@@ -612,10 +423,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
/* start Playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not start playback\n");
|
||||
AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid);
|
||||
AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
@@ -633,15 +444,15 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
if (!isAtexit) {
|
||||
/* stop playback */
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not stop playback\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* remove callback */
|
||||
status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
|
||||
core->ioprocid);
|
||||
status = AudioDeviceRemoveIOProc(core->outputDeviceID,
|
||||
audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not remove IOProc\n");
|
||||
}
|
||||
@@ -664,7 +475,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
case VOICE_ENABLE:
|
||||
/* start playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not resume playback\n");
|
||||
}
|
||||
@@ -675,8 +486,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
/* stop playback */
|
||||
if (!isAtexit) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID,
|
||||
core->ioprocid);
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not pause playback\n");
|
||||
}
|
||||
|
@@ -26,7 +26,6 @@
|
||||
* SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
#include "qemu/timer.h"
|
||||
|
@@ -21,8 +21,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include "qemu-common.h"
|
||||
|
@@ -1,5 +1,4 @@
|
||||
/* public domain */
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
#include "qemu-common.h"
|
||||
|
@@ -17,7 +17,6 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/timer.h"
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "audio.h"
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qemu/timer.h"
|
||||
@@ -567,7 +566,6 @@ static CharDriverState *chr_baum_init(const char *id,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevCommon *common = backend->u.braille;
|
||||
BaumDriverState *baum;
|
||||
CharDriverState *chr;
|
||||
brlapi_handle_t *handle;
|
||||
@@ -578,12 +576,8 @@ static CharDriverState *chr_baum_init(const char *id,
|
||||
#endif
|
||||
int tty;
|
||||
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
}
|
||||
baum = g_malloc0(sizeof(BaumDriverState));
|
||||
baum->chr = chr;
|
||||
baum->chr = chr = qemu_chr_alloc();
|
||||
|
||||
chr->opaque = baum;
|
||||
chr->chr_write = baum_write;
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* 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/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* 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 "sysemu/hostmem.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* 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 "sysemu/hostmem.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/visitor.h"
|
||||
@@ -27,18 +26,18 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
|
||||
#endif
|
||||
|
||||
static void
|
||||
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
uint64_t value = backend->size;
|
||||
|
||||
visit_type_size(v, name, &value, errp);
|
||||
visit_type_size(v, &value, name, errp);
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
Error *local_err = NULL;
|
||||
@@ -49,7 +48,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
visit_type_size(v, name, &value, &local_err);
|
||||
visit_type_size(v, &value, name, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
@@ -64,8 +63,8 @@ out:
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
uint16List *host_nodes = NULL;
|
||||
@@ -92,18 +91,18 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||
node = &(*node)->next;
|
||||
} while (true);
|
||||
|
||||
visit_type_uint16List(v, name, &host_nodes, errp);
|
||||
visit_type_uint16List(v, &host_nodes, name, errp);
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
#ifdef CONFIG_NUMA
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
uint16List *l = NULL;
|
||||
|
||||
visit_type_uint16List(v, name, &l, errp);
|
||||
visit_type_uint16List(v, &l, name, errp);
|
||||
|
||||
while (l) {
|
||||
bitmap_set(backend->host_nodes, l->value, 1);
|
||||
|
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <stdlib.h>
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "ui/console.h"
|
||||
@@ -68,13 +68,9 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevCommon *common = backend->u.msmouse;
|
||||
CharDriverState *chr;
|
||||
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
}
|
||||
chr = qemu_chr_alloc();
|
||||
chr->chr_write = msmouse_chr_write;
|
||||
chr->chr_close = msmouse_chr_close;
|
||||
chr->explicit_be_open = true;
|
||||
|
@@ -10,7 +10,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/rng.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -25,12 +24,33 @@ typedef struct RngEgd
|
||||
|
||||
CharDriverState *chr;
|
||||
char *chr_name;
|
||||
|
||||
GSList *requests;
|
||||
} RngEgd;
|
||||
|
||||
static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
|
||||
typedef struct RngRequest
|
||||
{
|
||||
EntropyReceiveFunc *receive_entropy;
|
||||
uint8_t *data;
|
||||
void *opaque;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
} RngRequest;
|
||||
|
||||
static void rng_egd_request_entropy(RngBackend *b, size_t size,
|
||||
EntropyReceiveFunc *receive_entropy,
|
||||
void *opaque)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
size_t size = req->size;
|
||||
RngRequest *req;
|
||||
|
||||
req = g_malloc(sizeof(*req));
|
||||
|
||||
req->offset = 0;
|
||||
req->size = size;
|
||||
req->receive_entropy = receive_entropy;
|
||||
req->opaque = opaque;
|
||||
req->data = g_malloc(req->size);
|
||||
|
||||
while (size > 0) {
|
||||
uint8_t header[2];
|
||||
@@ -44,15 +64,24 @@ static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
|
||||
|
||||
size -= len;
|
||||
}
|
||||
|
||||
s->requests = g_slist_append(s->requests, req);
|
||||
}
|
||||
|
||||
static void rng_egd_free_request(RngRequest *req)
|
||||
{
|
||||
g_free(req->data);
|
||||
g_free(req);
|
||||
}
|
||||
|
||||
static int rng_egd_chr_can_read(void *opaque)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(opaque);
|
||||
RngRequest *req;
|
||||
GSList *i;
|
||||
int size = 0;
|
||||
|
||||
QSIMPLEQ_FOREACH(req, &s->parent.requests, next) {
|
||||
for (i = s->requests; i; i = i->next) {
|
||||
RngRequest *req = i->data;
|
||||
size += req->size - req->offset;
|
||||
}
|
||||
|
||||
@@ -64,8 +93,8 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
RngEgd *s = RNG_EGD(opaque);
|
||||
size_t buf_offset = 0;
|
||||
|
||||
while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||
while (size > 0 && s->requests) {
|
||||
RngRequest *req = s->requests->data;
|
||||
int len = MIN(size, req->size - req->offset);
|
||||
|
||||
memcpy(req->data + req->offset, buf + buf_offset, len);
|
||||
@@ -74,13 +103,38 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
size -= len;
|
||||
|
||||
if (req->offset == req->size) {
|
||||
s->requests = g_slist_remove_link(s->requests, s->requests);
|
||||
|
||||
req->receive_entropy(req->opaque, req->data, req->size);
|
||||
|
||||
rng_backend_finalize_request(&s->parent, req);
|
||||
rng_egd_free_request(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rng_egd_free_requests(RngEgd *s)
|
||||
{
|
||||
GSList *i;
|
||||
|
||||
for (i = s->requests; i; i = i->next) {
|
||||
rng_egd_free_request(i->data);
|
||||
}
|
||||
|
||||
g_slist_free(s->requests);
|
||||
s->requests = NULL;
|
||||
}
|
||||
|
||||
static void rng_egd_cancel_requests(RngBackend *b)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
|
||||
/* We simply delete the list of pending requests. If there is data in the
|
||||
* queue waiting to be read, this is okay, because there will always be
|
||||
* more data than we requested originally
|
||||
*/
|
||||
rng_egd_free_requests(s);
|
||||
}
|
||||
|
||||
static void rng_egd_opened(RngBackend *b, Error **errp)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
@@ -149,6 +203,8 @@ static void rng_egd_finalize(Object *obj)
|
||||
}
|
||||
|
||||
g_free(s->chr_name);
|
||||
|
||||
rng_egd_free_requests(s);
|
||||
}
|
||||
|
||||
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
||||
@@ -156,6 +212,7 @@ static void rng_egd_class_init(ObjectClass *klass, void *data)
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
rbc->request_entropy = rng_egd_request_entropy;
|
||||
rbc->cancel_requests = rng_egd_cancel_requests;
|
||||
rbc->opened = rng_egd_opened;
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/rng-random.h"
|
||||
#include "sysemu/rng.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -22,6 +21,10 @@ struct RndRandom
|
||||
|
||||
int fd;
|
||||
char *filename;
|
||||
|
||||
EntropyReceiveFunc *receive_func;
|
||||
void *opaque;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -34,35 +37,36 @@ struct RndRandom
|
||||
static void entropy_available(void *opaque)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(opaque);
|
||||
uint8_t buffer[s->size];
|
||||
ssize_t len;
|
||||
|
||||
while (!QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||
ssize_t len;
|
||||
|
||||
len = read(s->fd, req->data, req->size);
|
||||
if (len < 0 && errno == EAGAIN) {
|
||||
return;
|
||||
}
|
||||
g_assert(len != -1);
|
||||
|
||||
req->receive_entropy(req->opaque, req->data, len);
|
||||
|
||||
rng_backend_finalize_request(&s->parent, req);
|
||||
len = read(s->fd, buffer, s->size);
|
||||
if (len < 0 && errno == EAGAIN) {
|
||||
return;
|
||||
}
|
||||
g_assert(len != -1);
|
||||
|
||||
s->receive_func(s->opaque, buffer, len);
|
||||
s->receive_func = NULL;
|
||||
|
||||
/* We've drained all requests, the fd handler can be reset. */
|
||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void rng_random_request_entropy(RngBackend *b, RngRequest *req)
|
||||
static void rng_random_request_entropy(RngBackend *b, size_t size,
|
||||
EntropyReceiveFunc *receive_entropy,
|
||||
void *opaque)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(b);
|
||||
|
||||
if (QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
/* If there are no pending requests yet, we need to
|
||||
* install our fd handler. */
|
||||
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
||||
if (s->receive_func) {
|
||||
s->receive_func(s->opaque, NULL, 0);
|
||||
}
|
||||
|
||||
s->receive_func = receive_entropy;
|
||||
s->opaque = opaque;
|
||||
s->size = size;
|
||||
|
||||
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
||||
}
|
||||
|
||||
static void rng_random_opened(RngBackend *b, Error **errp)
|
||||
|
@@ -10,7 +10,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/rng.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
@@ -20,20 +19,18 @@ void rng_backend_request_entropy(RngBackend *s, size_t size,
|
||||
void *opaque)
|
||||
{
|
||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||
RngRequest *req;
|
||||
|
||||
if (k->request_entropy) {
|
||||
req = g_malloc(sizeof(*req));
|
||||
k->request_entropy(s, size, receive_entropy, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
req->offset = 0;
|
||||
req->size = size;
|
||||
req->receive_entropy = receive_entropy;
|
||||
req->opaque = opaque;
|
||||
req->data = g_malloc(req->size);
|
||||
void rng_backend_cancel_requests(RngBackend *s)
|
||||
{
|
||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||
|
||||
k->request_entropy(s, req);
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&s->requests, req, next);
|
||||
if (k->cancel_requests) {
|
||||
k->cancel_requests(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,48 +72,14 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
s->opened = true;
|
||||
}
|
||||
|
||||
static void rng_backend_free_request(RngRequest *req)
|
||||
{
|
||||
g_free(req->data);
|
||||
g_free(req);
|
||||
}
|
||||
|
||||
static void rng_backend_free_requests(RngBackend *s)
|
||||
{
|
||||
RngRequest *req, *next;
|
||||
|
||||
QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||
rng_backend_free_request(req);
|
||||
}
|
||||
|
||||
QSIMPLEQ_INIT(&s->requests);
|
||||
}
|
||||
|
||||
void rng_backend_finalize_request(RngBackend *s, RngRequest *req)
|
||||
{
|
||||
QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next);
|
||||
rng_backend_free_request(req);
|
||||
}
|
||||
|
||||
static void rng_backend_init(Object *obj)
|
||||
{
|
||||
RngBackend *s = RNG_BACKEND(obj);
|
||||
|
||||
QSIMPLEQ_INIT(&s->requests);
|
||||
|
||||
object_property_add_bool(obj, "opened",
|
||||
rng_backend_prop_get_opened,
|
||||
rng_backend_prop_set_opened,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void rng_backend_finalize(Object *obj)
|
||||
{
|
||||
RngBackend *s = RNG_BACKEND(obj);
|
||||
|
||||
rng_backend_free_requests(s);
|
||||
}
|
||||
|
||||
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
@@ -129,7 +92,6 @@ static const TypeInfo rng_backend_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(RngBackend),
|
||||
.instance_init = rng_backend_init,
|
||||
.instance_finalize = rng_backend_finalize,
|
||||
.class_size = sizeof(RngBackendClass),
|
||||
.class_init = rng_backend_class_init,
|
||||
.abstract = true,
|
||||
|
@@ -23,7 +23,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
|
@@ -12,7 +12,6 @@
|
||||
* Based on backends/rng.c by Anthony Liguori
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "sysemu/tpm.h"
|
||||
|
@@ -24,7 +24,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
@@ -20,7 +20,7 @@ block-obj-$(CONFIG_RBD) += rbd.o
|
||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||
block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o
|
||||
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
||||
block-obj-y += accounting.o dirty-bitmap.o
|
||||
block-obj-y += accounting.o
|
||||
block-obj-y += write-threshold.o
|
||||
|
||||
common-obj-y += stream.o
|
||||
|
@@ -23,7 +23,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/accounting.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/timer.h"
|
||||
|
@@ -50,7 +50,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -60,6 +59,7 @@
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <xseg/xseg.h>
|
||||
#include <xseg/protocol.h>
|
||||
|
||||
|
102
block/backup.c
102
block/backup.c
@@ -11,7 +11,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "block/block.h"
|
||||
@@ -20,9 +22,11 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/bitmap.h"
|
||||
|
||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
#define BACKUP_CLUSTER_BITS 16
|
||||
#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS)
|
||||
#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct CowRequest {
|
||||
@@ -43,17 +47,10 @@ typedef struct BackupBlockJob {
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t sectors_read;
|
||||
unsigned long *done_bitmap;
|
||||
int64_t cluster_size;
|
||||
HBitmap *bitmap;
|
||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
} BackupBlockJob;
|
||||
|
||||
/* Size of a cluster in sectors, instead of bytes. */
|
||||
static inline int64_t cluster_size_sectors(BackupBlockJob *job)
|
||||
{
|
||||
return job->cluster_size / BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* See if in-flight requests overlap and wait for them to complete */
|
||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
@@ -102,14 +99,13 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
QEMUIOVector bounce_qiov;
|
||||
void *bounce_buffer = NULL;
|
||||
int ret = 0;
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||
int64_t start, end;
|
||||
int n;
|
||||
|
||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||
|
||||
start = sector_num / sectors_per_cluster;
|
||||
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||
start = sector_num / BACKUP_SECTORS_PER_CLUSTER;
|
||||
end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER);
|
||||
|
||||
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
||||
|
||||
@@ -117,19 +113,19 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
cow_request_begin(&cow_request, job, start, end);
|
||||
|
||||
for (; start < end; start++) {
|
||||
if (test_bit(start, job->done_bitmap)) {
|
||||
if (hbitmap_get(job->bitmap, start)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
continue; /* already copied */
|
||||
}
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
n = MIN(sectors_per_cluster,
|
||||
n = MIN(BACKUP_SECTORS_PER_CLUSTER,
|
||||
job->common.len / BDRV_SECTOR_SIZE -
|
||||
start * sectors_per_cluster);
|
||||
start * BACKUP_SECTORS_PER_CLUSTER);
|
||||
|
||||
if (!bounce_buffer) {
|
||||
bounce_buffer = qemu_blockalign(bs, job->cluster_size);
|
||||
bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE);
|
||||
}
|
||||
iov.iov_base = bounce_buffer;
|
||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||
@@ -137,10 +133,10 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
|
||||
if (is_write_notifier) {
|
||||
ret = bdrv_co_readv_no_serialising(bs,
|
||||
start * sectors_per_cluster,
|
||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
||||
n, &bounce_qiov);
|
||||
} else {
|
||||
ret = bdrv_co_readv(bs, start * sectors_per_cluster, n,
|
||||
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
|
||||
&bounce_qiov);
|
||||
}
|
||||
if (ret < 0) {
|
||||
@@ -153,11 +149,11 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
|
||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||
ret = bdrv_co_write_zeroes(job->target,
|
||||
start * sectors_per_cluster,
|
||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
||||
n, BDRV_REQ_MAY_UNMAP);
|
||||
} else {
|
||||
ret = bdrv_co_writev(job->target,
|
||||
start * sectors_per_cluster, n,
|
||||
start * BACKUP_SECTORS_PER_CLUSTER, n,
|
||||
&bounce_qiov);
|
||||
}
|
||||
if (ret < 0) {
|
||||
@@ -168,7 +164,7 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_bit(start, job->done_bitmap);
|
||||
hbitmap_set(job->bitmap, start, 1);
|
||||
|
||||
/* 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.
|
||||
@@ -328,22 +324,21 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
int64_t cluster;
|
||||
int64_t end;
|
||||
int64_t last_cluster = -1;
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||
BlockDriverState *bs = job->common.bs;
|
||||
HBitmapIter hbi;
|
||||
|
||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
|
||||
clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1);
|
||||
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
||||
|
||||
/* Find the next dirty sector(s) */
|
||||
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
||||
cluster = sector / sectors_per_cluster;
|
||||
cluster = sector / BACKUP_SECTORS_PER_CLUSTER;
|
||||
|
||||
/* Fake progress updates for any clusters we skipped */
|
||||
if (cluster != last_cluster + 1) {
|
||||
job->common.offset += ((cluster - last_cluster - 1) *
|
||||
job->cluster_size);
|
||||
BACKUP_CLUSTER_SIZE);
|
||||
}
|
||||
|
||||
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
||||
@@ -351,8 +346,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
if (yield_and_check(job)) {
|
||||
return ret;
|
||||
}
|
||||
ret = backup_do_cow(bs, cluster * sectors_per_cluster,
|
||||
sectors_per_cluster, &error_is_read,
|
||||
ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
|
||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read,
|
||||
false);
|
||||
if ((ret < 0) &&
|
||||
backup_error_action(job, error_is_read, -ret) ==
|
||||
@@ -364,17 +359,17 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
|
||||
/* If the bitmap granularity is smaller than the backup granularity,
|
||||
* we need to advance the iterator pointer to the next cluster. */
|
||||
if (granularity < job->cluster_size) {
|
||||
bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster);
|
||||
if (granularity < BACKUP_CLUSTER_SIZE) {
|
||||
bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER);
|
||||
}
|
||||
|
||||
last_cluster = cluster - 1;
|
||||
}
|
||||
|
||||
/* Play some final catchup with the progress meter */
|
||||
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
||||
if (last_cluster + 1 < end) {
|
||||
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
|
||||
job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -391,16 +386,15 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
.notify = backup_before_write_notify,
|
||||
};
|
||||
int64_t start, end;
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||
int ret = 0;
|
||||
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
start = 0;
|
||||
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
||||
|
||||
job->done_bitmap = bitmap_new(end);
|
||||
job->bitmap = hbitmap_alloc(end, 0);
|
||||
|
||||
bdrv_set_enable_write_cache(target, true);
|
||||
if (target->blk) {
|
||||
@@ -435,7 +429,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* Check to see if these blocks are already in the
|
||||
* backing file. */
|
||||
|
||||
for (i = 0; i < sectors_per_cluster;) {
|
||||
for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) {
|
||||
/* bdrv_is_allocated() only returns true/false based
|
||||
* on the first set of sectors it comes across that
|
||||
* are are all in the same state.
|
||||
@@ -444,8 +438,8 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
* needed but at some point that is always the case. */
|
||||
alloced =
|
||||
bdrv_is_allocated(bs,
|
||||
start * sectors_per_cluster + i,
|
||||
sectors_per_cluster - i, &n);
|
||||
start * BACKUP_SECTORS_PER_CLUSTER + i,
|
||||
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
||||
i += n;
|
||||
|
||||
if (alloced == 1 || n == 0) {
|
||||
@@ -460,8 +454,8 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
}
|
||||
}
|
||||
/* FULL sync mode we copy the whole drive. */
|
||||
ret = backup_do_cow(bs, start * sectors_per_cluster,
|
||||
sectors_per_cluster, &error_is_read, false);
|
||||
ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
|
||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read, false);
|
||||
if (ret < 0) {
|
||||
/* Depending on error action, fail now or retry cluster */
|
||||
BlockErrorAction action =
|
||||
@@ -481,7 +475,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
g_free(job->done_bitmap);
|
||||
hbitmap_free(job->bitmap);
|
||||
|
||||
if (target->blk) {
|
||||
blk_iostatus_disable(target->blk);
|
||||
@@ -502,8 +496,6 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
BlockJobTxn *txn, Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
BlockDriverInfo bdi;
|
||||
int ret;
|
||||
|
||||
assert(bs);
|
||||
assert(target);
|
||||
@@ -573,32 +565,14 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
goto error;
|
||||
}
|
||||
|
||||
bdrv_op_block_all(target, job->common.blocker);
|
||||
|
||||
job->on_source_error = on_source_error;
|
||||
job->on_target_error = on_target_error;
|
||||
job->target = target;
|
||||
job->sync_mode = sync_mode;
|
||||
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
||||
sync_bitmap : NULL;
|
||||
|
||||
/* 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
|
||||
* targets with a backing file, try to avoid COW if possible. */
|
||||
ret = bdrv_get_info(job->target, &bdi);
|
||||
if (ret < 0 && !target->backing) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Couldn't determine the cluster size of the target image, "
|
||||
"which has no backing file");
|
||||
error_append_hint(errp,
|
||||
"Aborting, since this may create an unusable destination image\n");
|
||||
goto error;
|
||||
} else if (ret < 0 && target->backing) {
|
||||
/* Not fatal; just trudge on ahead. */
|
||||
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
} else {
|
||||
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||
}
|
||||
|
||||
bdrv_op_block_all(target, job->common.blocker);
|
||||
job->common.len = len;
|
||||
job->common.co = qemu_coroutine_create(backup_run);
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
|
104
block/blkdebug.c
104
block/blkdebug.c
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "block/block_int.h"
|
||||
@@ -37,7 +36,7 @@ typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
|
||||
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
|
||||
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
|
||||
} BDRVBlkdebugState;
|
||||
@@ -65,7 +64,7 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct BlkdebugRule {
|
||||
BlkdebugEvent event;
|
||||
BlkDebugEvent event;
|
||||
int action;
|
||||
int state;
|
||||
union {
|
||||
@@ -144,12 +143,69 @@ static QemuOptsList *config_groups[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static int get_event_by_name(const char *name, BlkdebugEvent *event)
|
||||
static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||
[BLKDBG_L1_UPDATE] = "l1_update",
|
||||
[BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table",
|
||||
[BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table",
|
||||
[BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table",
|
||||
|
||||
[BLKDBG_L2_LOAD] = "l2_load",
|
||||
[BLKDBG_L2_UPDATE] = "l2_update",
|
||||
[BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed",
|
||||
[BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read",
|
||||
[BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write",
|
||||
|
||||
[BLKDBG_READ_AIO] = "read_aio",
|
||||
[BLKDBG_READ_BACKING_AIO] = "read_backing_aio",
|
||||
[BLKDBG_READ_COMPRESSED] = "read_compressed",
|
||||
|
||||
[BLKDBG_WRITE_AIO] = "write_aio",
|
||||
[BLKDBG_WRITE_COMPRESSED] = "write_compressed",
|
||||
|
||||
[BLKDBG_VMSTATE_LOAD] = "vmstate_load",
|
||||
[BLKDBG_VMSTATE_SAVE] = "vmstate_save",
|
||||
|
||||
[BLKDBG_COW_READ] = "cow_read",
|
||||
[BLKDBG_COW_WRITE] = "cow_write",
|
||||
|
||||
[BLKDBG_REFTABLE_LOAD] = "reftable_load",
|
||||
[BLKDBG_REFTABLE_GROW] = "reftable_grow",
|
||||
[BLKDBG_REFTABLE_UPDATE] = "reftable_update",
|
||||
|
||||
[BLKDBG_REFBLOCK_LOAD] = "refblock_load",
|
||||
[BLKDBG_REFBLOCK_UPDATE] = "refblock_update",
|
||||
[BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part",
|
||||
[BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc",
|
||||
[BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup",
|
||||
[BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write",
|
||||
[BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks",
|
||||
[BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table",
|
||||
[BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table",
|
||||
|
||||
[BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
|
||||
[BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
|
||||
[BLKDBG_CLUSTER_FREE] = "cluster_free",
|
||||
|
||||
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
|
||||
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
|
||||
|
||||
[BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head",
|
||||
[BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head",
|
||||
[BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail",
|
||||
[BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail",
|
||||
[BLKDBG_PWRITEV] = "pwritev",
|
||||
[BLKDBG_PWRITEV_ZERO] = "pwritev_zero",
|
||||
[BLKDBG_PWRITEV_DONE] = "pwritev_done",
|
||||
|
||||
[BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare",
|
||||
};
|
||||
|
||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
if (!strcmp(BlkdebugEvent_lookup[i], name)) {
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
if (!strcmp(event_names[i], name)) {
|
||||
*event = i;
|
||||
return 0;
|
||||
}
|
||||
@@ -168,7 +224,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
struct add_rule_data *d = opaque;
|
||||
BDRVBlkdebugState *s = d->s;
|
||||
const char* event_name;
|
||||
BlkdebugEvent event;
|
||||
BlkDebugEvent event;
|
||||
struct BlkdebugRule *rule;
|
||||
|
||||
/* Find the right event for the rule */
|
||||
@@ -508,7 +564,7 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
BlkdebugRule *rule, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
remove_rule(rule);
|
||||
}
|
||||
@@ -571,13 +627,13 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
return injected;
|
||||
}
|
||||
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule, *next;
|
||||
bool injected;
|
||||
|
||||
assert((int)event >= 0 && event < BLKDBG__MAX);
|
||||
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
|
||||
|
||||
injected = false;
|
||||
s->new_state = s->state;
|
||||
@@ -592,7 +648,7 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
BlkdebugEvent blkdebug_event;
|
||||
BlkDebugEvent blkdebug_event;
|
||||
|
||||
if (get_event_by_name(event, &blkdebug_event) < 0) {
|
||||
return -ENOENT;
|
||||
@@ -634,7 +690,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
|
||||
BlkdebugRule *rule, *next;
|
||||
int i, ret = -ENOENT;
|
||||
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
if (rule->action == ACTION_SUSPEND &&
|
||||
!strcmp(rule->options.suspend.tag, tag)) {
|
||||
@@ -675,15 +731,17 @@ static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
|
||||
return bdrv_truncate(bs->file->bs, offset);
|
||||
}
|
||||
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
QDict *opts;
|
||||
const QDictEntry *e;
|
||||
bool force_json = false;
|
||||
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "config") &&
|
||||
strcmp(qdict_entry_key(e), "x-image"))
|
||||
strcmp(qdict_entry_key(e), "x-image") &&
|
||||
strcmp(qdict_entry_key(e), "image") &&
|
||||
strncmp(qdict_entry_key(e), "image.", strlen("image.")))
|
||||
{
|
||||
force_json = true;
|
||||
break;
|
||||
@@ -699,7 +757,7 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
if (!force_json && bs->file->bs->exact_filename[0]) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkdebug:%s:%s",
|
||||
qdict_get_try_str(options, "config") ?: "",
|
||||
qdict_get_try_str(bs->options, "config") ?: "",
|
||||
bs->file->bs->exact_filename);
|
||||
}
|
||||
|
||||
@@ -709,8 +767,11 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
|
||||
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||
for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image") &&
|
||||
strcmp(qdict_entry_key(e), "image") &&
|
||||
strncmp(qdict_entry_key(e), "image.", strlen("image.")))
|
||||
{
|
||||
qobject_incref(qdict_entry_value(e));
|
||||
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
|
||||
}
|
||||
@@ -719,12 +780,6 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkdebug = {
|
||||
.format_name = "blkdebug",
|
||||
.protocol_name = "blkdebug",
|
||||
@@ -733,7 +788,6 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_parse_filename = blkdebug_parse_filename,
|
||||
.bdrv_file_open = blkdebug_open,
|
||||
.bdrv_close = blkdebug_close,
|
||||
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
|
||||
.bdrv_getlength = blkdebug_getlength,
|
||||
.bdrv_truncate = blkdebug_truncate,
|
||||
.bdrv_refresh_filename = blkdebug_refresh_filename,
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <stdarg.h>
|
||||
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
@@ -307,7 +307,7 @@ static void blkverify_attach_aio_context(BlockDriverState *bs,
|
||||
bdrv_attach_aio_context(s->test_file->bs, new_context);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
|
@@ -10,7 +10,6 @@
|
||||
* or later. See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
@@ -49,10 +48,6 @@ struct BlockBackend {
|
||||
BlockdevOnError on_read_error, on_write_error;
|
||||
bool iostatus_enabled;
|
||||
BlockDeviceIoStatus iostatus;
|
||||
|
||||
bool allow_write_beyond_eof;
|
||||
|
||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||
};
|
||||
|
||||
typedef struct BlockBackendAIOCB {
|
||||
@@ -103,8 +98,6 @@ BlockBackend *blk_new(const char *name, Error **errp)
|
||||
blk = g_new0(BlockBackend, 1);
|
||||
blk->name = g_strdup(name);
|
||||
blk->refcnt = 1;
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
notifier_list_init(&blk->insert_bs_notifiers);
|
||||
QTAILQ_INSERT_TAIL(&blk_backends, blk, link);
|
||||
return blk;
|
||||
}
|
||||
@@ -168,10 +161,11 @@ static void blk_delete(BlockBackend *blk)
|
||||
assert(!blk->refcnt);
|
||||
assert(!blk->dev);
|
||||
if (blk->bs) {
|
||||
blk_remove_bs(blk);
|
||||
assert(blk->bs->blk == blk);
|
||||
blk->bs->blk = NULL;
|
||||
bdrv_unref(blk->bs);
|
||||
blk->bs = NULL;
|
||||
}
|
||||
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
||||
if (blk->root_state.throttle_state) {
|
||||
g_free(blk->root_state.throttle_group);
|
||||
throttle_group_unref(blk->root_state.throttle_state);
|
||||
@@ -225,21 +219,6 @@ void blk_unref(BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
void blk_remove_all_bs(void)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
QTAILQ_FOREACH(blk, &blk_backends, link) {
|
||||
AioContext *ctx = blk_get_aio_context(blk);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (blk->bs) {
|
||||
blk_remove_bs(blk);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the BlockBackend after @blk.
|
||||
* If @blk is null, return the first one.
|
||||
@@ -365,10 +344,6 @@ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk)
|
||||
*/
|
||||
void blk_remove_bs(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->bs->blk == blk);
|
||||
|
||||
notifier_list_notify(&blk->remove_bs_notifiers, blk);
|
||||
|
||||
blk_update_root_state(blk);
|
||||
|
||||
blk->bs->blk = NULL;
|
||||
@@ -385,8 +360,6 @@ void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
|
||||
bdrv_ref(bs);
|
||||
blk->bs = bs;
|
||||
bs->blk = blk;
|
||||
|
||||
notifier_list_notify(&blk->insert_bs_notifiers, blk);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -581,11 +554,6 @@ void blk_iostatus_set_err(BlockBackend *blk, int error)
|
||||
}
|
||||
}
|
||||
|
||||
void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow)
|
||||
{
|
||||
blk->allow_write_beyond_eof = allow;
|
||||
}
|
||||
|
||||
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
size_t size)
|
||||
{
|
||||
@@ -599,19 +567,17 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
len = blk_getlength(blk);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!blk->allow_write_beyond_eof) {
|
||||
len = blk_getlength(blk);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
if (offset > len || len - offset < size) {
|
||||
return -EIO;
|
||||
}
|
||||
if (offset > len || len - offset < size) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1065,21 +1031,11 @@ int blk_get_max_transfer_length(BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
int blk_get_max_iov(BlockBackend *blk)
|
||||
{
|
||||
return blk->bs->bl.max_iov;
|
||||
}
|
||||
|
||||
void blk_set_guest_block_size(BlockBackend *blk, int align)
|
||||
{
|
||||
blk->guest_block_size = align;
|
||||
}
|
||||
|
||||
void *blk_try_blockalign(BlockBackend *blk, size_t size)
|
||||
{
|
||||
return qemu_try_blockalign(blk ? blk->bs : NULL, size);
|
||||
}
|
||||
|
||||
void *blk_blockalign(BlockBackend *blk, size_t size)
|
||||
{
|
||||
return qemu_blockalign(blk ? blk->bs : NULL, size);
|
||||
@@ -1159,14 +1115,11 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
|
||||
}
|
||||
}
|
||||
|
||||
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
|
||||
void blk_add_close_notifier(BlockBackend *blk, Notifier *notify)
|
||||
{
|
||||
notifier_list_add(&blk->remove_bs_notifiers, notify);
|
||||
}
|
||||
|
||||
void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify)
|
||||
{
|
||||
notifier_list_add(&blk->insert_bs_notifiers, notify);
|
||||
if (blk->bs) {
|
||||
bdrv_add_close_notifier(blk->bs, notify);
|
||||
}
|
||||
}
|
||||
|
||||
void blk_io_plug(BlockBackend *blk)
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
|
@@ -12,7 +12,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
|
67
block/curl.c
67
block/curl.c
@@ -21,13 +21,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "crypto/secret.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
// #define DEBUG_CURL
|
||||
@@ -79,10 +77,6 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
||||
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
||||
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
||||
#define CURL_BLOCK_OPT_USERNAME "username"
|
||||
#define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
|
||||
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
||||
#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
|
||||
|
||||
struct BDRVCURLState;
|
||||
|
||||
@@ -125,10 +119,6 @@ typedef struct BDRVCURLState {
|
||||
char *cookie;
|
||||
bool accept_range;
|
||||
AioContext *aio_context;
|
||||
char *username;
|
||||
char *password;
|
||||
char *proxyusername;
|
||||
char *proxypassword;
|
||||
} BDRVCURLState;
|
||||
|
||||
static void curl_clean_state(CURLState *s);
|
||||
@@ -428,21 +418,6 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
||||
|
||||
if (s->username) {
|
||||
curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username);
|
||||
}
|
||||
if (s->password) {
|
||||
curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password);
|
||||
}
|
||||
if (s->proxyusername) {
|
||||
curl_easy_setopt(state->curl,
|
||||
CURLOPT_PROXYUSERNAME, s->proxyusername);
|
||||
}
|
||||
if (s->proxypassword) {
|
||||
curl_easy_setopt(state->curl,
|
||||
CURLOPT_PROXYPASSWORD, s->proxypassword);
|
||||
}
|
||||
|
||||
/* Restrict supported protocols to avoid security issues in the more
|
||||
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
||||
* CVE-2013-0249.
|
||||
@@ -549,31 +524,10 @@ static QemuOptsList runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Pass the cookie or list of cookies with each request"
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_USERNAME,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Username for HTTP auth"
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_PASSWORD_SECRET,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of secret used as password for HTTP auth",
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_PROXY_USERNAME,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Username for HTTP proxy auth"
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of secret used as password for HTTP proxy auth",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -584,7 +538,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
const char *file;
|
||||
const char *cookie;
|
||||
double d;
|
||||
const char *secretid;
|
||||
|
||||
static int inited = 0;
|
||||
|
||||
@@ -626,26 +579,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
|
||||
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET);
|
||||
|
||||
if (secretid) {
|
||||
s->password = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||
if (!s->password) {
|
||||
goto out_noclean;
|
||||
}
|
||||
}
|
||||
|
||||
s->proxyusername = g_strdup(
|
||||
qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME));
|
||||
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET);
|
||||
if (secretid) {
|
||||
s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||
if (!s->proxypassword) {
|
||||
goto out_noclean;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inited) {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
inited = 1;
|
||||
|
@@ -1,387 +0,0 @@
|
||||
/*
|
||||
* Block Dirty Bitmap
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat. Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "config-host.h"
|
||||
#include "qemu-common.h"
|
||||
#include "trace.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
|
||||
/**
|
||||
* A BdrvDirtyBitmap can be in three possible states:
|
||||
* (1) successor is NULL and disabled is false: full r/w mode
|
||||
* (2) successor is NULL and disabled is true: read only mode ("disabled")
|
||||
* (3) successor is set: frozen mode.
|
||||
* A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
|
||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
||||
*/
|
||||
struct BdrvDirtyBitmap {
|
||||
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||
char *name; /* Optional non-empty unique ID */
|
||||
int64_t size; /* Size of the bitmap (Number of sectors) */
|
||||
bool disabled; /* Bitmap is read-only */
|
||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||
};
|
||||
|
||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
|
||||
assert(name);
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
if (bm->name && !strcmp(name, bm->name)) {
|
||||
return bm;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
g_free(bitmap->name);
|
||||
bitmap->name = NULL;
|
||||
}
|
||||
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t bitmap_size;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint32_t sector_granularity;
|
||||
|
||||
assert((granularity & (granularity - 1)) == 0);
|
||||
|
||||
if (name && bdrv_find_dirty_bitmap(bs, name)) {
|
||||
error_setg(errp, "Bitmap already exists: %s", name);
|
||||
return NULL;
|
||||
}
|
||||
sector_granularity = granularity >> BDRV_SECTOR_BITS;
|
||||
assert(sector_granularity);
|
||||
bitmap_size = bdrv_nb_sectors(bs);
|
||||
if (bitmap_size < 0) {
|
||||
error_setg_errno(errp, -bitmap_size, "could not get length of device");
|
||||
errno = -bitmap_size;
|
||||
return NULL;
|
||||
}
|
||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
||||
bitmap->size = bitmap_size;
|
||||
bitmap->name = g_strdup(name);
|
||||
bitmap->disabled = false;
|
||||
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !(bitmap->disabled || bitmap->successor);
|
||||
}
|
||||
|
||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||
} else {
|
||||
return DIRTY_BITMAP_STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||
* Requires that the bitmap is not frozen and has no successor.
|
||||
*/
|
||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
||||
{
|
||||
uint64_t granularity;
|
||||
BdrvDirtyBitmap *child;
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
||||
"currently frozen");
|
||||
return -1;
|
||||
}
|
||||
assert(!bitmap->successor);
|
||||
|
||||
/* Create an anonymous successor */
|
||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||
child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
||||
if (!child) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Successor will be on or off based on our current state. */
|
||||
child->disabled = bitmap->disabled;
|
||||
|
||||
/* Install the successor and freeze the parent */
|
||||
bitmap->successor = child;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a bitmap with a successor, yield our name to the successor,
|
||||
* delete the old bitmap, and return a handle to the new bitmap.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
Error **errp)
|
||||
{
|
||||
char *name;
|
||||
BdrvDirtyBitmap *successor = bitmap->successor;
|
||||
|
||||
if (successor == NULL) {
|
||||
error_setg(errp, "Cannot relinquish control if "
|
||||
"there's no successor present");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = bitmap->name;
|
||||
bitmap->name = NULL;
|
||||
successor->name = name;
|
||||
bitmap->successor = NULL;
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
|
||||
return successor;
|
||||
}
|
||||
|
||||
/**
|
||||
* In cases of failure where we can no longer safely delete the parent,
|
||||
* we may wish to re-join the parent and child/successor.
|
||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *successor = parent->successor;
|
||||
|
||||
if (!successor) {
|
||||
error_setg(errp, "Cannot reclaim a successor when none is present");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
bdrv_release_dirty_bitmap(bs, successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates _all_ bitmaps attached to a BDS.
|
||||
*/
|
||||
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t size = bdrv_nb_sectors(bs);
|
||||
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
hbitmap_truncate(bitmap->bitmap, size);
|
||||
bitmap->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
bool only_named)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
QLIST_REMOVE(bm, list);
|
||||
hbitmap_free(bm->bitmap);
|
||||
g_free(bm->name);
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
*/
|
||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
||||
}
|
||||
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = true;
|
||||
}
|
||||
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
}
|
||||
|
||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDirtyInfoList *list = NULL;
|
||||
BlockDirtyInfoList **plist = &list;
|
||||
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
||||
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
||||
info->count = bdrv_get_dirty_count(bm);
|
||||
info->granularity = bdrv_dirty_bitmap_granularity(bm);
|
||||
info->has_name = !!bm->name;
|
||||
info->name = g_strdup(bm->name);
|
||||
info->status = bdrv_dirty_bitmap_status(bm);
|
||||
entry->value = info;
|
||||
*plist = entry;
|
||||
plist = &entry->next;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
int64_t sector)
|
||||
{
|
||||
if (bitmap) {
|
||||
return hbitmap_get(bitmap->bitmap, sector);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a default granularity based on the existing cluster size,
|
||||
* but clamped between [4K, 64K]. Defaults to 64K in the case that there
|
||||
* is no cluster size information available.
|
||||
*/
|
||||
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
uint32_t granularity;
|
||||
|
||||
if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) {
|
||||
granularity = MAX(4096, bdi.cluster_size);
|
||||
granularity = MIN(65536, granularity);
|
||||
} else {
|
||||
granularity = 65536;
|
||||
}
|
||||
|
||||
return granularity;
|
||||
}
|
||||
|
||||
uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap);
|
||||
}
|
||||
|
||||
void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
|
||||
{
|
||||
hbitmap_iter_init(hbi, bitmap->bitmap, 0);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
if (!out) {
|
||||
hbitmap_reset_all(bitmap->bitmap);
|
||||
} else {
|
||||
HBitmap *backup = bitmap->bitmap;
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap->size,
|
||||
hbitmap_granularity(backup));
|
||||
*out = backup;
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||
{
|
||||
HBitmap *tmp = bitmap->bitmap;
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
bitmap->bitmap = in;
|
||||
hbitmap_free(tmp);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance an HBitmapIter to an arbitrary offset.
|
||||
*/
|
||||
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
|
||||
{
|
||||
assert(hbi->hb);
|
||||
hbitmap_iter_init(hbi, hbi->hb, offset);
|
||||
}
|
||||
|
||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return hbitmap_count(bitmap->bitmap);
|
||||
}
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
@@ -7,7 +7,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <glusterfs/api/glfs.h>
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/uri.h"
|
||||
|
65
block/io.c
65
block/io.c
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/blockjob.h"
|
||||
@@ -167,13 +166,9 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
bs->bl.max_transfer_length = bs->file->bs->bl.max_transfer_length;
|
||||
bs->bl.min_mem_alignment = bs->file->bs->bl.min_mem_alignment;
|
||||
bs->bl.opt_mem_alignment = bs->file->bs->bl.opt_mem_alignment;
|
||||
bs->bl.max_iov = bs->file->bs->bl.max_iov;
|
||||
} else {
|
||||
bs->bl.min_mem_alignment = 512;
|
||||
bs->bl.opt_mem_alignment = getpagesize();
|
||||
|
||||
/* Safe default since most protocols use readv()/writev()/etc */
|
||||
bs->bl.max_iov = IOV_MAX;
|
||||
}
|
||||
|
||||
if (bs->backing) {
|
||||
@@ -194,9 +189,6 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
bs->bl.min_mem_alignment =
|
||||
MAX(bs->bl.min_mem_alignment,
|
||||
bs->backing->bs->bl.min_mem_alignment);
|
||||
bs->bl.max_iov =
|
||||
MIN(bs->bl.max_iov,
|
||||
bs->backing->bs->bl.max_iov);
|
||||
}
|
||||
|
||||
/* Then let the driver override it */
|
||||
@@ -301,7 +293,6 @@ void bdrv_drain_all(void)
|
||||
if (bs->job) {
|
||||
block_job_pause(bs->job);
|
||||
}
|
||||
bdrv_drain_recurse(bs);
|
||||
aio_context_release(aio_context);
|
||||
|
||||
if (!g_slist_find(aio_ctxs, aio_context)) {
|
||||
@@ -665,7 +656,6 @@ int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
|
||||
{
|
||||
int64_t target_sectors, ret, nb_sectors, sector_num = 0;
|
||||
BlockDriverState *file;
|
||||
int n;
|
||||
|
||||
target_sectors = bdrv_nb_sectors(bs);
|
||||
@@ -678,7 +668,7 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
|
||||
if (nb_sectors <= 0) {
|
||||
return 0;
|
||||
}
|
||||
ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n, &file);
|
||||
ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n);
|
||||
if (ret < 0) {
|
||||
error_report("error getting block status at sector %" PRId64 ": %s",
|
||||
sector_num, strerror(-ret));
|
||||
@@ -1303,7 +1293,6 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
||||
if (bs->read_only) {
|
||||
return -EPERM;
|
||||
}
|
||||
assert(!(bs->open_flags & BDRV_O_INACTIVE));
|
||||
|
||||
ret = bdrv_check_byte_request(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
@@ -1468,7 +1457,6 @@ int bdrv_flush_all(void)
|
||||
typedef struct BdrvCoGetBlockStatusData {
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *base;
|
||||
BlockDriverState **file;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
int *pnum;
|
||||
@@ -1490,14 +1478,10 @@ typedef struct BdrvCoGetBlockStatusData {
|
||||
*
|
||||
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
||||
* beyond the end of the disk image it will be clamped.
|
||||
*
|
||||
* If returned value is positive and BDRV_BLOCK_OFFSET_VALID bit is set, 'file'
|
||||
* points to the BDS which the sector range is allocated in.
|
||||
*/
|
||||
static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
int64_t total_sectors;
|
||||
int64_t n;
|
||||
@@ -1527,9 +1511,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
*file = NULL;
|
||||
ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum,
|
||||
file);
|
||||
ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum);
|
||||
if (ret < 0) {
|
||||
*pnum = 0;
|
||||
return ret;
|
||||
@@ -1538,7 +1520,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
if (ret & BDRV_BLOCK_RAW) {
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
||||
return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, pnum, file);
|
||||
*pnum, pnum);
|
||||
}
|
||||
|
||||
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
|
||||
@@ -1555,14 +1537,13 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
if (*file && *file != bs &&
|
||||
if (bs->file &&
|
||||
(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) &&
|
||||
(ret & BDRV_BLOCK_OFFSET_VALID)) {
|
||||
BlockDriverState *file2;
|
||||
int file_pnum;
|
||||
|
||||
ret2 = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, &file_pnum, &file2);
|
||||
ret2 = bdrv_co_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, &file_pnum);
|
||||
if (ret2 >= 0) {
|
||||
/* Ignore errors. This is just providing extra information, it
|
||||
* is useful but not necessary.
|
||||
@@ -1587,15 +1568,14 @@ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
int *pnum,
|
||||
BlockDriverState **file)
|
||||
int *pnum)
|
||||
{
|
||||
BlockDriverState *p;
|
||||
int64_t ret = 0;
|
||||
|
||||
assert(bs != base);
|
||||
for (p = bs; p != base; p = backing_bs(p)) {
|
||||
ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum, file);
|
||||
ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum);
|
||||
if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) {
|
||||
break;
|
||||
}
|
||||
@@ -1614,8 +1594,7 @@ static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque)
|
||||
data->ret = bdrv_co_get_block_status_above(data->bs, data->base,
|
||||
data->sector_num,
|
||||
data->nb_sectors,
|
||||
data->pnum,
|
||||
data->file);
|
||||
data->pnum);
|
||||
data->done = true;
|
||||
}
|
||||
|
||||
@@ -1627,14 +1606,12 @@ static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque)
|
||||
int64_t bdrv_get_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
Coroutine *co;
|
||||
BdrvCoGetBlockStatusData data = {
|
||||
.bs = bs,
|
||||
.base = base,
|
||||
.file = file,
|
||||
.sector_num = sector_num,
|
||||
.nb_sectors = nb_sectors,
|
||||
.pnum = pnum,
|
||||
@@ -1658,19 +1635,16 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs,
|
||||
|
||||
int64_t bdrv_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
return bdrv_get_block_status_above(bs, backing_bs(bs),
|
||||
sector_num, nb_sectors, pnum, file);
|
||||
sector_num, nb_sectors, pnum);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BlockDriverState *file;
|
||||
int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum,
|
||||
&file);
|
||||
int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -1908,8 +1882,7 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
|
||||
merge = 1;
|
||||
}
|
||||
|
||||
if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 >
|
||||
bs->bl.max_iov) {
|
||||
if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) {
|
||||
merge = 0;
|
||||
}
|
||||
|
||||
@@ -2480,7 +2453,6 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
} else if (bs->read_only) {
|
||||
return -EPERM;
|
||||
}
|
||||
assert(!(bs->open_flags & BDRV_O_INACTIVE));
|
||||
|
||||
/* Do nothing if disabled. */
|
||||
if (!(bs->open_flags & BDRV_O_UNMAP)) {
|
||||
@@ -2642,11 +2614,10 @@ int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
bdrv_co_ioctl_entry(&data);
|
||||
} else {
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry);
|
||||
|
||||
qemu_coroutine_enter(co, &data);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
}
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
return data.ret;
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "config-host.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <math.h>
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "crypto/secret.h"
|
||||
|
||||
#include <iscsi/iscsi.h>
|
||||
#include <iscsi/scsi-lowlevel.h>
|
||||
@@ -533,8 +532,7 @@ static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
|
||||
|
||||
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct scsi_get_lba_status *lbas = NULL;
|
||||
@@ -626,9 +624,6 @@ out:
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
}
|
||||
if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
|
||||
*file = bs;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -655,8 +650,7 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||
!iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
|
||||
int64_t ret;
|
||||
int pnum;
|
||||
BlockDriverState *file;
|
||||
ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum, &file);
|
||||
ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -1081,8 +1075,6 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
||||
QemuOpts *opts;
|
||||
const char *user = NULL;
|
||||
const char *password = NULL;
|
||||
const char *secretid;
|
||||
char *secret = NULL;
|
||||
|
||||
list = qemu_find_opts("iscsi");
|
||||
if (!list) {
|
||||
@@ -1102,20 +1094,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
||||
return;
|
||||
}
|
||||
|
||||
secretid = qemu_opt_get(opts, "password-secret");
|
||||
password = qemu_opt_get(opts, "password");
|
||||
if (secretid && password) {
|
||||
error_setg(errp, "'password' and 'password-secret' properties are "
|
||||
"mutually exclusive");
|
||||
return;
|
||||
}
|
||||
if (secretid) {
|
||||
secret = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||
if (!secret) {
|
||||
return;
|
||||
}
|
||||
password = secret;
|
||||
} else if (!password) {
|
||||
if (!password) {
|
||||
error_setg(errp, "CHAP username specified but no password was given");
|
||||
return;
|
||||
}
|
||||
@@ -1123,8 +1103,6 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
||||
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
||||
error_setg(errp, "Failed to set initiator username and password");
|
||||
}
|
||||
|
||||
g_free(secret);
|
||||
}
|
||||
|
||||
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
||||
@@ -1265,13 +1243,8 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||
iscsilun->lbprz = !!rc16->lbprz;
|
||||
iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
||||
break;
|
||||
}
|
||||
/* Fall through and try READ CAPACITY(10) instead. */
|
||||
break;
|
||||
case TYPE_ROM:
|
||||
task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0);
|
||||
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
||||
@@ -1297,7 +1270,7 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||
&& retries-- > 0);
|
||||
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
error_setg(errp, "iSCSI: failed to send readcapacity10/16 command");
|
||||
error_setg(errp, "iSCSI: failed to send readcapacity10 command.");
|
||||
} else if (!iscsilun->block_size ||
|
||||
iscsilun->block_size % BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "iSCSI: the target returned an invalid "
|
||||
@@ -1874,11 +1847,6 @@ static QemuOptsList qemu_iscsi_opts = {
|
||||
.name = "password",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "password for CHAP authentication to target",
|
||||
},{
|
||||
.name = "password-secret",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of the secret providing password for CHAP "
|
||||
"authentication to target",
|
||||
},{
|
||||
.name = "header-digest",
|
||||
.type = QEMU_OPT_STRING,
|
||||
|
@@ -7,7 +7,6 @@
|
||||
* 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 "block/aio.h"
|
||||
#include "qemu/queue.h"
|
||||
|
381
block/mirror.c
381
block/mirror.c
@@ -11,7 +11,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
@@ -19,7 +18,6 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
#define MAX_IN_FLIGHT 16
|
||||
@@ -47,6 +45,7 @@ typedef struct MirrorBlockJob {
|
||||
BlockdevOnError on_source_error, on_target_error;
|
||||
bool synced;
|
||||
bool should_complete;
|
||||
int64_t sector_num;
|
||||
int64_t granularity;
|
||||
size_t buf_size;
|
||||
int64_t bdev_length;
|
||||
@@ -63,8 +62,6 @@ typedef struct MirrorBlockJob {
|
||||
int ret;
|
||||
bool unmap;
|
||||
bool waiting_for_io;
|
||||
int target_cluster_sectors;
|
||||
int max_iov;
|
||||
} MirrorBlockJob;
|
||||
|
||||
typedef struct MirrorOp {
|
||||
@@ -160,85 +157,113 @@ static void mirror_read_complete(void *opaque, int ret)
|
||||
mirror_write_complete, op);
|
||||
}
|
||||
|
||||
/* Round sector_num and/or nb_sectors to target cluster if COW is needed, and
|
||||
* return the offset of the adjusted tail sector against original. */
|
||||
static int mirror_cow_align(MirrorBlockJob *s,
|
||||
int64_t *sector_num,
|
||||
int *nb_sectors)
|
||||
{
|
||||
bool need_cow;
|
||||
int ret = 0;
|
||||
int chunk_sectors = s->granularity >> BDRV_SECTOR_BITS;
|
||||
int64_t align_sector_num = *sector_num;
|
||||
int align_nb_sectors = *nb_sectors;
|
||||
int max_sectors = chunk_sectors * s->max_iov;
|
||||
|
||||
need_cow = !test_bit(*sector_num / chunk_sectors, s->cow_bitmap);
|
||||
need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors,
|
||||
s->cow_bitmap);
|
||||
if (need_cow) {
|
||||
bdrv_round_to_clusters(s->target, *sector_num, *nb_sectors,
|
||||
&align_sector_num, &align_nb_sectors);
|
||||
}
|
||||
|
||||
if (align_nb_sectors > max_sectors) {
|
||||
align_nb_sectors = max_sectors;
|
||||
if (need_cow) {
|
||||
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors,
|
||||
s->target_cluster_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
ret = align_sector_num + align_nb_sectors - (*sector_num + *nb_sectors);
|
||||
*sector_num = align_sector_num;
|
||||
*nb_sectors = align_nb_sectors;
|
||||
assert(ret >= 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void mirror_wait_for_io(MirrorBlockJob *s)
|
||||
{
|
||||
assert(!s->waiting_for_io);
|
||||
s->waiting_for_io = true;
|
||||
qemu_coroutine_yield();
|
||||
s->waiting_for_io = false;
|
||||
}
|
||||
|
||||
/* Submit async read while handling COW.
|
||||
* Returns: nb_sectors if no alignment is necessary, or
|
||||
* (new_end - sector_num) if tail is rounded up or down due to
|
||||
* alignment or buffer limit.
|
||||
*/
|
||||
static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source = s->common.bs;
|
||||
int sectors_per_chunk, nb_chunks;
|
||||
int ret = nb_sectors;
|
||||
int nb_sectors, sectors_per_chunk, nb_chunks;
|
||||
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
|
||||
uint64_t delay_ns = 0;
|
||||
MirrorOp *op;
|
||||
int pnum;
|
||||
int64_t ret;
|
||||
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
if (s->sector_num < 0) {
|
||||
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||
assert(s->sector_num >= 0);
|
||||
}
|
||||
|
||||
hbitmap_next_sector = s->sector_num;
|
||||
sector_num = s->sector_num;
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||
|
||||
/* We can only handle as much as buf_size at a time. */
|
||||
nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors);
|
||||
assert(nb_sectors);
|
||||
/* Extend the QEMUIOVector to include all adjacent blocks that will
|
||||
* be copied in this operation.
|
||||
*
|
||||
* We have to do this if we have no backing file yet in the destination,
|
||||
* and the cluster size is very large. Then we need to do COW ourselves.
|
||||
* The first time a cluster is copied, copy it entirely. Note that,
|
||||
* because both the granularity and the cluster size are powers of two,
|
||||
* the number of sectors to copy cannot exceed one cluster.
|
||||
*
|
||||
* We also want to extend the QEMUIOVector to include more adjacent
|
||||
* dirty blocks if possible, to limit the number of I/O operations and
|
||||
* run efficiently even with a small granularity.
|
||||
*/
|
||||
nb_chunks = 0;
|
||||
nb_sectors = 0;
|
||||
next_sector = sector_num;
|
||||
next_chunk = sector_num / sectors_per_chunk;
|
||||
|
||||
if (s->cow_bitmap) {
|
||||
ret += mirror_cow_align(s, §or_num, &nb_sectors);
|
||||
}
|
||||
assert(nb_sectors << BDRV_SECTOR_BITS <= s->buf_size);
|
||||
/* The sector range must meet granularity because:
|
||||
* 1) Caller passes in aligned values;
|
||||
* 2) mirror_cow_align is used only when target cluster is larger. */
|
||||
assert(!(nb_sectors % sectors_per_chunk));
|
||||
assert(!(sector_num % sectors_per_chunk));
|
||||
nb_chunks = nb_sectors / sectors_per_chunk;
|
||||
|
||||
while (s->buf_free_count < nb_chunks) {
|
||||
/* Wait for I/O to this cluster (from a previous iteration) to be done. */
|
||||
while (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||
mirror_wait_for_io(s);
|
||||
s->waiting_for_io = true;
|
||||
qemu_coroutine_yield();
|
||||
s->waiting_for_io = false;
|
||||
}
|
||||
|
||||
do {
|
||||
int added_sectors, added_chunks;
|
||||
|
||||
if (!bdrv_get_dirty(source, s->dirty_bitmap, next_sector) ||
|
||||
test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
assert(nb_sectors > 0);
|
||||
break;
|
||||
}
|
||||
|
||||
added_sectors = sectors_per_chunk;
|
||||
if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) {
|
||||
bdrv_round_to_clusters(s->target,
|
||||
next_sector, added_sectors,
|
||||
&next_sector, &added_sectors);
|
||||
|
||||
/* On the first iteration, the rounding may make us copy
|
||||
* sectors before the first dirty one.
|
||||
*/
|
||||
if (next_sector < sector_num) {
|
||||
assert(nb_sectors == 0);
|
||||
sector_num = next_sector;
|
||||
next_chunk = next_sector / sectors_per_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors));
|
||||
added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk;
|
||||
|
||||
/* When doing COW, it may happen that there is not enough space for
|
||||
* a full cluster. Wait if that is the case.
|
||||
*/
|
||||
while (nb_chunks == 0 && s->buf_free_count < added_chunks) {
|
||||
trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight);
|
||||
s->waiting_for_io = true;
|
||||
qemu_coroutine_yield();
|
||||
s->waiting_for_io = false;
|
||||
}
|
||||
if (s->buf_free_count < nb_chunks + added_chunks) {
|
||||
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
|
||||
break;
|
||||
}
|
||||
if (IOV_MAX < nb_chunks + added_chunks) {
|
||||
trace_mirror_break_iov_max(s, nb_chunks, added_chunks);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We have enough free space to copy these sectors. */
|
||||
bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks);
|
||||
|
||||
nb_sectors += added_sectors;
|
||||
nb_chunks += added_chunks;
|
||||
next_sector += added_sectors;
|
||||
next_chunk += added_chunks;
|
||||
if (!s->synced && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors);
|
||||
}
|
||||
} while (delay_ns == 0 && next_sector < end);
|
||||
|
||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||
op = g_new(MirrorOp, 1);
|
||||
op->s = s;
|
||||
@@ -249,151 +274,47 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||
* from s->buf_free.
|
||||
*/
|
||||
qemu_iovec_init(&op->qiov, nb_chunks);
|
||||
next_sector = sector_num;
|
||||
while (nb_chunks-- > 0) {
|
||||
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
||||
size_t remaining = nb_sectors * BDRV_SECTOR_SIZE - op->qiov.size;
|
||||
size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size;
|
||||
|
||||
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
||||
s->buf_free_count--;
|
||||
qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
|
||||
|
||||
/* Advance the HBitmapIter in parallel, so that we do not examine
|
||||
* the same sector twice.
|
||||
*/
|
||||
if (next_sector > hbitmap_next_sector
|
||||
&& bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
||||
hbitmap_next_sector = hbitmap_iter_next(&s->hbi);
|
||||
}
|
||||
|
||||
next_sector += sectors_per_chunk;
|
||||
}
|
||||
|
||||
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, nb_sectors);
|
||||
|
||||
/* Copy the dirty cluster. */
|
||||
s->in_flight++;
|
||||
s->sectors_in_flight += nb_sectors;
|
||||
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
||||
|
||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||
mirror_read_complete, op);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
bool is_discard)
|
||||
{
|
||||
MirrorOp *op;
|
||||
|
||||
/* Allocate a MirrorOp that is used as an AIO callback. The qiov is zeroed
|
||||
* so the freeing in mirror_iteration_done is nop. */
|
||||
op = g_new0(MirrorOp, 1);
|
||||
op->s = s;
|
||||
op->sector_num = sector_num;
|
||||
op->nb_sectors = nb_sectors;
|
||||
|
||||
s->in_flight++;
|
||||
s->sectors_in_flight += nb_sectors;
|
||||
if (is_discard) {
|
||||
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
||||
mirror_write_complete, op);
|
||||
} else {
|
||||
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
||||
nb_sectors, &pnum);
|
||||
if (ret < 0 || pnum < nb_sectors ||
|
||||
(ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) {
|
||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||
mirror_read_complete, op);
|
||||
} else if (ret & BDRV_BLOCK_ZERO) {
|
||||
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
|
||||
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||
mirror_write_complete, op);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source = s->common.bs;
|
||||
int64_t sector_num;
|
||||
uint64_t delay_ns = 0;
|
||||
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||
int nb_chunks = 1;
|
||||
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
|
||||
sector_num = hbitmap_iter_next(&s->hbi);
|
||||
if (sector_num < 0) {
|
||||
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
|
||||
sector_num = hbitmap_iter_next(&s->hbi);
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||
assert(sector_num >= 0);
|
||||
}
|
||||
|
||||
/* Find the number of consective dirty chunks following the first dirty
|
||||
* one, and wait for in flight requests in them. */
|
||||
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||
int64_t hbitmap_next;
|
||||
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
||||
int64_t next_chunk = next_sector / sectors_per_chunk;
|
||||
if (next_sector >= end ||
|
||||
!bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
||||
break;
|
||||
}
|
||||
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
if (nb_chunks > 0) {
|
||||
break;
|
||||
}
|
||||
trace_mirror_yield_in_flight(s, next_sector, s->in_flight);
|
||||
mirror_wait_for_io(s);
|
||||
/* Now retry. */
|
||||
} else {
|
||||
hbitmap_next = hbitmap_iter_next(&s->hbi);
|
||||
assert(hbitmap_next == next_sector);
|
||||
nb_chunks++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear dirty bits before querying the block status, because
|
||||
* calling bdrv_get_block_status_above could yield - if some blocks are
|
||||
* marked dirty in this window, we need to know.
|
||||
*/
|
||||
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num,
|
||||
nb_chunks * sectors_per_chunk);
|
||||
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
||||
while (nb_chunks > 0 && sector_num < end) {
|
||||
int ret;
|
||||
int io_sectors;
|
||||
BlockDriverState *file;
|
||||
enum MirrorMethod {
|
||||
MIRROR_METHOD_COPY,
|
||||
MIRROR_METHOD_ZERO,
|
||||
MIRROR_METHOD_DISCARD
|
||||
} mirror_method = MIRROR_METHOD_COPY;
|
||||
|
||||
assert(!(sector_num % sectors_per_chunk));
|
||||
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
||||
nb_chunks * sectors_per_chunk,
|
||||
&io_sectors, &file);
|
||||
if (ret < 0) {
|
||||
io_sectors = nb_chunks * sectors_per_chunk;
|
||||
}
|
||||
|
||||
io_sectors -= io_sectors % sectors_per_chunk;
|
||||
if (io_sectors < sectors_per_chunk) {
|
||||
io_sectors = sectors_per_chunk;
|
||||
} else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) {
|
||||
int64_t target_sector_num;
|
||||
int target_nb_sectors;
|
||||
bdrv_round_to_clusters(s->target, sector_num, io_sectors,
|
||||
&target_sector_num, &target_nb_sectors);
|
||||
if (target_sector_num == sector_num &&
|
||||
target_nb_sectors == io_sectors) {
|
||||
mirror_method = ret & BDRV_BLOCK_ZERO ?
|
||||
MIRROR_METHOD_ZERO :
|
||||
MIRROR_METHOD_DISCARD;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mirror_method) {
|
||||
case MIRROR_METHOD_COPY:
|
||||
io_sectors = mirror_do_read(s, sector_num, io_sectors);
|
||||
break;
|
||||
case MIRROR_METHOD_ZERO:
|
||||
mirror_do_zero_or_discard(s, sector_num, io_sectors, false);
|
||||
break;
|
||||
case MIRROR_METHOD_DISCARD:
|
||||
mirror_do_zero_or_discard(s, sector_num, io_sectors, true);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
assert(io_sectors);
|
||||
sector_num += io_sectors;
|
||||
nb_chunks -= io_sectors / sectors_per_chunk;
|
||||
delay_ns += ratelimit_calculate_delay(&s->limit, io_sectors);
|
||||
} else {
|
||||
assert(!(ret & BDRV_BLOCK_DATA));
|
||||
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
||||
mirror_write_complete, op);
|
||||
}
|
||||
return delay_ns;
|
||||
}
|
||||
@@ -418,7 +339,9 @@ static void mirror_free_init(MirrorBlockJob *s)
|
||||
static void mirror_drain(MirrorBlockJob *s)
|
||||
{
|
||||
while (s->in_flight > 0) {
|
||||
mirror_wait_for_io(s);
|
||||
s->waiting_for_io = true;
|
||||
qemu_coroutine_yield();
|
||||
s->waiting_for_io = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,22 +370,11 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
if (s->to_replace) {
|
||||
to_replace = s->to_replace;
|
||||
}
|
||||
|
||||
/* This was checked in mirror_start_job(), but meanwhile one of the
|
||||
* nodes could have been newly attached to a BlockBackend. */
|
||||
if (to_replace->blk && s->target->blk) {
|
||||
error_report("block job: Can't create node with two BlockBackends");
|
||||
data->ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
|
||||
bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
|
||||
}
|
||||
bdrv_replace_in_backing_chain(to_replace, s->target);
|
||||
}
|
||||
|
||||
out:
|
||||
if (s->to_replace) {
|
||||
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
|
||||
error_free(s->replace_blocker);
|
||||
@@ -492,7 +404,6 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
checking for a NULL string */
|
||||
int ret = 0;
|
||||
int n;
|
||||
int target_cluster_size = BDRV_SECTOR_SIZE;
|
||||
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
goto immediate_exit;
|
||||
@@ -522,16 +433,16 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
*/
|
||||
bdrv_get_backing_filename(s->target, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
if (!bdrv_get_info(s->target, &bdi) && bdi.cluster_size) {
|
||||
target_cluster_size = bdi.cluster_size;
|
||||
if (backing_filename[0] && !s->target->backing) {
|
||||
ret = bdrv_get_info(s->target, &bdi);
|
||||
if (ret < 0) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
if (s->granularity < bdi.cluster_size) {
|
||||
s->buf_size = MAX(s->buf_size, bdi.cluster_size);
|
||||
s->cow_bitmap = bitmap_new(length);
|
||||
}
|
||||
}
|
||||
if (backing_filename[0] && !s->target->backing
|
||||
&& s->granularity < target_cluster_size) {
|
||||
s->buf_size = MAX(s->buf_size, target_cluster_size);
|
||||
s->cow_bitmap = bitmap_new(length);
|
||||
}
|
||||
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
|
||||
s->max_iov = MIN(s->common.bs->bl.max_iov, s->target->bl.max_iov);
|
||||
|
||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
||||
@@ -606,7 +517,9 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||
(cnt == 0 && s->in_flight > 0)) {
|
||||
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
|
||||
mirror_wait_for_io(s);
|
||||
s->waiting_for_io = true;
|
||||
qemu_coroutine_yield();
|
||||
s->waiting_for_io = false;
|
||||
continue;
|
||||
} else if (cnt != 0) {
|
||||
delay_ns = mirror_iteration(s);
|
||||
@@ -727,7 +640,7 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open_backing_file(s->target, NULL, "backing", &local_err);
|
||||
ret = bdrv_open_backing_file(s->target, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -792,7 +705,6 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
bool is_none_mode, BlockDriverState *base)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
BlockDriverState *replaced_bs;
|
||||
|
||||
if (granularity == 0) {
|
||||
granularity = bdrv_get_default_bitmap_granularity(target);
|
||||
@@ -816,21 +728,6 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
||||
}
|
||||
|
||||
/* We can't support this case as long as the block layer can't handle
|
||||
* multiple BlockBackends per BlockDriverState. */
|
||||
if (replaces) {
|
||||
replaced_bs = bdrv_lookup_bs(replaces, replaces, errp);
|
||||
if (replaced_bs == NULL) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
replaced_bs = bs;
|
||||
}
|
||||
if (replaced_bs->blk && target->blk) {
|
||||
error_setg(errp, "Can't create node with two BlockBackends");
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
|
@@ -26,8 +26,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "nbd-client.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
||||
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
|
||||
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
|
||||
@@ -47,21 +47,13 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
|
||||
if (!client->ioc) { /* Already closed */
|
||||
return;
|
||||
}
|
||||
|
||||
/* finish any pending coroutines */
|
||||
qio_channel_shutdown(client->ioc,
|
||||
QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||
NULL);
|
||||
shutdown(client->sock, 2);
|
||||
nbd_recv_coroutines_enter_all(client);
|
||||
|
||||
nbd_client_detach_aio_context(bs);
|
||||
object_unref(OBJECT(client->sioc));
|
||||
client->sioc = NULL;
|
||||
object_unref(OBJECT(client->ioc));
|
||||
client->ioc = NULL;
|
||||
closesocket(client->sock);
|
||||
client->sock = -1;
|
||||
}
|
||||
|
||||
static void nbd_reply_ready(void *opaque)
|
||||
@@ -71,16 +63,12 @@ static void nbd_reply_ready(void *opaque)
|
||||
uint64_t i;
|
||||
int ret;
|
||||
|
||||
if (!s->ioc) { /* Already closed */
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->reply.handle == 0) {
|
||||
/* No reply already in flight. Fetch a header. It is possible
|
||||
* that another thread has done the same thing in parallel, so
|
||||
* the socket is not readable anymore.
|
||||
*/
|
||||
ret = nbd_receive_reply(s->ioc, &s->reply);
|
||||
ret = nbd_receive_reply(s->sock, &s->reply);
|
||||
if (ret == -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
@@ -131,35 +119,32 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
g_assert(qemu_in_coroutine());
|
||||
assert(i < MAX_NBD_REQUESTS);
|
||||
request->handle = INDEX_TO_HANDLE(s, i);
|
||||
|
||||
if (!s->ioc) {
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
s->send_coroutine = qemu_coroutine_self();
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_set_fd_handler(aio_context, s->sioc->fd, false,
|
||||
aio_set_fd_handler(aio_context, s->sock, false,
|
||||
nbd_reply_ready, nbd_restart_write, bs);
|
||||
if (qiov) {
|
||||
qio_channel_set_cork(s->ioc, true);
|
||||
rc = nbd_send_request(s->ioc, request);
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 1);
|
||||
}
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
if (rc >= 0) {
|
||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
|
||||
offset, request->len, 0);
|
||||
ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (ret != request->len) {
|
||||
rc = -EIO;
|
||||
}
|
||||
}
|
||||
qio_channel_set_cork(s->ioc, false);
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 0);
|
||||
}
|
||||
} else {
|
||||
rc = nbd_send_request(s->ioc, request);
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
}
|
||||
aio_set_fd_handler(aio_context, s->sioc->fd, false,
|
||||
aio_set_fd_handler(aio_context, s->sock, false,
|
||||
nbd_reply_ready, NULL, bs);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
@@ -176,13 +161,12 @@ static void nbd_co_receive_reply(NbdClientSession *s,
|
||||
* peek at the next reply and avoid yielding if it's ours? */
|
||||
qemu_coroutine_yield();
|
||||
*reply = s->reply;
|
||||
if (reply->handle != request->handle ||
|
||||
!s->ioc) {
|
||||
if (reply->handle != request->handle) {
|
||||
reply->error = EIO;
|
||||
} else {
|
||||
if (qiov && reply->error == 0) {
|
||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
|
||||
offset, request->len, 1);
|
||||
ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (ret != request->len) {
|
||||
reply->error = EIO;
|
||||
}
|
||||
@@ -365,14 +349,14 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs),
|
||||
nbd_get_client_session(bs)->sioc->fd,
|
||||
nbd_get_client_session(bs)->sock,
|
||||
false, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd,
|
||||
aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock,
|
||||
false, nbd_reply_ready, NULL, bs);
|
||||
}
|
||||
|
||||
@@ -385,20 +369,16 @@ void nbd_client_close(BlockDriverState *bs)
|
||||
.len = 0
|
||||
};
|
||||
|
||||
if (client->ioc == NULL) {
|
||||
if (client->sock == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nbd_send_request(client->ioc, &request);
|
||||
nbd_send_request(client->sock, &request);
|
||||
|
||||
nbd_teardown_connection(bs);
|
||||
}
|
||||
|
||||
int nbd_client_init(BlockDriverState *bs,
|
||||
QIOChannelSocket *sioc,
|
||||
const char *export,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *hostname,
|
||||
int nbd_client_init(BlockDriverState *bs, int sock, const char *export,
|
||||
Error **errp)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
@@ -406,32 +386,22 @@ int nbd_client_init(BlockDriverState *bs,
|
||||
|
||||
/* NBD handshake */
|
||||
logout("session init %s\n", export);
|
||||
qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
|
||||
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
||||
&client->nbdflags,
|
||||
tlscreds, hostname,
|
||||
&client->ioc,
|
||||
&client->size, errp);
|
||||
qemu_set_block(sock);
|
||||
ret = nbd_receive_negotiate(sock, export,
|
||||
&client->nbdflags, &client->size, errp);
|
||||
if (ret < 0) {
|
||||
logout("Failed to negotiate with the NBD server\n");
|
||||
closesocket(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&client->send_mutex);
|
||||
qemu_co_mutex_init(&client->free_sema);
|
||||
client->sioc = sioc;
|
||||
object_ref(OBJECT(client->sioc));
|
||||
|
||||
if (!client->ioc) {
|
||||
client->ioc = QIO_CHANNEL(sioc);
|
||||
object_ref(OBJECT(client->ioc));
|
||||
}
|
||||
client->sock = sock;
|
||||
|
||||
/* Now that we're connected, set the socket to be non-blocking and
|
||||
* kick the reply mechanism. */
|
||||
qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
|
||||
|
||||
qemu_set_nonblock(sock);
|
||||
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
||||
|
||||
logout("Established connection with NBD server\n");
|
||||
|
@@ -4,7 +4,6 @@
|
||||
#include "qemu-common.h"
|
||||
#include "block/nbd.h"
|
||||
#include "block/block_int.h"
|
||||
#include "io/channel-socket.h"
|
||||
|
||||
/* #define DEBUG_NBD */
|
||||
|
||||
@@ -18,8 +17,7 @@
|
||||
#define MAX_NBD_REQUESTS 16
|
||||
|
||||
typedef struct NbdClientSession {
|
||||
QIOChannelSocket *sioc; /* The master data channel */
|
||||
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
||||
int sock;
|
||||
uint32_t nbdflags;
|
||||
off_t size;
|
||||
|
||||
@@ -36,11 +34,7 @@ typedef struct NbdClientSession {
|
||||
|
||||
NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
|
||||
|
||||
int nbd_client_init(BlockDriverState *bs,
|
||||
QIOChannelSocket *sock,
|
||||
const char *export_name,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *hostname,
|
||||
int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name,
|
||||
Error **errp);
|
||||
void nbd_client_close(BlockDriverState *bs);
|
||||
|
||||
|
134
block/nbd.c
134
block/nbd.c
@@ -26,16 +26,18 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/nbd-client.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qint.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define EN_OPTSTR ":exportname="
|
||||
|
||||
@@ -204,20 +206,18 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
||||
saddr = g_new0(SocketAddress, 1);
|
||||
|
||||
if (qdict_haskey(options, "path")) {
|
||||
UnixSocketAddress *q_unix;
|
||||
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
||||
q_unix = saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
||||
q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
||||
saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
||||
saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
||||
qdict_del(options, "path");
|
||||
} else {
|
||||
InetSocketAddress *inet;
|
||||
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
||||
inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
|
||||
inet->host = g_strdup(qdict_get_str(options, "host"));
|
||||
saddr->u.inet = g_new0(InetSocketAddress, 1);
|
||||
saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
|
||||
if (!qdict_get_try_str(options, "port")) {
|
||||
inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
||||
saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
||||
} else {
|
||||
inet->port = g_strdup(qdict_get_str(options, "port"));
|
||||
saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
|
||||
}
|
||||
qdict_del(options, "host");
|
||||
qdict_del(options, "port");
|
||||
@@ -239,113 +239,55 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
|
||||
return &s->client;
|
||||
}
|
||||
|
||||
static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
|
||||
Error **errp)
|
||||
static int nbd_establish_connection(BlockDriverState *bs,
|
||||
SocketAddress *saddr,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelSocket *sioc;
|
||||
Error *local_err = NULL;
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
int sock;
|
||||
|
||||
sioc = qio_channel_socket_new();
|
||||
sock = socket_connect(saddr, errp, NULL, NULL);
|
||||
|
||||
qio_channel_socket_connect_sync(sioc,
|
||||
saddr,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
if (sock < 0) {
|
||||
logout("Failed to establish connection to NBD server\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
|
||||
if (!s->client.is_unix) {
|
||||
socket_set_nodelay(sock);
|
||||
}
|
||||
|
||||
return sioc;
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
{
|
||||
Object *obj;
|
||||
QCryptoTLSCreds *creds;
|
||||
|
||||
obj = object_resolve_path_component(
|
||||
object_get_objects_root(), id);
|
||||
if (!obj) {
|
||||
error_setg(errp, "No TLS credentials with id '%s'",
|
||||
id);
|
||||
return NULL;
|
||||
}
|
||||
creds = (QCryptoTLSCreds *)
|
||||
object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
|
||||
if (!creds) {
|
||||
error_setg(errp, "Object with id '%s' is not TLS credentials",
|
||||
id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
|
||||
error_setg(errp,
|
||||
"Expecting TLS credentials with a client endpoint");
|
||||
return NULL;
|
||||
}
|
||||
object_ref(obj);
|
||||
return creds;
|
||||
}
|
||||
|
||||
|
||||
static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
char *export = NULL;
|
||||
QIOChannelSocket *sioc = NULL;
|
||||
int result, sock;
|
||||
SocketAddress *saddr;
|
||||
const char *tlscredsid;
|
||||
QCryptoTLSCreds *tlscreds = NULL;
|
||||
const char *hostname = NULL;
|
||||
int ret = -EINVAL;
|
||||
|
||||
/* Pop the config into our state object. Exit if invalid. */
|
||||
saddr = nbd_config(s, options, &export, errp);
|
||||
if (!saddr) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds"));
|
||||
if (tlscredsid) {
|
||||
qdict_del(options, "tls-creds");
|
||||
tlscreds = nbd_get_tls_creds(tlscredsid, errp);
|
||||
if (!tlscreds) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (saddr->type != SOCKET_ADDRESS_KIND_INET) {
|
||||
error_setg(errp, "TLS only supported over IP sockets");
|
||||
goto error;
|
||||
}
|
||||
hostname = saddr->u.inet->host;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* establish TCP connection, return error if it fails
|
||||
* TODO: Configurable retry-until-timeout behaviour.
|
||||
*/
|
||||
sioc = nbd_establish_connection(saddr, errp);
|
||||
if (!sioc) {
|
||||
ret = -ECONNREFUSED;
|
||||
goto error;
|
||||
sock = nbd_establish_connection(bs, saddr, errp);
|
||||
qapi_free_SocketAddress(saddr);
|
||||
if (sock < 0) {
|
||||
g_free(export);
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* NBD handshake */
|
||||
ret = nbd_client_init(bs, sioc, export,
|
||||
tlscreds, hostname, errp);
|
||||
error:
|
||||
if (sioc) {
|
||||
object_unref(OBJECT(sioc));
|
||||
}
|
||||
if (tlscreds) {
|
||||
object_unref(OBJECT(tlscreds));
|
||||
}
|
||||
qapi_free_SocketAddress(saddr);
|
||||
result = nbd_client_init(bs, sock, export, errp);
|
||||
g_free(export);
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -400,14 +342,13 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
|
||||
nbd_client_attach_aio_context(bs, new_context);
|
||||
}
|
||||
|
||||
static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
static void nbd_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
QDict *opts = qdict_new();
|
||||
const char *path = qdict_get_try_str(options, "path");
|
||||
const char *host = qdict_get_try_str(options, "host");
|
||||
const char *port = qdict_get_try_str(options, "port");
|
||||
const char *export = qdict_get_try_str(options, "export");
|
||||
const char *tlscreds = qdict_get_try_str(options, "tls-creds");
|
||||
const char *path = qdict_get_try_str(bs->options, "path");
|
||||
const char *host = qdict_get_try_str(bs->options, "host");
|
||||
const char *port = qdict_get_try_str(bs->options, "port");
|
||||
const char *export = qdict_get_try_str(bs->options, "export");
|
||||
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
|
||||
|
||||
@@ -442,9 +383,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
if (export) {
|
||||
qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
|
||||
}
|
||||
if (tlscreds) {
|
||||
qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds)));
|
||||
}
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
14
block/nfs.c
14
block/nfs.c
@@ -22,7 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "config-host.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include "qemu-common.h"
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <nfsc/libnfs.h>
|
||||
|
||||
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
|
||||
#define QEMU_NFS_MAX_DEBUG_LEVEL 2
|
||||
|
||||
typedef struct NFSClient {
|
||||
struct nfs_context *context;
|
||||
@@ -334,17 +333,6 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||
val = QEMU_NFS_MAX_READAHEAD_SIZE;
|
||||
}
|
||||
nfs_set_readahead(client->context, val);
|
||||
#endif
|
||||
#ifdef LIBNFS_FEATURE_DEBUG
|
||||
} else if (!strcmp(qp->p[i].name, "debug")) {
|
||||
/* limit the maximum debug level to avoid potential flooding
|
||||
* of our log files. */
|
||||
if (val > QEMU_NFS_MAX_DEBUG_LEVEL) {
|
||||
error_report("NFS Warning: Limiting NFS debug level"
|
||||
" to %d", QEMU_NFS_MAX_DEBUG_LEVEL);
|
||||
val = QEMU_NFS_MAX_DEBUG_LEVEL;
|
||||
}
|
||||
nfs_set_debug(client->context, val);
|
||||
#endif
|
||||
} else {
|
||||
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||
|
@@ -10,7 +10,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
#define NULL_OPT_LATENCY "latency-ns"
|
||||
|
@@ -27,10 +27,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qapi/util.h"
|
||||
@@ -63,7 +61,7 @@ typedef struct ParallelsHeader {
|
||||
typedef enum ParallelsPreallocMode {
|
||||
PRL_PREALLOC_MODE_FALLOCATE = 0,
|
||||
PRL_PREALLOC_MODE_TRUNCATE = 1,
|
||||
PRL_PREALLOC_MODE__MAX = 2,
|
||||
PRL_PREALLOC_MODE_MAX = 2,
|
||||
} ParallelsPreallocMode;
|
||||
|
||||
static const char *prealloc_mode_lookup[] = {
|
||||
@@ -262,7 +260,7 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs)
|
||||
|
||||
|
||||
static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
int64_t offset;
|
||||
@@ -275,7 +273,6 @@ static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
*file = bs->file->bs;
|
||||
return (offset << BDRV_SECTOR_BITS) |
|
||||
BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
|
||||
}
|
||||
@@ -462,7 +459,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int64_t total_size, cl_size;
|
||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *file;
|
||||
BlockDriverState *file;
|
||||
uint32_t bat_entries, bat_sectors;
|
||||
ParallelsHeader header;
|
||||
int ret;
|
||||
@@ -478,17 +475,14 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
file = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (file == NULL) {
|
||||
file = NULL;
|
||||
ret = bdrv_open(&file, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(file, true);
|
||||
|
||||
ret = blk_truncate(file, 0);
|
||||
ret = bdrv_truncate(file, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -512,18 +506,18 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
memcpy(tmp, &header, sizeof(header));
|
||||
|
||||
ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
|
||||
ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = blk_write_zeroes(file, 1, bat_sectors - 1, 0);
|
||||
ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
blk_unref(file);
|
||||
bdrv_unref(file);
|
||||
return ret;
|
||||
|
||||
exit:
|
||||
@@ -666,7 +660,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS);
|
||||
buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE);
|
||||
s->prealloc_mode = qapi_enum_parse(prealloc_mode_lookup, buf,
|
||||
PRL_PREALLOC_MODE__MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err);
|
||||
PRL_PREALLOC_MODE_MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err);
|
||||
g_free(buf);
|
||||
if (local_err != NULL) {
|
||||
goto fail_options;
|
||||
|
248
block/qapi.c
248
block/qapi.c
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/qapi.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/throttle-groups.h"
|
||||
@@ -92,26 +91,6 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
|
||||
info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||
info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||
|
||||
info->has_bps_max_length = info->has_bps_max;
|
||||
info->bps_max_length =
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length;
|
||||
info->has_bps_rd_max_length = info->has_bps_rd_max;
|
||||
info->bps_rd_max_length =
|
||||
cfg.buckets[THROTTLE_BPS_READ].burst_length;
|
||||
info->has_bps_wr_max_length = info->has_bps_wr_max;
|
||||
info->bps_wr_max_length =
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].burst_length;
|
||||
|
||||
info->has_iops_max_length = info->has_iops_max;
|
||||
info->iops_max_length =
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length;
|
||||
info->has_iops_rd_max_length = info->has_iops_rd_max;
|
||||
info->iops_rd_max_length =
|
||||
cfg.buckets[THROTTLE_OPS_READ].burst_length;
|
||||
info->has_iops_wr_max_length = info->has_iops_wr_max;
|
||||
info->iops_wr_max_length =
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].burst_length;
|
||||
|
||||
info->has_iops_size = cfg.op_size;
|
||||
info->iops_size = cfg.op_size;
|
||||
|
||||
@@ -231,13 +210,11 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
Error *err = NULL;
|
||||
ImageInfo *info;
|
||||
|
||||
aio_context_acquire(bdrv_get_aio_context(bs));
|
||||
|
||||
size = bdrv_getlength(bs);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "Can't get size of device '%s'",
|
||||
bdrv_get_device_name(bs));
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
info = g_new0(ImageInfo, 1);
|
||||
@@ -268,18 +245,15 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
info->has_backing_filename = true;
|
||||
bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err);
|
||||
if (err) {
|
||||
/* Can't reconstruct the full backing filename, so we must omit
|
||||
* this field and apply a Best Effort to this query. */
|
||||
error_propagate(errp, err);
|
||||
qapi_free_ImageInfo(info);
|
||||
g_free(backing_filename2);
|
||||
backing_filename2 = NULL;
|
||||
error_free(err);
|
||||
err = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Always report the full_backing_filename if present, even if it's the
|
||||
* same as backing_filename. That they are same is useful info. */
|
||||
if (backing_filename2) {
|
||||
info->full_backing_filename = g_strdup(backing_filename2);
|
||||
if (strcmp(backing_filename, backing_filename2) != 0) {
|
||||
info->full_backing_filename =
|
||||
g_strdup(backing_filename2);
|
||||
info->has_full_backing_filename = true;
|
||||
}
|
||||
|
||||
@@ -305,13 +279,10 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
default:
|
||||
error_propagate(errp, err);
|
||||
qapi_free_ImageInfo(info);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
*p_info = info;
|
||||
|
||||
out:
|
||||
aio_context_release(bdrv_get_aio_context(bs));
|
||||
}
|
||||
|
||||
/* @p_info will be set only on success. */
|
||||
@@ -325,7 +296,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
info->locked = blk_dev_is_medium_locked(blk);
|
||||
info->removable = blk_dev_has_removable_media(blk);
|
||||
|
||||
if (blk_dev_has_tray(blk)) {
|
||||
if (blk_dev_has_removable_media(blk)) {
|
||||
info->has_tray_open = true;
|
||||
info->tray_open = blk_dev_is_tray_open(blk);
|
||||
}
|
||||
@@ -355,116 +326,100 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||
const BlockDriverState *bs,
|
||||
bool query_backing);
|
||||
|
||||
static void bdrv_query_blk_stats(BlockStats *s, BlockBackend *blk)
|
||||
static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
BlockAcctStats *stats = blk_get_stats(blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
BlockStats *s;
|
||||
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(blk_name(blk));
|
||||
s = g_malloc0(sizeof(*s));
|
||||
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
|
||||
s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (s->stats->has_idle_time_ns) {
|
||||
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
|
||||
if (bdrv_get_device_name(bs)[0]) {
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(bdrv_get_device_name(bs));
|
||||
}
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
|
||||
dev_stats->avg_rd_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
if (bdrv_get_node_name(bs)[0]) {
|
||||
s->has_node_name = true;
|
||||
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
||||
}
|
||||
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
if (bs->blk) {
|
||||
BlockAcctStats *stats = blk_get_stats(bs->blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
|
||||
s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (s->stats->has_idle_time_ns) {
|
||||
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
|
||||
}
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
|
||||
dev_stats->avg_rd_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
s->parent = bdrv_query_stats(NULL, bs->file->bs, query_backing);
|
||||
s->parent = bdrv_query_stats(bs->file->bs, query_backing);
|
||||
}
|
||||
|
||||
if (query_backing && bs->backing) {
|
||||
s->has_backing = true;
|
||||
s->backing = bdrv_query_stats(NULL, bs->backing->bs, query_backing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||
const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
BlockStats *s;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
|
||||
if (blk) {
|
||||
bdrv_query_blk_stats(s, blk);
|
||||
}
|
||||
if (bs) {
|
||||
bdrv_query_bds_stats(s, bs, query_backing);
|
||||
s->backing = bdrv_query_stats(bs->backing->bs, query_backing);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -493,38 +448,22 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
return head;
|
||||
}
|
||||
|
||||
static bool next_query_bds(BlockBackend **blk, BlockDriverState **bs,
|
||||
bool query_nodes)
|
||||
{
|
||||
if (query_nodes) {
|
||||
*bs = bdrv_next_node(*bs);
|
||||
return !!*bs;
|
||||
}
|
||||
|
||||
*blk = blk_next(*blk);
|
||||
*bs = *blk ? blk_bs(*blk) : NULL;
|
||||
|
||||
return !!*blk;
|
||||
}
|
||||
|
||||
BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
bool query_nodes,
|
||||
Error **errp)
|
||||
{
|
||||
BlockStatsList *head = NULL, **p_next = &head;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
/* Just to be safe if query_nodes is not always initialized */
|
||||
query_nodes = has_query_nodes && query_nodes;
|
||||
|
||||
while (next_query_bds(&blk, &bs, query_nodes)) {
|
||||
while ((bs = query_nodes ? bdrv_next_node(bs) : bdrv_next(bs))) {
|
||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||
AioContext *ctx = blk ? blk_get_aio_context(blk)
|
||||
: bdrv_get_aio_context(bs);
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
info->value = bdrv_query_stats(blk, bs, !query_nodes);
|
||||
info->value = bdrv_query_stats(bs, !query_nodes);
|
||||
aio_context_release(ctx);
|
||||
|
||||
*p_next = info;
|
||||
@@ -649,7 +588,7 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
|
||||
int i = 0;
|
||||
|
||||
for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
|
||||
QType type = qobject_type(entry->value);
|
||||
qtype_code type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";
|
||||
|
||||
@@ -667,7 +606,7 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
const QDictEntry *entry;
|
||||
|
||||
for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
|
||||
QType type = qobject_type(entry->value);
|
||||
qtype_code type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
|
||||
char key[strlen(entry->key) + 1];
|
||||
@@ -693,7 +632,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||
QObject *obj, *data;
|
||||
|
||||
visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec,
|
||||
visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL,
|
||||
&error_abort);
|
||||
obj = qmp_output_get_qobject(ov);
|
||||
assert(qobject_type(obj) == QTYPE_QDICT);
|
||||
@@ -737,10 +676,7 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||
|
||||
if (info->has_backing_filename) {
|
||||
func_fprintf(f, "backing file: %s", info->backing_filename);
|
||||
if (!info->has_full_backing_filename) {
|
||||
func_fprintf(f, " (cannot determine actual path)");
|
||||
} else if (strcmp(info->backing_filename,
|
||||
info->full_backing_filename) != 0) {
|
||||
if (info->has_full_backing_filename) {
|
||||
func_fprintf(f, " (actual path: %s)", info->full_backing_filename);
|
||||
}
|
||||
func_fprintf(f, "\n");
|
||||
|
28
block/qcow.c
28
block/qcow.c
@@ -21,10 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -490,7 +488,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
@@ -511,7 +509,6 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
|
||||
return BDRV_BLOCK_DATA;
|
||||
}
|
||||
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||
}
|
||||
|
||||
@@ -781,7 +778,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int flags = 0;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockBackend *qcow_blk;
|
||||
BlockDriverState *qcow_bs;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
@@ -797,18 +794,15 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
qcow_blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (qcow_blk == NULL) {
|
||||
qcow_bs = NULL;
|
||||
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||
|
||||
ret = blk_truncate(qcow_blk, 0);
|
||||
ret = bdrv_truncate(qcow_bs, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -848,13 +842,13 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
/* write all the data */
|
||||
ret = blk_pwrite(qcow_blk, 0, &header, sizeof(header));
|
||||
ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header));
|
||||
if (ret != sizeof(header)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (backing_file) {
|
||||
ret = blk_pwrite(qcow_blk, sizeof(header),
|
||||
ret = bdrv_pwrite(qcow_bs, sizeof(header),
|
||||
backing_file, backing_filename_len);
|
||||
if (ret != backing_filename_len) {
|
||||
goto exit;
|
||||
@@ -864,7 +858,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
tmp = g_malloc0(BDRV_SECTOR_SIZE);
|
||||
for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/
|
||||
BDRV_SECTOR_SIZE); i++) {
|
||||
ret = blk_pwrite(qcow_blk, header_size +
|
||||
ret = bdrv_pwrite(qcow_bs, header_size +
|
||||
BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE);
|
||||
if (ret != BDRV_SECTOR_SIZE) {
|
||||
g_free(tmp);
|
||||
@@ -875,7 +869,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
g_free(tmp);
|
||||
ret = 0;
|
||||
exit:
|
||||
blk_unref(qcow_blk);
|
||||
bdrv_unref(qcow_bs);
|
||||
cleanup:
|
||||
g_free(backing_file);
|
||||
return ret;
|
||||
|
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
/* Needed for CONFIG_MADVISE */
|
||||
#include "qemu/osdep.h"
|
||||
#include "config-host.h"
|
||||
|
||||
#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE)
|
||||
#include <sys/mman.h>
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/osdep.h"
|
||||
#include "qcow2.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
@@ -1642,8 +1641,7 @@ fail:
|
||||
static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
int l1_size, int64_t *visited_l1_entries,
|
||||
int64_t l1_entries,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque)
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
bool is_active_l1 = (l1_table == s->l1_table);
|
||||
@@ -1669,7 +1667,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
/* unallocated */
|
||||
(*visited_l1_entries)++;
|
||||
if (status_cb) {
|
||||
status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque);
|
||||
status_cb(bs, *visited_l1_entries, l1_entries);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1806,7 +1804,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
|
||||
(*visited_l1_entries)++;
|
||||
if (status_cb) {
|
||||
status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque);
|
||||
status_cb(bs, *visited_l1_entries, l1_entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1830,8 +1828,7 @@ fail:
|
||||
* qcow2 version which doesn't yet support metadata zero clusters.
|
||||
*/
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque)
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL;
|
||||
@@ -1848,7 +1845,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
|
||||
&visited_l1_entries, l1_entries,
|
||||
status_cb, cb_opaque);
|
||||
status_cb);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1884,7 +1881,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
|
||||
&visited_l1_entries, l1_entries,
|
||||
status_cb, cb_opaque);
|
||||
status_cb);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
@@ -1346,9 +1345,6 @@ static int inc_refcounts(BlockDriverState *bs,
|
||||
if (refcount == s->refcount_max) {
|
||||
fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
|
||||
"\n", cluster_offset);
|
||||
fprintf(stderr, "Use qemu-img amend to increase the refcount entry "
|
||||
"width or qemu-img convert to create a clean copy if the "
|
||||
"image cannot be opened for writing\n");
|
||||
res->corruptions++;
|
||||
continue;
|
||||
}
|
||||
@@ -2471,450 +2467,3 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A pointer to a function of this type is given to walk_over_reftable(). That
|
||||
* function will create refblocks and pass them to a RefblockFinishOp once they
|
||||
* are completed (@refblock). @refblock_empty is set if the refblock is
|
||||
* completely empty.
|
||||
*
|
||||
* Along with the refblock, a corresponding reftable entry is passed, in the
|
||||
* reftable @reftable (which may be reallocated) at @reftable_index.
|
||||
*
|
||||
* @allocated should be set to true if a new cluster has been allocated.
|
||||
*/
|
||||
typedef int (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable,
|
||||
uint64_t reftable_index, uint64_t *reftable_size,
|
||||
void *refblock, bool refblock_empty,
|
||||
bool *allocated, Error **errp);
|
||||
|
||||
/**
|
||||
* This "operation" for walk_over_reftable() allocates the refblock on disk (if
|
||||
* it is not empty) and inserts its offset into the new reftable. The size of
|
||||
* this new reftable is increased as required.
|
||||
*/
|
||||
static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable,
|
||||
uint64_t reftable_index, uint64_t *reftable_size,
|
||||
void *refblock, bool refblock_empty, bool *allocated,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int64_t offset;
|
||||
|
||||
if (!refblock_empty && reftable_index >= *reftable_size) {
|
||||
uint64_t *new_reftable;
|
||||
uint64_t new_reftable_size;
|
||||
|
||||
new_reftable_size = ROUND_UP(reftable_index + 1,
|
||||
s->cluster_size / sizeof(uint64_t));
|
||||
if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
|
||||
error_setg(errp,
|
||||
"This operation would make the refcount table grow "
|
||||
"beyond the maximum size supported by QEMU, aborting");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
new_reftable = g_try_realloc(*reftable, new_reftable_size *
|
||||
sizeof(uint64_t));
|
||||
if (!new_reftable) {
|
||||
error_setg(errp, "Failed to increase reftable buffer size");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(new_reftable + *reftable_size, 0,
|
||||
(new_reftable_size - *reftable_size) * sizeof(uint64_t));
|
||||
|
||||
*reftable = new_reftable;
|
||||
*reftable_size = new_reftable_size;
|
||||
}
|
||||
|
||||
if (!refblock_empty && !(*reftable)[reftable_index]) {
|
||||
offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
if (offset < 0) {
|
||||
error_setg_errno(errp, -offset, "Failed to allocate refblock");
|
||||
return offset;
|
||||
}
|
||||
(*reftable)[reftable_index] = offset;
|
||||
*allocated = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This "operation" for walk_over_reftable() writes the refblock to disk at the
|
||||
* offset specified by the new reftable's entry. It does not modify the new
|
||||
* reftable or change any refcounts.
|
||||
*/
|
||||
static int flush_refblock(BlockDriverState *bs, uint64_t **reftable,
|
||||
uint64_t reftable_index, uint64_t *reftable_size,
|
||||
void *refblock, bool refblock_empty, bool *allocated,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int64_t offset;
|
||||
int ret;
|
||||
|
||||
if (reftable_index < *reftable_size && (*reftable)[reftable_index]) {
|
||||
offset = (*reftable)[reftable_index];
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Overlap check failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, refblock, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to write refblock");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
assert(refblock_empty);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function walks over the existing reftable and every referenced refblock;
|
||||
* if @new_set_refcount is non-NULL, it is called for every refcount entry to
|
||||
* create an equal new entry in the passed @new_refblock. Once that
|
||||
* @new_refblock is completely filled, @operation will be called.
|
||||
*
|
||||
* @status_cb and @cb_opaque are used for the amend operation's status callback.
|
||||
* @index is the index of the walk_over_reftable() calls and @total is the total
|
||||
* number of walk_over_reftable() calls per amend operation. Both are used for
|
||||
* calculating the parameters for the status callback.
|
||||
*
|
||||
* @allocated is set to true if a new cluster has been allocated.
|
||||
*/
|
||||
static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable,
|
||||
uint64_t *new_reftable_index,
|
||||
uint64_t *new_reftable_size,
|
||||
void *new_refblock, int new_refblock_size,
|
||||
int new_refcount_bits,
|
||||
RefblockFinishOp *operation, bool *allocated,
|
||||
Qcow2SetRefcountFunc *new_set_refcount,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque, int index, int total,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t reftable_index;
|
||||
bool new_refblock_empty = true;
|
||||
int refblock_index;
|
||||
int new_refblock_index = 0;
|
||||
int ret;
|
||||
|
||||
for (reftable_index = 0; reftable_index < s->refcount_table_size;
|
||||
reftable_index++)
|
||||
{
|
||||
uint64_t refblock_offset = s->refcount_table[reftable_index]
|
||||
& REFT_OFFSET_MASK;
|
||||
|
||||
status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index,
|
||||
(uint64_t)total * s->refcount_table_size, cb_opaque);
|
||||
|
||||
if (refblock_offset) {
|
||||
void *refblock;
|
||||
|
||||
if (offset_into_cluster(s, refblock_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
|
||||
PRIx64 " unaligned (reftable index: %#"
|
||||
PRIx64 ")", refblock_offset,
|
||||
reftable_index);
|
||||
error_setg(errp,
|
||||
"Image is corrupt (unaligned refblock offset)");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset,
|
||||
&refblock);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to retrieve refblock");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (refblock_index = 0; refblock_index < s->refcount_block_size;
|
||||
refblock_index++)
|
||||
{
|
||||
uint64_t refcount;
|
||||
|
||||
if (new_refblock_index >= new_refblock_size) {
|
||||
/* new_refblock is now complete */
|
||||
ret = operation(bs, new_reftable, *new_reftable_index,
|
||||
new_reftable_size, new_refblock,
|
||||
new_refblock_empty, allocated, errp);
|
||||
if (ret < 0) {
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*new_reftable_index)++;
|
||||
new_refblock_index = 0;
|
||||
new_refblock_empty = true;
|
||||
}
|
||||
|
||||
refcount = s->get_refcount(refblock, refblock_index);
|
||||
if (new_refcount_bits < 64 && refcount >> new_refcount_bits) {
|
||||
uint64_t offset;
|
||||
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
|
||||
|
||||
offset = ((reftable_index << s->refcount_block_bits)
|
||||
+ refblock_index) << s->cluster_bits;
|
||||
|
||||
error_setg(errp, "Cannot decrease refcount entry width to "
|
||||
"%i bits: Cluster at offset %#" PRIx64 " has a "
|
||||
"refcount of %" PRIu64, new_refcount_bits,
|
||||
offset, refcount);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_set_refcount) {
|
||||
new_set_refcount(new_refblock, new_refblock_index++,
|
||||
refcount);
|
||||
} else {
|
||||
new_refblock_index++;
|
||||
}
|
||||
new_refblock_empty = new_refblock_empty && refcount == 0;
|
||||
}
|
||||
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
|
||||
} else {
|
||||
/* No refblock means every refcount is 0 */
|
||||
for (refblock_index = 0; refblock_index < s->refcount_block_size;
|
||||
refblock_index++)
|
||||
{
|
||||
if (new_refblock_index >= new_refblock_size) {
|
||||
/* new_refblock is now complete */
|
||||
ret = operation(bs, new_reftable, *new_reftable_index,
|
||||
new_reftable_size, new_refblock,
|
||||
new_refblock_empty, allocated, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*new_reftable_index)++;
|
||||
new_refblock_index = 0;
|
||||
new_refblock_empty = true;
|
||||
}
|
||||
|
||||
if (new_set_refcount) {
|
||||
new_set_refcount(new_refblock, new_refblock_index++, 0);
|
||||
} else {
|
||||
new_refblock_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_refblock_index > 0) {
|
||||
/* Complete the potentially existing partially filled final refblock */
|
||||
if (new_set_refcount) {
|
||||
for (; new_refblock_index < new_refblock_size;
|
||||
new_refblock_index++)
|
||||
{
|
||||
new_set_refcount(new_refblock, new_refblock_index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ret = operation(bs, new_reftable, *new_reftable_index,
|
||||
new_reftable_size, new_refblock, new_refblock_empty,
|
||||
allocated, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*new_reftable_index)++;
|
||||
}
|
||||
|
||||
status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size,
|
||||
(uint64_t)total * s->refcount_table_size, cb_opaque);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2GetRefcountFunc *new_get_refcount;
|
||||
Qcow2SetRefcountFunc *new_set_refcount;
|
||||
void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size);
|
||||
uint64_t *new_reftable = NULL, new_reftable_size = 0;
|
||||
uint64_t *old_reftable, old_reftable_size, old_reftable_offset;
|
||||
uint64_t new_reftable_index = 0;
|
||||
uint64_t i;
|
||||
int64_t new_reftable_offset = 0, allocated_reftable_size = 0;
|
||||
int new_refblock_size, new_refcount_bits = 1 << refcount_order;
|
||||
int old_refcount_order;
|
||||
int walk_index = 0;
|
||||
int ret;
|
||||
bool new_allocation;
|
||||
|
||||
assert(s->qcow_version >= 3);
|
||||
assert(refcount_order >= 0 && refcount_order <= 6);
|
||||
|
||||
/* see qcow2_open() */
|
||||
new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3));
|
||||
|
||||
new_get_refcount = get_refcount_funcs[refcount_order];
|
||||
new_set_refcount = set_refcount_funcs[refcount_order];
|
||||
|
||||
|
||||
do {
|
||||
int total_walks;
|
||||
|
||||
new_allocation = false;
|
||||
|
||||
/* At least we have to do this walk and the one which writes the
|
||||
* refblocks; also, at least we have to do this loop here at least
|
||||
* twice (normally), first to do the allocations, and second to
|
||||
* determine that everything is correctly allocated, this then makes
|
||||
* three walks in total */
|
||||
total_walks = MAX(walk_index + 2, 3);
|
||||
|
||||
/* First, allocate the structures so they are present in the refcount
|
||||
* structures */
|
||||
ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index,
|
||||
&new_reftable_size, NULL, new_refblock_size,
|
||||
new_refcount_bits, &alloc_refblock,
|
||||
&new_allocation, NULL, status_cb, cb_opaque,
|
||||
walk_index++, total_walks, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
new_reftable_index = 0;
|
||||
|
||||
if (new_allocation) {
|
||||
if (new_reftable_offset) {
|
||||
qcow2_free_clusters(bs, new_reftable_offset,
|
||||
allocated_reftable_size * sizeof(uint64_t),
|
||||
QCOW2_DISCARD_NEVER);
|
||||
}
|
||||
|
||||
new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size *
|
||||
sizeof(uint64_t));
|
||||
if (new_reftable_offset < 0) {
|
||||
error_setg_errno(errp, -new_reftable_offset,
|
||||
"Failed to allocate the new reftable");
|
||||
ret = new_reftable_offset;
|
||||
goto done;
|
||||
}
|
||||
allocated_reftable_size = new_reftable_size;
|
||||
}
|
||||
} while (new_allocation);
|
||||
|
||||
/* Second, write the new refblocks */
|
||||
ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index,
|
||||
&new_reftable_size, new_refblock,
|
||||
new_refblock_size, new_refcount_bits,
|
||||
&flush_refblock, &new_allocation, new_set_refcount,
|
||||
status_cb, cb_opaque, walk_index, walk_index + 1,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
assert(!new_allocation);
|
||||
|
||||
|
||||
/* Write the new reftable */
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset,
|
||||
new_reftable_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Overlap check failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < new_reftable_size; i++) {
|
||||
cpu_to_be64s(&new_reftable[i]);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, new_reftable_offset, new_reftable,
|
||||
new_reftable_size * sizeof(uint64_t));
|
||||
|
||||
for (i = 0; i < new_reftable_size; i++) {
|
||||
be64_to_cpus(&new_reftable[i]);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to write the new reftable");
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/* Empty the refcount cache */
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to flush the refblock cache");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Update the image header to point to the new reftable; this only updates
|
||||
* the fields which are relevant to qcow2_update_header(); other fields
|
||||
* such as s->refcount_table or s->refcount_bits stay stale for now
|
||||
* (because we have to restore everything if qcow2_update_header() fails) */
|
||||
old_refcount_order = s->refcount_order;
|
||||
old_reftable_size = s->refcount_table_size;
|
||||
old_reftable_offset = s->refcount_table_offset;
|
||||
|
||||
s->refcount_order = refcount_order;
|
||||
s->refcount_table_size = new_reftable_size;
|
||||
s->refcount_table_offset = new_reftable_offset;
|
||||
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->refcount_order = old_refcount_order;
|
||||
s->refcount_table_size = old_reftable_size;
|
||||
s->refcount_table_offset = old_reftable_offset;
|
||||
error_setg_errno(errp, -ret, "Failed to update the qcow2 header");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Now update the rest of the in-memory information */
|
||||
old_reftable = s->refcount_table;
|
||||
s->refcount_table = new_reftable;
|
||||
|
||||
s->refcount_bits = 1 << refcount_order;
|
||||
s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1);
|
||||
s->refcount_max += s->refcount_max - 1;
|
||||
|
||||
s->refcount_block_bits = s->cluster_bits - (refcount_order - 3);
|
||||
s->refcount_block_size = 1 << s->refcount_block_bits;
|
||||
|
||||
s->get_refcount = new_get_refcount;
|
||||
s->set_refcount = new_set_refcount;
|
||||
|
||||
/* For cleaning up all old refblocks and the old reftable below the "done"
|
||||
* label */
|
||||
new_reftable = old_reftable;
|
||||
new_reftable_size = old_reftable_size;
|
||||
new_reftable_offset = old_reftable_offset;
|
||||
|
||||
done:
|
||||
if (new_reftable) {
|
||||
/* On success, new_reftable actually points to the old reftable (and
|
||||
* new_reftable_size is the old reftable's size); but that is just
|
||||
* fine */
|
||||
for (i = 0; i < new_reftable_size; i++) {
|
||||
uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK;
|
||||
if (offset) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_OTHER);
|
||||
}
|
||||
}
|
||||
g_free(new_reftable);
|
||||
|
||||
if (new_reftable_offset > 0) {
|
||||
qcow2_free_clusters(bs, new_reftable_offset,
|
||||
new_reftable_size * sizeof(uint64_t),
|
||||
QCOW2_DISCARD_OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_vfree(new_refblock);
|
||||
return ret;
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
|
409
block/qcow2.c
409
block/qcow2.c
@@ -21,10 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "block/qcow2.h"
|
||||
@@ -1142,7 +1140,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Clear unknown autoclear feature bits */
|
||||
if (!bs->read_only && !(flags & BDRV_O_INACTIVE) && s->autoclear_features) {
|
||||
if (!bs->read_only && !(flags & BDRV_O_INCOMING) && s->autoclear_features) {
|
||||
s->autoclear_features = 0;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
@@ -1155,7 +1153,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
/* Repair image if dirty */
|
||||
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
|
||||
if (!(flags & (BDRV_O_CHECK | BDRV_O_INCOMING)) && !bs->read_only &&
|
||||
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
@@ -1284,54 +1282,8 @@ static void qcow2_reopen_abort(BDRVReopenState *state)
|
||||
g_free(state->opaque);
|
||||
}
|
||||
|
||||
static void qcow2_join_options(QDict *options, QDict *old_options)
|
||||
{
|
||||
bool has_new_overlap_template =
|
||||
qdict_haskey(options, QCOW2_OPT_OVERLAP) ||
|
||||
qdict_haskey(options, QCOW2_OPT_OVERLAP_TEMPLATE);
|
||||
bool has_new_total_cache_size =
|
||||
qdict_haskey(options, QCOW2_OPT_CACHE_SIZE);
|
||||
bool has_all_cache_options;
|
||||
|
||||
/* New overlap template overrides all old overlap options */
|
||||
if (has_new_overlap_template) {
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_TEMPLATE);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_MAIN_HEADER);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L1);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L2);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_TABLE);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L1);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L2);
|
||||
}
|
||||
|
||||
/* New total cache size overrides all old options */
|
||||
if (qdict_haskey(options, QCOW2_OPT_CACHE_SIZE)) {
|
||||
qdict_del(old_options, QCOW2_OPT_L2_CACHE_SIZE);
|
||||
qdict_del(old_options, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
|
||||
}
|
||||
|
||||
qdict_join(options, old_options, false);
|
||||
|
||||
/*
|
||||
* If after merging all cache size options are set, an old total size is
|
||||
* overwritten. Do keep all options, however, if all three are new. The
|
||||
* resulting error message is what we want to happen.
|
||||
*/
|
||||
has_all_cache_options =
|
||||
qdict_haskey(options, QCOW2_OPT_CACHE_SIZE) ||
|
||||
qdict_haskey(options, QCOW2_OPT_L2_CACHE_SIZE) ||
|
||||
qdict_haskey(options, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
|
||||
|
||||
if (has_all_cache_options && !has_new_total_cache_size) {
|
||||
qdict_del(options, QCOW2_OPT_CACHE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t cluster_offset;
|
||||
@@ -1350,7 +1302,6 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
!s->cipher) {
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
|
||||
*file = bs->file->bs;
|
||||
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||
}
|
||||
if (ret == QCOW2_CLUSTER_ZERO) {
|
||||
@@ -1688,32 +1639,6 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_inactivate(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int ret, result = 0;
|
||||
|
||||
ret = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
if (ret) {
|
||||
result = ret;
|
||||
error_report("Failed to flush the L2 table cache: %s",
|
||||
strerror(-ret));
|
||||
}
|
||||
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret) {
|
||||
result = ret;
|
||||
error_report("Failed to flush the refcount block cache: %s",
|
||||
strerror(-ret));
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
qcow2_mark_clean(bs);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void qcow2_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
@@ -1721,8 +1646,24 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
/* else pre-write overlap checks in cache_destroy may crash */
|
||||
s->l1_table = NULL;
|
||||
|
||||
if (!(s->flags & BDRV_O_INACTIVE)) {
|
||||
qcow2_inactivate(bs);
|
||||
if (!(bs->open_flags & BDRV_O_INCOMING)) {
|
||||
int ret1, ret2;
|
||||
|
||||
ret1 = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
ret2 = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
|
||||
if (ret1) {
|
||||
error_report("Failed to flush the L2 table cache: %s",
|
||||
strerror(-ret1));
|
||||
}
|
||||
if (ret2) {
|
||||
error_report("Failed to flush the refcount block cache: %s",
|
||||
strerror(-ret2));
|
||||
}
|
||||
|
||||
if (!ret1 && !ret2) {
|
||||
qcow2_mark_clean(bs);
|
||||
}
|
||||
}
|
||||
|
||||
cache_clean_timer_del(bs);
|
||||
@@ -1766,24 +1707,21 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
bdrv_invalidate_cache(bs->file->bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
bs->drv = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(s, 0, sizeof(BDRVQcow2State));
|
||||
options = qdict_clone_shallow(bs->options);
|
||||
|
||||
flags &= ~BDRV_O_INACTIVE;
|
||||
ret = qcow2_open(bs, options, flags, &local_err);
|
||||
QDECREF(options);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qcow2 layer: ");
|
||||
bs->drv = NULL;
|
||||
error_setg(errp, "Could not reopen qcow2 layer: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qcow2 layer");
|
||||
bs->drv = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1911,33 +1849,31 @@ int qcow2_update_header(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
/* Feature table */
|
||||
if (s->qcow_version >= 3) {
|
||||
Qcow2Feature features[] = {
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||
.name = "dirty bit",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||
.bit = QCOW2_INCOMPAT_CORRUPT_BITNR,
|
||||
.name = "corrupt bit",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
|
||||
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
||||
.name = "lazy refcounts",
|
||||
},
|
||||
};
|
||||
Qcow2Feature features[] = {
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||
.name = "dirty bit",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||
.bit = QCOW2_INCOMPAT_CORRUPT_BITNR,
|
||||
.name = "corrupt bit",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
|
||||
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
||||
.name = "lazy refcounts",
|
||||
},
|
||||
};
|
||||
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
|
||||
features, sizeof(features), buflen);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
|
||||
features, sizeof(features), buflen);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
|
||||
/* Keep unknown header extensions */
|
||||
QLIST_FOREACH(uext, &s->unknown_header_ext, next) {
|
||||
@@ -2098,7 +2034,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
||||
* size for any qcow2 image.
|
||||
*/
|
||||
BlockBackend *blk;
|
||||
BlockDriverState* bs;
|
||||
QCowHeader *header;
|
||||
uint64_t* refcount_table;
|
||||
Error *local_err = NULL;
|
||||
@@ -2173,16 +2109,14 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Write the header */
|
||||
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
|
||||
header = g_malloc0(cluster_size);
|
||||
@@ -2210,7 +2144,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
||||
}
|
||||
|
||||
ret = blk_pwrite(blk, 0, header, cluster_size);
|
||||
ret = bdrv_pwrite(bs, 0, header, cluster_size);
|
||||
g_free(header);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write qcow2 header");
|
||||
@@ -2220,7 +2154,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
/* Write a refcount table with one refcount block */
|
||||
refcount_table = g_malloc0(2 * cluster_size);
|
||||
refcount_table[0] = cpu_to_be64(2 * cluster_size);
|
||||
ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size);
|
||||
ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size);
|
||||
g_free(refcount_table);
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -2228,8 +2162,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_unref(blk);
|
||||
blk = NULL;
|
||||
bdrv_unref(bs);
|
||||
bs = NULL;
|
||||
|
||||
/*
|
||||
* And now open the image and make it consistent first (i.e. increase the
|
||||
@@ -2238,16 +2172,15 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
*/
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||
blk = blk_new_open("image-qcow2", filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
ret = bdrv_open(&bs, filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size);
|
||||
ret = qcow2_alloc_clusters(bs, 3 * cluster_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
|
||||
"header and refcount table");
|
||||
@@ -2258,15 +2191,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Create a full header (including things like feature table) */
|
||||
ret = qcow2_update_header(blk_bs(blk));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not update qcow2 header");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Okay, now that we have a valid image, let's give it the right size */
|
||||
ret = blk_truncate(blk, total_size);
|
||||
ret = bdrv_truncate(bs, total_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not resize image");
|
||||
goto out;
|
||||
@@ -2274,7 +2200,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
|
||||
/* Want a backing file? There you go.*/
|
||||
if (backing_file) {
|
||||
ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
|
||||
ret = bdrv_change_backing_file(bs, backing_file, backing_format);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
|
||||
"with format '%s'", backing_file, backing_format);
|
||||
@@ -2284,9 +2210,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
|
||||
/* And if we're supposed to preallocate metadata, do that now */
|
||||
if (prealloc != PREALLOC_MODE_OFF) {
|
||||
BDRVQcow2State *s = blk_bs(blk)->opaque;
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = preallocate(blk_bs(blk));
|
||||
ret = preallocate(bs);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not preallocate metadata");
|
||||
@@ -2294,25 +2220,24 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
}
|
||||
}
|
||||
|
||||
blk_unref(blk);
|
||||
blk = NULL;
|
||||
bdrv_unref(bs);
|
||||
bs = NULL;
|
||||
|
||||
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||
blk = blk_new_open("image-flush", filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
ret = bdrv_open(&bs, filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -2344,7 +2269,7 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
|
||||
PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -2832,10 +2757,6 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
|
||||
.has_corrupt = true,
|
||||
.refcount_bits = s->refcount_bits,
|
||||
};
|
||||
} else {
|
||||
/* if this assertion fails, this probably means a new version was
|
||||
* added without having it covered here */
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return spec_info;
|
||||
@@ -2903,7 +2824,7 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
* have to be removed.
|
||||
*/
|
||||
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int current_version = s->qcow_version;
|
||||
@@ -2918,7 +2839,13 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
}
|
||||
|
||||
if (s->refcount_order != 4) {
|
||||
error_report("compat=0.10 requires refcount_bits=16");
|
||||
/* we would have to convert the image to a refcount_order == 4 image
|
||||
* here; however, since qemu (at the time of writing this) does not
|
||||
* support anything different than 4 anyway, there is no point in doing
|
||||
* so right now; however, we should error out (if qemu supports this in
|
||||
* the future and this code has not been adapted) */
|
||||
error_report("qcow2_downgrade: Image refcount orders other than 4 are "
|
||||
"currently not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -2946,7 +2873,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
/* clearing autoclear features is trivial */
|
||||
s->autoclear_features = 0;
|
||||
|
||||
ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
|
||||
ret = qcow2_expand_zero_clusters(bs, status_cb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -2960,79 +2887,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum Qcow2AmendOperation {
|
||||
/* This is the value Qcow2AmendHelperCBInfo::last_operation will be
|
||||
* statically initialized to so that the helper CB can discern the first
|
||||
* invocation from an operation change */
|
||||
QCOW2_NO_OPERATION = 0,
|
||||
|
||||
QCOW2_CHANGING_REFCOUNT_ORDER,
|
||||
QCOW2_DOWNGRADING,
|
||||
} Qcow2AmendOperation;
|
||||
|
||||
typedef struct Qcow2AmendHelperCBInfo {
|
||||
/* The code coordinating the amend operations should only modify
|
||||
* these four fields; the rest will be managed by the CB */
|
||||
BlockDriverAmendStatusCB *original_status_cb;
|
||||
void *original_cb_opaque;
|
||||
|
||||
Qcow2AmendOperation current_operation;
|
||||
|
||||
/* Total number of operations to perform (only set once) */
|
||||
int total_operations;
|
||||
|
||||
/* The following fields are managed by the CB */
|
||||
|
||||
/* Number of operations completed */
|
||||
int operations_completed;
|
||||
|
||||
/* Cumulative offset of all completed operations */
|
||||
int64_t offset_completed;
|
||||
|
||||
Qcow2AmendOperation last_operation;
|
||||
int64_t last_work_size;
|
||||
} Qcow2AmendHelperCBInfo;
|
||||
|
||||
static void qcow2_amend_helper_cb(BlockDriverState *bs,
|
||||
int64_t operation_offset,
|
||||
int64_t operation_work_size, void *opaque)
|
||||
{
|
||||
Qcow2AmendHelperCBInfo *info = opaque;
|
||||
int64_t current_work_size;
|
||||
int64_t projected_work_size;
|
||||
|
||||
if (info->current_operation != info->last_operation) {
|
||||
if (info->last_operation != QCOW2_NO_OPERATION) {
|
||||
info->offset_completed += info->last_work_size;
|
||||
info->operations_completed++;
|
||||
}
|
||||
|
||||
info->last_operation = info->current_operation;
|
||||
}
|
||||
|
||||
assert(info->total_operations > 0);
|
||||
assert(info->operations_completed < info->total_operations);
|
||||
|
||||
info->last_work_size = operation_work_size;
|
||||
|
||||
current_work_size = info->offset_completed + operation_work_size;
|
||||
|
||||
/* current_work_size is the total work size for (operations_completed + 1)
|
||||
* operations (which includes this one), so multiply it by the number of
|
||||
* operations not covered and divide it by the number of operations
|
||||
* covered to get a projection for the operations not covered */
|
||||
projected_work_size = current_work_size * (info->total_operations -
|
||||
info->operations_completed - 1)
|
||||
/ (info->operations_completed + 1);
|
||||
|
||||
info->original_status_cb(bs, info->offset_completed + operation_offset,
|
||||
current_work_size + projected_work_size,
|
||||
info->original_cb_opaque);
|
||||
}
|
||||
|
||||
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque)
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int old_version = s->qcow_version, new_version = old_version;
|
||||
@@ -3042,10 +2898,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
const char *compat = NULL;
|
||||
uint64_t cluster_size = s->cluster_size;
|
||||
bool encrypt;
|
||||
int refcount_bits = s->refcount_bits;
|
||||
int ret;
|
||||
QemuOptDesc *desc = opts->list->desc;
|
||||
Qcow2AmendHelperCBInfo helper_cb_info;
|
||||
|
||||
while (desc && desc->name) {
|
||||
if (!qemu_opt_find(opts, desc->name)) {
|
||||
@@ -3063,11 +2917,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
} else if (!strcmp(compat, "1.1")) {
|
||||
new_version = 3;
|
||||
} else {
|
||||
error_report("Unknown compatibility level %s", compat);
|
||||
fprintf(stderr, "Unknown compatibility level %s.\n", compat);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
|
||||
error_report("Cannot change preallocation mode");
|
||||
fprintf(stderr, "Cannot change preallocation mode.\n");
|
||||
return -ENOTSUP;
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
|
||||
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
|
||||
@@ -3080,74 +2934,47 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
!!s->cipher);
|
||||
|
||||
if (encrypt != !!s->cipher) {
|
||||
error_report("Changing the encryption flag is not supported");
|
||||
fprintf(stderr, "Changing the encryption flag is not "
|
||||
"supported.\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
cluster_size);
|
||||
if (cluster_size != s->cluster_size) {
|
||||
error_report("Changing the cluster size is not supported");
|
||||
fprintf(stderr, "Changing the cluster size is not "
|
||||
"supported.\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
|
||||
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
|
||||
lazy_refcounts);
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) {
|
||||
refcount_bits = qemu_opt_get_number(opts, BLOCK_OPT_REFCOUNT_BITS,
|
||||
refcount_bits);
|
||||
|
||||
if (refcount_bits <= 0 || refcount_bits > 64 ||
|
||||
!is_power_of_2(refcount_bits))
|
||||
{
|
||||
error_report("Refcount width must be a power of two and may "
|
||||
"not exceed 64 bits");
|
||||
return -EINVAL;
|
||||
}
|
||||
error_report("Cannot change refcount entry width");
|
||||
return -ENOTSUP;
|
||||
} else {
|
||||
/* if this point is reached, this probably means a new option was
|
||||
/* if this assertion fails, this probably means a new option was
|
||||
* added without having it covered here */
|
||||
abort();
|
||||
assert(false);
|
||||
}
|
||||
|
||||
desc++;
|
||||
}
|
||||
|
||||
helper_cb_info = (Qcow2AmendHelperCBInfo){
|
||||
.original_status_cb = status_cb,
|
||||
.original_cb_opaque = cb_opaque,
|
||||
.total_operations = (new_version < old_version)
|
||||
+ (s->refcount_bits != refcount_bits)
|
||||
};
|
||||
|
||||
/* Upgrade first (some features may require compat=1.1) */
|
||||
if (new_version > old_version) {
|
||||
s->qcow_version = new_version;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->qcow_version = old_version;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->refcount_bits != refcount_bits) {
|
||||
int refcount_order = ctz32(refcount_bits);
|
||||
Error *local_error = NULL;
|
||||
|
||||
if (new_version < 3 && refcount_bits != 16) {
|
||||
error_report("Different refcount widths than 16 bits require "
|
||||
"compatibility level 1.1 or above (use compat=1.1 or "
|
||||
"greater)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
|
||||
ret = qcow2_change_refcount_order(bs, refcount_order,
|
||||
&qcow2_amend_helper_cb,
|
||||
&helper_cb_info, &local_error);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_error);
|
||||
return ret;
|
||||
if (new_version != old_version) {
|
||||
if (new_version > old_version) {
|
||||
/* Upgrade */
|
||||
s->qcow_version = new_version;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->qcow_version = old_version;
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = qcow2_downgrade(bs, new_version, status_cb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3162,9 +2989,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
|
||||
if (s->use_lazy_refcounts != lazy_refcounts) {
|
||||
if (lazy_refcounts) {
|
||||
if (new_version < 3) {
|
||||
error_report("Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)");
|
||||
if (s->qcow_version < 3) {
|
||||
fprintf(stderr, "Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
|
||||
@@ -3198,16 +3025,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
}
|
||||
}
|
||||
|
||||
/* Downgrade last (so unsupported features can be removed before) */
|
||||
if (new_version < old_version) {
|
||||
helper_cb_info.current_operation = QCOW2_DOWNGRADING;
|
||||
ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
|
||||
&helper_cb_info);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3328,7 +3145,6 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
||||
.bdrv_reopen_commit = qcow2_reopen_commit,
|
||||
.bdrv_reopen_abort = qcow2_reopen_abort,
|
||||
.bdrv_join_options = qcow2_join_options,
|
||||
.bdrv_create = qcow2_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
||||
@@ -3360,7 +3176,6 @@ BlockDriver bdrv_qcow2 = {
|
||||
|
||||
.bdrv_refresh_limits = qcow2_refresh_limits,
|
||||
.bdrv_invalidate_cache = qcow2_invalidate_cache,
|
||||
.bdrv_inactivate = qcow2_inactivate,
|
||||
|
||||
.create_opts = &qcow2_create_opts,
|
||||
.bdrv_check = qcow2_check,
|
||||
|
@@ -529,10 +529,6 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size);
|
||||
|
||||
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque, Error **errp);
|
||||
|
||||
/* qcow2-cluster.c functions */
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
bool exact_size);
|
||||
@@ -557,8 +553,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
|
||||
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque);
|
||||
BlockDriverAmendStatusCB *status_cb);
|
||||
|
||||
/* qcow2-snapshot.c functions */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
||||
|
@@ -11,7 +11,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qed.h"
|
||||
|
||||
typedef struct {
|
||||
|
@@ -12,7 +12,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qed.h"
|
||||
|
||||
/**
|
||||
|
@@ -11,7 +11,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qed.h"
|
||||
|
||||
void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque)
|
||||
|
@@ -50,7 +50,6 @@
|
||||
* table will be deleted in favor of the existing cache entry.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "qed.h"
|
||||
|
||||
|
@@ -12,7 +12,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
|
||||
#include "qed.h"
|
||||
|
44
block/qed.c
44
block/qed.c
@@ -12,13 +12,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "trace.h"
|
||||
#include "qed.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "migration/migration.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
static const AIOCBInfo qed_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEDAIOCB),
|
||||
@@ -479,7 +477,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* feature is no longer valid.
|
||||
*/
|
||||
if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 &&
|
||||
!bdrv_is_read_only(bs->file->bs) && !(flags & BDRV_O_INACTIVE)) {
|
||||
!bdrv_is_read_only(bs->file->bs) && !(flags & BDRV_O_INCOMING)) {
|
||||
s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK;
|
||||
|
||||
ret = qed_write_header_sync(s);
|
||||
@@ -507,7 +505,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* aid data recovery from an otherwise inconsistent image.
|
||||
*/
|
||||
if (!bdrv_is_read_only(bs->file->bs) &&
|
||||
!(flags & BDRV_O_INACTIVE)) {
|
||||
!(flags & BDRV_O_INCOMING)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
ret = qed_check(s, &result, true);
|
||||
@@ -581,7 +579,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
size_t l1_size = header.cluster_size * header.table_size;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
@@ -589,18 +587,17 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* File must start empty and grow, check truncate is supported */
|
||||
ret = blk_truncate(blk, 0);
|
||||
ret = bdrv_truncate(bs, 0);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -616,18 +613,18 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
}
|
||||
|
||||
qed_header_cpu_to_le(&header, &le_header);
|
||||
ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header));
|
||||
ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header));
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = blk_pwrite(blk, sizeof(le_header), backing_file,
|
||||
header.backing_filename_size);
|
||||
ret = bdrv_pwrite(bs, sizeof(le_header), backing_file,
|
||||
header.backing_filename_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
l1_table = g_malloc0(l1_size);
|
||||
ret = blk_pwrite(blk, header.l1_table_offset, l1_table, l1_size);
|
||||
ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -635,7 +632,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
ret = 0; /* success */
|
||||
out:
|
||||
g_free(l1_table);
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -695,7 +692,6 @@ typedef struct {
|
||||
uint64_t pos;
|
||||
int64_t status;
|
||||
int *pnum;
|
||||
BlockDriverState **file;
|
||||
} QEDIsAllocatedCB;
|
||||
|
||||
static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len)
|
||||
@@ -707,7 +703,6 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l
|
||||
case QED_CLUSTER_FOUND:
|
||||
offset |= qed_offset_into_cluster(s, cb->pos);
|
||||
cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
|
||||
*cb->file = cb->bs->file->bs;
|
||||
break;
|
||||
case QED_CLUSTER_ZERO:
|
||||
cb->status = BDRV_BLOCK_ZERO;
|
||||
@@ -729,8 +724,7 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l
|
||||
|
||||
static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE;
|
||||
@@ -739,7 +733,6 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
|
||||
.pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE,
|
||||
.status = BDRV_BLOCK_OFFSET_MASK,
|
||||
.pnum = pnum,
|
||||
.file = file,
|
||||
};
|
||||
QEDRequest request = { .l2_table = NULL };
|
||||
|
||||
@@ -1618,8 +1611,9 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
memset(s, 0, sizeof(BDRVQEDState));
|
||||
ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qed layer: ");
|
||||
error_setg(errp, "Could not reopen qed layer: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
||||
|
@@ -13,7 +13,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
@@ -215,16 +214,14 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
return acb;
|
||||
}
|
||||
|
||||
static void quorum_report_bad(QuorumOpType type, uint64_t sector_num,
|
||||
int nb_sectors, char *node_name, int ret)
|
||||
static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
|
||||
{
|
||||
const char *msg = NULL;
|
||||
if (ret < 0) {
|
||||
msg = strerror(-ret);
|
||||
}
|
||||
|
||||
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name,
|
||||
sector_num, nb_sectors, &error_abort);
|
||||
qapi_event_send_quorum_report_bad(!!msg, msg, node_name,
|
||||
acb->sector_num, acb->nb_sectors, &error_abort);
|
||||
}
|
||||
|
||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
@@ -284,13 +281,11 @@ static void quorum_aio_cb(void *opaque, int ret)
|
||||
QuorumChildRequest *sacb = opaque;
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
QuorumOpType type;
|
||||
bool rewrite = false;
|
||||
|
||||
if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
|
||||
/* We try to read next child in FIFO order if we fail to read */
|
||||
if (ret < 0 && (acb->child_iter + 1) < s->num_children) {
|
||||
acb->child_iter++;
|
||||
if (ret < 0 && ++acb->child_iter < s->num_children) {
|
||||
read_fifo_child(acb);
|
||||
return;
|
||||
}
|
||||
@@ -303,14 +298,12 @@ static void quorum_aio_cb(void *opaque, int ret)
|
||||
return;
|
||||
}
|
||||
|
||||
type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE;
|
||||
sacb->ret = ret;
|
||||
acb->count++;
|
||||
if (ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
quorum_report_bad(type, acb->sector_num, acb->nb_sectors,
|
||||
sacb->aiocb->bs->node_name, ret);
|
||||
quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
|
||||
}
|
||||
assert(acb->count <= s->num_children);
|
||||
assert(acb->success_count <= s->num_children);
|
||||
@@ -343,9 +336,7 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
quorum_report_bad(QUORUM_OP_TYPE_READ, acb->sector_num,
|
||||
acb->nb_sectors,
|
||||
s->children[item->index]->bs->node_name, 0);
|
||||
quorum_report_bad(acb, s->children[item->index]->bs->node_name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -769,30 +760,19 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
||||
QuorumVoteValue result_value;
|
||||
int i;
|
||||
int result = 0;
|
||||
int success_count = 0;
|
||||
|
||||
QLIST_INIT(&error_votes.vote_list);
|
||||
error_votes.compare = quorum_64bits_compare;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
result = bdrv_co_flush(s->children[i]->bs);
|
||||
if (result) {
|
||||
quorum_report_bad(QUORUM_OP_TYPE_FLUSH, 0,
|
||||
bdrv_nb_sectors(s->children[i]->bs),
|
||||
s->children[i]->bs->node_name, result);
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
} else {
|
||||
success_count++;
|
||||
}
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
}
|
||||
|
||||
if (success_count >= s->threshold) {
|
||||
result = 0;
|
||||
} else {
|
||||
winner = quorum_get_vote_winner(&error_votes);
|
||||
result = winner->value.l;
|
||||
}
|
||||
winner = quorum_get_vote_winner(&error_votes);
|
||||
result = winner->value.l;
|
||||
|
||||
quorum_free_vote_list(&error_votes);
|
||||
|
||||
return result;
|
||||
@@ -869,7 +849,7 @@ static int parse_read_pattern(const char *opt)
|
||||
return QUORUM_READ_PATTERN_QUORUM;
|
||||
}
|
||||
|
||||
for (i = 0; i < QUORUM_READ_PATTERN__MAX; i++) {
|
||||
for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) {
|
||||
if (!strcmp(opt, QuorumReadPattern_lookup[i])) {
|
||||
return i;
|
||||
}
|
||||
@@ -1019,7 +999,7 @@ static void quorum_attach_aio_context(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
static void quorum_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QDict *opts;
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/timer.h"
|
||||
@@ -52,6 +51,8 @@
|
||||
#include <sys/dkio.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <linux/cdrom.h>
|
||||
@@ -499,17 +500,21 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
goto fail;
|
||||
}
|
||||
if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) {
|
||||
error_setg(errp, "aio=native was specified, but it requires "
|
||||
"cache.direct=on, which was not specified.");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
error_printf("WARNING: aio=native was specified for '%s', but "
|
||||
"it requires cache.direct=on, which was not "
|
||||
"specified. Falling back to aio=threads.\n"
|
||||
" This will become an error condition in "
|
||||
"future QEMU versions.\n",
|
||||
bs->filename);
|
||||
}
|
||||
#else
|
||||
if (bdrv_flags & BDRV_O_NATIVE_AIO) {
|
||||
error_setg(errp, "aio=native was specified, but is not supported "
|
||||
"in this build.");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
error_printf("WARNING: aio=native was specified for '%s', but "
|
||||
"is not supported in this build. Falling back to "
|
||||
"aio=threads.\n"
|
||||
" This will become an error condition in "
|
||||
"future QEMU versions.\n",
|
||||
bs->filename);
|
||||
}
|
||||
#endif /* !defined(CONFIG_LINUX_AIO) */
|
||||
|
||||
@@ -1624,7 +1629,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
|
||||
PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
|
||||
&local_err);
|
||||
g_free(buf);
|
||||
if (local_err) {
|
||||
@@ -1818,8 +1823,7 @@ static int find_allocation(BlockDriverState *bs, off_t start,
|
||||
*/
|
||||
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
off_t start, data = 0, hole = 0;
|
||||
int64_t total_size;
|
||||
@@ -1861,7 +1865,6 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
*pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
|
||||
ret = BDRV_BLOCK_ZERO;
|
||||
}
|
||||
*file = bs;
|
||||
return ret | BDRV_BLOCK_OFFSET_VALID | start;
|
||||
}
|
||||
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "block/block_int.h"
|
||||
|
@@ -26,7 +26,6 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
@@ -115,11 +114,9 @@ fail:
|
||||
|
||||
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
*pnum = nb_sectors;
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
49
block/rbd.c
49
block/rbd.c
@@ -11,12 +11,11 @@
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "block/block_int.h"
|
||||
#include "crypto/secret.h"
|
||||
|
||||
#include <rbd/librbd.h>
|
||||
|
||||
@@ -229,27 +228,6 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
|
||||
Error **errp)
|
||||
{
|
||||
if (secretid == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
|
||||
errp);
|
||||
if (!secret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rados_conf_set(cluster, "key", secret);
|
||||
g_free(secret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
|
||||
bool only_read_conf_file,
|
||||
Error **errp)
|
||||
@@ -321,13 +299,10 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
char conf[RBD_MAX_CONF_SIZE];
|
||||
char clientname_buf[RBD_MAX_CONF_SIZE];
|
||||
char *clientname;
|
||||
const char *secretid;
|
||||
rados_t cluster;
|
||||
rados_ioctx_t io_ctx;
|
||||
int ret;
|
||||
|
||||
secretid = qemu_opt_get(opts, "password-secret");
|
||||
|
||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||
snap_buf, sizeof(snap_buf),
|
||||
name, sizeof(name),
|
||||
@@ -375,11 +350,6 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
|
||||
rados_shutdown(cluster);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rados_connect(cluster) < 0) {
|
||||
error_setg(errp, "error connecting");
|
||||
rados_shutdown(cluster);
|
||||
@@ -453,11 +423,6 @@ static QemuOptsList runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Specification of the rbd image",
|
||||
},
|
||||
{
|
||||
.name = "password-secret",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of secret providing the password",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -471,7 +436,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
char conf[RBD_MAX_CONF_SIZE];
|
||||
char clientname_buf[RBD_MAX_CONF_SIZE];
|
||||
char *clientname;
|
||||
const char *secretid;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
@@ -486,7 +450,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
filename = qemu_opt_get(opts, "filename");
|
||||
secretid = qemu_opt_get(opts, "password-secret");
|
||||
|
||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||
snap_buf, sizeof(snap_buf),
|
||||
@@ -525,11 +488,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
|
||||
r = -EIO;
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fallback to more conservative semantics if setting cache
|
||||
* options fails. Ignore errors from setting rbd_cache because the
|
||||
@@ -961,11 +919,6 @@ static QemuOptsList qemu_rbd_create_opts = {
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "RBD object size"
|
||||
},
|
||||
{
|
||||
.name = "password-secret",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of secret providing the password",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
185
block/sheepdog.c
185
block/sheepdog.c
@@ -12,13 +12,11 @@
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
#define SD_PROTO_VER 0x01
|
||||
@@ -285,12 +283,6 @@ static inline bool is_snapshot(struct SheepdogInode *inode)
|
||||
return !!inode->snap_ctime;
|
||||
}
|
||||
|
||||
static inline size_t count_data_objs(const struct SheepdogInode *inode)
|
||||
{
|
||||
return DIV_ROUND_UP(inode->vdi_size,
|
||||
(1UL << inode->block_size_shift));
|
||||
}
|
||||
|
||||
#undef DPRINTF
|
||||
#ifdef DEBUG_SDOG
|
||||
#define DPRINTF(fmt, args...) \
|
||||
@@ -616,13 +608,14 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
|
||||
if (ret != sizeof(*hdr)) {
|
||||
error_report("failed to send a req, %s", strerror(errno));
|
||||
return -errno;
|
||||
ret = -socket_error();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = qemu_co_send(sockfd, data, *wlen);
|
||||
if (ret != *wlen) {
|
||||
ret = -socket_error();
|
||||
error_report("failed to send a req, %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1637,7 +1630,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
|
||||
|
||||
static int sd_prealloc(const char *filename, Error **errp)
|
||||
{
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
BDRVSheepdogState *base = NULL;
|
||||
unsigned long buf_size;
|
||||
uint32_t idx, max_idx;
|
||||
@@ -1646,23 +1639,19 @@ static int sd_prealloc(const char *filename, Error **errp)
|
||||
void *buf = NULL;
|
||||
int ret;
|
||||
|
||||
blk = blk_new_open("image-prealloc", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
errp);
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto out_with_err_set;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
vdi_size = blk_getlength(blk);
|
||||
vdi_size = bdrv_getlength(bs);
|
||||
if (vdi_size < 0) {
|
||||
ret = vdi_size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = blk_bs(blk)->opaque;
|
||||
base = bs->opaque;
|
||||
object_size = (UINT32_C(1) << base->inode.block_size_shift);
|
||||
buf_size = MIN(object_size, SD_DATA_OBJ_SIZE);
|
||||
buf = g_malloc0(buf_size);
|
||||
@@ -1674,24 +1663,23 @@ static int sd_prealloc(const char *filename, Error **errp)
|
||||
* The created image can be a cloned image, so we need to read
|
||||
* a data from the source image.
|
||||
*/
|
||||
ret = blk_pread(blk, idx * buf_size, buf, buf_size);
|
||||
ret = bdrv_pread(bs, idx * buf_size, buf, buf_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = blk_pwrite(blk, idx * buf_size, buf, buf_size);
|
||||
ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||
}
|
||||
out_with_err_set:
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
g_free(buf);
|
||||
|
||||
@@ -1831,7 +1819,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
}
|
||||
|
||||
if (backing_file) {
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BDRVSheepdogState *base;
|
||||
BlockDriver *drv;
|
||||
|
||||
@@ -1843,23 +1831,22 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = blk_new_open("backing", backing_file, NULL, NULL,
|
||||
BDRV_O_PROTOCOL | BDRV_O_CACHE_WB, errp);
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = blk_bs(blk)->opaque;
|
||||
base = bs->opaque;
|
||||
|
||||
if (!is_snapshot(&base->inode)) {
|
||||
error_setg(errp, "cannot clone from a non snapshot vdi");
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
s->inode.vdi_id = base->inode.vdi_id;
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
|
||||
s->aio_context = qemu_get_aio_context();
|
||||
@@ -1874,7 +1861,8 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report_err(local_err);
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
@@ -2418,8 +2406,9 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
||||
ret = do_sd_create(s, &new_vid, 1, &local_err);
|
||||
if (ret < 0) {
|
||||
error_reportf_err(local_err,
|
||||
"failed to create inode for snapshot: ");
|
||||
error_report("failed to create inode for snapshot: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -2490,128 +2479,13 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define NR_BATCHED_DISCARD 128
|
||||
|
||||
static bool remove_objects(BDRVSheepdogState *s)
|
||||
{
|
||||
int fd, i = 0, nr_objs = 0;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
bool result = true;
|
||||
SheepdogInode *inode = &s->inode;
|
||||
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report_err(local_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
nr_objs = count_data_objs(inode);
|
||||
while (i < nr_objs) {
|
||||
int start_idx, nr_filled_idx;
|
||||
|
||||
while (i < nr_objs && !inode->data_vdi_id[i]) {
|
||||
i++;
|
||||
}
|
||||
start_idx = i;
|
||||
|
||||
nr_filled_idx = 0;
|
||||
while (i < nr_objs && nr_filled_idx < NR_BATCHED_DISCARD) {
|
||||
if (inode->data_vdi_id[i]) {
|
||||
inode->data_vdi_id[i] = 0;
|
||||
nr_filled_idx++;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
ret = write_object(fd, s->aio_context,
|
||||
(char *)&inode->data_vdi_id[start_idx],
|
||||
vid_to_vdi_oid(s->inode.vdi_id), inode->nr_copies,
|
||||
(i - start_idx) * sizeof(uint32_t),
|
||||
offsetof(struct SheepdogInode,
|
||||
data_vdi_id[start_idx]),
|
||||
false, s->cache_flags);
|
||||
if (ret < 0) {
|
||||
error_report("failed to discard snapshot inode.");
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
closesocket(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sd_snapshot_delete(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
uint32_t snap_id = 0;
|
||||
char snap_tag[SD_MAX_VDI_TAG_LEN];
|
||||
Error *local_err = NULL;
|
||||
int fd, ret;
|
||||
char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
unsigned int wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN, rlen = 0;
|
||||
uint32_t vid;
|
||||
SheepdogVdiReq hdr = {
|
||||
.opcode = SD_OP_DEL_VDI,
|
||||
.data_length = wlen,
|
||||
.flags = SD_FLAG_CMD_WRITE,
|
||||
};
|
||||
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||
|
||||
if (!remove_objects(s)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(snap_tag, 0, sizeof(snap_tag));
|
||||
pstrcpy(buf, SD_MAX_VDI_LEN, s->name);
|
||||
if (qemu_strtoul(snapshot_id, NULL, 10, (unsigned long *)&snap_id)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (snap_id) {
|
||||
hdr.snapid = snap_id;
|
||||
} else {
|
||||
pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id);
|
||||
pstrcpy(buf + SD_MAX_VDI_LEN, SD_MAX_VDI_TAG_LEN, snap_tag);
|
||||
}
|
||||
|
||||
ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true,
|
||||
&local_err);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report_err(local_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
|
||||
buf, &wlen, &rlen);
|
||||
closesocket(fd);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (rsp->result) {
|
||||
case SD_RES_NO_VDI:
|
||||
error_report("%s was already deleted", s->name);
|
||||
case SD_RES_SUCCESS:
|
||||
break;
|
||||
default:
|
||||
error_report("%s, %s", sd_strerror(rsp->result), s->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
/* FIXME: Delete specified snapshot id. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
@@ -2835,7 +2709,7 @@ retry:
|
||||
|
||||
static coroutine_fn int64_t
|
||||
sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
int *pnum, BlockDriverState **file)
|
||||
int *pnum)
|
||||
{
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
SheepdogInode *inode = &s->inode;
|
||||
@@ -2866,9 +2740,6 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
if (*pnum > nb_sectors) {
|
||||
*pnum = nb_sectors;
|
||||
}
|
||||
if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
|
||||
*file = bs;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/snapshot.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -230,8 +229,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
int ret;
|
||||
|
||||
if (!drv) {
|
||||
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
|
||||
return -ENOMEDIUM;
|
||||
@@ -242,21 +239,18 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* drain all pending i/o before deleting snapshot */
|
||||
bdrv_drained_begin(bs);
|
||||
bdrv_drain(bs);
|
||||
|
||||
if (drv->bdrv_snapshot_delete) {
|
||||
ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
||||
} else if (bs->file) {
|
||||
ret = bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
|
||||
} else {
|
||||
error_setg(errp, "Block format '%s' used by device '%s' "
|
||||
"does not support internal snapshot deletion",
|
||||
drv->format_name, bdrv_get_device_name(bs));
|
||||
ret = -ENOTSUP;
|
||||
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
||||
}
|
||||
|
||||
bdrv_drained_end(bs);
|
||||
return ret;
|
||||
if (bs->file) {
|
||||
return bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
|
||||
}
|
||||
error_setg(errp, "Block format '%s' used by device '%s' "
|
||||
"does not support internal snapshot deletion",
|
||||
drv->format_name, bdrv_get_device_name(bs));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
||||
|
@@ -22,7 +22,9 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <libssh2.h>
|
||||
#include <libssh2_sftp.h>
|
||||
|
@@ -11,7 +11,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
|
@@ -22,7 +22,6 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/thread.h"
|
||||
|
26
block/vdi.c
26
block/vdi.c
@@ -49,10 +49,8 @@
|
||||
* so this seems to be reasonable.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/migration.h"
|
||||
#include "qemu/coroutine.h"
|
||||
@@ -528,7 +526,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state,
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
/* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
|
||||
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
|
||||
@@ -552,7 +550,6 @@ static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs,
|
||||
offset = s->header.offset_data +
|
||||
(uint64_t)bmap_entry * s->block_size +
|
||||
sector_in_block * SECTOR_SIZE;
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
|
||||
}
|
||||
|
||||
@@ -734,7 +731,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
size_t bmap_size;
|
||||
int64_t offset = 0;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
uint32_t *bmap = NULL;
|
||||
|
||||
logout("\n");
|
||||
@@ -767,18 +764,13 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* We need enough blocks to store the given disk size,
|
||||
so always round up. */
|
||||
blocks = DIV_ROUND_UP(bytes, block_size);
|
||||
@@ -808,7 +800,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
vdi_header_print(&header);
|
||||
#endif
|
||||
vdi_header_to_le(&header);
|
||||
ret = blk_pwrite(blk, offset, &header, sizeof(header));
|
||||
ret = bdrv_pwrite_sync(bs, offset, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing header to %s", filename);
|
||||
goto exit;
|
||||
@@ -829,7 +821,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
bmap[i] = VDI_UNALLOCATED;
|
||||
}
|
||||
}
|
||||
ret = blk_pwrite(blk, offset, bmap, bmap_size);
|
||||
ret = bdrv_pwrite_sync(bs, offset, bmap, bmap_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing bmap to %s", filename);
|
||||
goto exit;
|
||||
@@ -838,7 +830,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
ret = blk_truncate(blk, offset + blocks * block_size);
|
||||
ret = bdrv_truncate(bs, offset + blocks * block_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to statically allocate %s", filename);
|
||||
goto exit;
|
||||
@@ -846,7 +838,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
g_free(bmap);
|
||||
return ret;
|
||||
}
|
||||
|
@@ -15,7 +15,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/vhdx.h"
|
||||
|
@@ -17,7 +17,6 @@
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -785,13 +784,12 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
|
||||
if (logs.valid) {
|
||||
if (bs->read_only) {
|
||||
ret = -EPERM;
|
||||
error_setg(errp,
|
||||
"VHDX image file '%s' opened read-only, but "
|
||||
"contains a log that needs to be replayed",
|
||||
bs->filename);
|
||||
error_append_hint(errp, "To replay the log, run:\n"
|
||||
"qemu-img check -r all '%s'\n",
|
||||
bs->filename);
|
||||
error_setg_errno(errp, EPERM,
|
||||
"VHDX image file '%s' opened read-only, but "
|
||||
"contains a log that needs to be replayed. To "
|
||||
"replay the log, execute:\n qemu-img check -r "
|
||||
"all '%s'",
|
||||
bs->filename, bs->filename);
|
||||
goto exit;
|
||||
}
|
||||
/* now flush the log */
|
||||
|
47
block/vhdx.c
47
block/vhdx.c
@@ -15,10 +15,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include "block/vhdx.h"
|
||||
@@ -265,10 +263,10 @@ static void vhdx_region_unregister_all(BDRVVHDXState *s)
|
||||
|
||||
static void vhdx_set_shift_bits(BDRVVHDXState *s)
|
||||
{
|
||||
s->logical_sector_size_bits = ctz32(s->logical_sector_size);
|
||||
s->sectors_per_block_bits = ctz32(s->sectors_per_block);
|
||||
s->chunk_ratio_bits = ctz64(s->chunk_ratio);
|
||||
s->block_size_bits = ctz32(s->block_size);
|
||||
s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size);
|
||||
s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block);
|
||||
s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio);
|
||||
s->block_size_bits = 31 - clz32(s->block_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -858,8 +856,14 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s)
|
||||
{
|
||||
uint32_t data_blocks_cnt, bitmap_blocks_cnt;
|
||||
|
||||
data_blocks_cnt = DIV_ROUND_UP(s->virtual_disk_size, s->block_size);
|
||||
bitmap_blocks_cnt = DIV_ROUND_UP(data_blocks_cnt, s->chunk_ratio);
|
||||
data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits;
|
||||
if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) {
|
||||
data_blocks_cnt++;
|
||||
}
|
||||
bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits;
|
||||
if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) {
|
||||
bitmap_blocks_cnt++;
|
||||
}
|
||||
|
||||
if (s->parent_entries) {
|
||||
s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
|
||||
@@ -1773,7 +1777,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
gunichar2 *creator = NULL;
|
||||
glong creator_items;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
char *type = NULL;
|
||||
VHDXImageType image_type;
|
||||
Error *local_err = NULL;
|
||||
@@ -1838,17 +1842,14 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Create (A) */
|
||||
|
||||
/* The creator field is optional, but may be useful for
|
||||
@@ -1856,13 +1857,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
|
||||
&creator_items, NULL);
|
||||
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
|
||||
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
if (creator) {
|
||||
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||
creator, creator_items * sizeof(gunichar2));
|
||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||
creator, creator_items * sizeof(gunichar2));
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
@@ -1870,13 +1871,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
|
||||
/* Creates (B),(C) */
|
||||
ret = vhdx_create_new_headers(blk_bs(blk), image_size, log_size);
|
||||
ret = vhdx_create_new_headers(bs, image_size, log_size);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
|
||||
/* Creates (D),(E),(G) explicitly. (F) created as by-product */
|
||||
ret = vhdx_create_new_region_table(blk_bs(blk), image_size, block_size, 512,
|
||||
ret = vhdx_create_new_region_table(bs, image_size, block_size, 512,
|
||||
log_size, use_zero_blocks, image_type,
|
||||
&metadata_offset);
|
||||
if (ret < 0) {
|
||||
@@ -1884,7 +1885,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
/* Creates (H) */
|
||||
ret = vhdx_create_new_metadata(blk_bs(blk), image_size, block_size, 512,
|
||||
ret = vhdx_create_new_metadata(bs, image_size, block_size, 512,
|
||||
metadata_offset, image_type);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
@@ -1892,7 +1893,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
|
||||
delete_and_exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
exit:
|
||||
g_free(type);
|
||||
g_free(creator);
|
||||
|
184
block/vmdk.c
184
block/vmdk.c
@@ -23,10 +23,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
@@ -243,17 +241,15 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
|
||||
|
||||
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
{
|
||||
char *desc;
|
||||
char desc[DESC_SIZE];
|
||||
uint32_t cid = 0xffffffff;
|
||||
const char *p_name, *cid_str;
|
||||
size_t cid_str_size;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
desc = g_malloc0(DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
g_free(desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -272,45 +268,41 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
sscanf(p_name, "%" SCNx32, &cid);
|
||||
}
|
||||
|
||||
g_free(desc);
|
||||
return cid;
|
||||
}
|
||||
|
||||
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
{
|
||||
char *desc, *tmp_desc;
|
||||
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
|
||||
char *p_name, *tmp_str;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
desc = g_malloc0(DESC_SIZE);
|
||||
tmp_desc = g_malloc0(DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
desc[DESC_SIZE - 1] = '\0';
|
||||
tmp_str = strstr(desc, "parentCID");
|
||||
if (tmp_str == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pstrcpy(tmp_desc, DESC_SIZE, tmp_str);
|
||||
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
|
||||
p_name = strstr(desc, "CID");
|
||||
if (p_name != NULL) {
|
||||
p_name += sizeof("CID");
|
||||
snprintf(p_name, DESC_SIZE - (p_name - desc), "%" PRIx32 "\n", cid);
|
||||
pstrcat(desc, DESC_SIZE, tmp_desc);
|
||||
snprintf(p_name, sizeof(desc) - (p_name - desc), "%" PRIx32 "\n", cid);
|
||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(desc);
|
||||
g_free(tmp_desc);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
@@ -344,16 +336,15 @@ static int vmdk_reopen_prepare(BDRVReopenState *state,
|
||||
static int vmdk_parent_open(BlockDriverState *bs)
|
||||
{
|
||||
char *p_name;
|
||||
char *desc;
|
||||
char desc[DESC_SIZE + 1];
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
desc = g_malloc0(DESC_SIZE + 1);
|
||||
desc[DESC_SIZE] = '\0';
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
p_name = strstr(desc, "parentFileNameHint");
|
||||
if (p_name != NULL) {
|
||||
@@ -362,20 +353,16 @@ static int vmdk_parent_open(BlockDriverState *bs)
|
||||
p_name += sizeof("parentFileNameHint") + 1;
|
||||
end_name = strchr(p_name, '\"');
|
||||
if (end_name == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(desc);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create and append extent to the extent array. Return the added VmdkExtent
|
||||
@@ -777,17 +764,6 @@ static int vmdk_open_sparse(BlockDriverState *bs, BdrvChild *file, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
static const char *next_line(const char *s)
|
||||
{
|
||||
while (*s) {
|
||||
if (*s == '\n') {
|
||||
return s + 1;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
const char *desc_file_path, QDict *options,
|
||||
Error **errp)
|
||||
@@ -797,7 +773,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
char access[11];
|
||||
char type[11];
|
||||
char fname[512];
|
||||
const char *p, *np;
|
||||
const char *p = desc;
|
||||
int64_t sectors = 0;
|
||||
int64_t flat_offset;
|
||||
char *extent_path;
|
||||
@@ -807,7 +783,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
char extent_opt_prefix[32];
|
||||
Error *local_err = NULL;
|
||||
|
||||
for (p = desc; *p; p = next_line(p)) {
|
||||
while (*p) {
|
||||
/* parse extent line in one of below formats:
|
||||
*
|
||||
* RW [size in sectors] FLAT "file-name.vmdk" OFFSET
|
||||
@@ -819,26 +795,29 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
|
||||
access, §ors, type, fname, &flat_offset);
|
||||
if (matches < 4 || strcmp(access, "RW")) {
|
||||
continue;
|
||||
goto next_line;
|
||||
} else if (!strcmp(type, "FLAT")) {
|
||||
if (matches != 5 || flat_offset < 0) {
|
||||
goto invalid;
|
||||
error_setg(errp, "Invalid extent lines: \n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(type, "VMFS")) {
|
||||
if (matches == 4) {
|
||||
flat_offset = 0;
|
||||
} else {
|
||||
goto invalid;
|
||||
error_setg(errp, "Invalid extent lines:\n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (matches != 4) {
|
||||
goto invalid;
|
||||
error_setg(errp, "Invalid extent lines:\n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sectors <= 0 ||
|
||||
(strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
|
||||
strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) ||
|
||||
(strcmp(access, "RW"))) {
|
||||
continue;
|
||||
goto next_line;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(fname) && !path_has_protocol(fname) &&
|
||||
@@ -895,17 +874,17 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
extent->type = g_strdup(type);
|
||||
next_line:
|
||||
/* move to next line */
|
||||
while (*p) {
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
np = next_line(p);
|
||||
assert(np != p);
|
||||
if (np[-1] == '\n') {
|
||||
np--;
|
||||
}
|
||||
error_setg(errp, "Invalid extent line: %.*s", (int)(np - p), p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
@@ -1273,7 +1252,7 @@ static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int64_t index_in_cluster, n, ret;
|
||||
@@ -1290,7 +1269,6 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
0, 0);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
switch (ret) {
|
||||
case VMDK_ERROR:
|
||||
ret = -EIO;
|
||||
@@ -1303,15 +1281,14 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
break;
|
||||
case VMDK_OK:
|
||||
ret = BDRV_BLOCK_DATA;
|
||||
if (!extent->compressed) {
|
||||
ret |= BDRV_BLOCK_OFFSET_VALID;
|
||||
ret |= (offset + (index_in_cluster << BDRV_SECTOR_BITS))
|
||||
& BDRV_BLOCK_OFFSET_MASK;
|
||||
if (extent->file == bs->file && !extent->compressed) {
|
||||
ret |= BDRV_BLOCK_OFFSET_VALID | offset;
|
||||
}
|
||||
*file = extent->file->bs;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@@ -1521,8 +1498,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
if (sector_num > bs->total_sectors) {
|
||||
error_report("Wrong offset: sector_num=0x%" PRIx64
|
||||
" total_sectors=0x%" PRIx64,
|
||||
sector_num, bs->total_sectors);
|
||||
" total_sectors=0x%" PRIx64 "\n",
|
||||
sector_num, bs->total_sectors);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -1651,7 +1628,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
int ret, i;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
VMDK4Header header;
|
||||
Error *local_err = NULL;
|
||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||
@@ -1664,19 +1641,16 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open("extent", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
assert(bs == NULL);
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
if (flat) {
|
||||
ret = blk_truncate(blk, filesize);
|
||||
ret = bdrv_truncate(bs, filesize);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
}
|
||||
@@ -1731,18 +1705,18 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
header.check_bytes[3] = 0xa;
|
||||
|
||||
/* write all the data */
|
||||
ret = blk_pwrite(blk, 0, &magic, sizeof(magic));
|
||||
ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
}
|
||||
ret = blk_pwrite(blk, sizeof(magic), &header, sizeof(header));
|
||||
ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9);
|
||||
ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
goto exit;
|
||||
@@ -1755,8 +1729,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
i < gt_count; i++, tmp += gt_size) {
|
||||
gd_buf[i] = cpu_to_le32(tmp);
|
||||
}
|
||||
ret = blk_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
@@ -1767,8 +1741,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
i < gt_count; i++, tmp += gt_size) {
|
||||
gd_buf[i] = cpu_to_le32(tmp);
|
||||
}
|
||||
ret = blk_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
@@ -1776,8 +1750,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
g_free(gd_buf);
|
||||
return ret;
|
||||
@@ -1826,7 +1800,7 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
|
||||
static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
int idx = 0;
|
||||
BlockBackend *new_blk = NULL;
|
||||
BlockDriverState *new_bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
char *desc = NULL;
|
||||
int64_t total_size = 0, filesize;
|
||||
@@ -1937,7 +1911,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
if (backing_file) {
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs = NULL;
|
||||
char *full_backing = g_new0(char, PATH_MAX);
|
||||
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
||||
full_backing, PATH_MAX,
|
||||
@@ -1948,21 +1922,18 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ret = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open("backing", full_backing, NULL, NULL,
|
||||
BDRV_O_NO_BACKING | BDRV_O_CACHE_WB, errp);
|
||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, errp);
|
||||
g_free(full_backing);
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
if (ret != 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) {
|
||||
blk_unref(blk);
|
||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
||||
bdrv_unref(bs);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
parent_cid = vmdk_read_cid(blk_bs(blk), 0);
|
||||
blk_unref(blk);
|
||||
parent_cid = vmdk_read_cid(bs, 0);
|
||||
bdrv_unref(bs);
|
||||
snprintf(parent_desc_line, BUF_SIZE,
|
||||
"parentFileNameHint=\"%s\"", backing_file);
|
||||
}
|
||||
@@ -2020,19 +1991,14 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
new_blk = blk_new_open("descriptor", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (new_blk == NULL) {
|
||||
assert(new_bs == NULL);
|
||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(new_blk, true);
|
||||
|
||||
ret = blk_pwrite(new_blk, desc_offset, desc, desc_len);
|
||||
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write description");
|
||||
goto exit;
|
||||
@@ -2040,14 +2006,14 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
||||
* for description file */
|
||||
if (desc_offset == 0) {
|
||||
ret = blk_truncate(new_blk, desc_len);
|
||||
ret = bdrv_truncate(new_bs, desc_len);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (new_blk) {
|
||||
blk_unref(new_blk);
|
||||
if (new_bs) {
|
||||
bdrv_unref(new_bs);
|
||||
}
|
||||
g_free(adapter_type);
|
||||
g_free(backing_file);
|
||||
|
170
block/vpc.c
170
block/vpc.c
@@ -22,10 +22,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/migration.h"
|
||||
#if defined(CONFIG_UUID)
|
||||
@@ -47,14 +45,8 @@ enum vhd_type {
|
||||
// Seconds since Jan 1, 2000 0:00:00 (UTC)
|
||||
#define VHD_TIMESTAMP_BASE 946684800
|
||||
|
||||
#define VHD_CHS_MAX_C 65535LL
|
||||
#define VHD_CHS_MAX_H 16
|
||||
#define VHD_CHS_MAX_S 255
|
||||
|
||||
#define VHD_MAX_SECTORS (65535LL * 255 * 255)
|
||||
#define VHD_MAX_GEOMETRY (VHD_CHS_MAX_C * VHD_CHS_MAX_H * VHD_CHS_MAX_S)
|
||||
|
||||
#define VPC_OPT_FORCE_SIZE "force_size"
|
||||
#define VHD_MAX_GEOMETRY (65535LL * 16 * 255)
|
||||
|
||||
// always big-endian
|
||||
typedef struct vhd_footer {
|
||||
@@ -135,8 +127,6 @@ typedef struct BDRVVPCState {
|
||||
|
||||
uint32_t block_size;
|
||||
uint32_t bitmap_size;
|
||||
bool force_use_chs;
|
||||
bool force_use_sz;
|
||||
|
||||
#ifdef CACHE
|
||||
uint8_t *pageentry_u8;
|
||||
@@ -149,22 +139,6 @@ typedef struct BDRVVPCState {
|
||||
Error *migration_blocker;
|
||||
} BDRVVPCState;
|
||||
|
||||
#define VPC_OPT_SIZE_CALC "force_size_calc"
|
||||
static QemuOptsList vpc_runtime_opts = {
|
||||
.name = "vpc-runtime-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vpc_runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = VPC_OPT_SIZE_CALC,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Force disk size calculation to use either CHS geometry, "
|
||||
"or use the disk current_size specified in the VHD footer. "
|
||||
"{chs, current_size}"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
@@ -184,25 +158,6 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
const char *size_calc;
|
||||
|
||||
size_calc = qemu_opt_get(opts, VPC_OPT_SIZE_CALC);
|
||||
|
||||
if (!size_calc) {
|
||||
/* no override, use autodetect only */
|
||||
} else if (!strcmp(size_calc, "current_size")) {
|
||||
s->force_use_sz = true;
|
||||
} else if (!strcmp(size_calc, "chs")) {
|
||||
s->force_use_chs = true;
|
||||
} else {
|
||||
error_setg(errp, "Invalid size calculation mode: '%s'", size_calc);
|
||||
}
|
||||
}
|
||||
|
||||
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -210,9 +165,6 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int i;
|
||||
VHDFooter *footer;
|
||||
VHDDynDiskHeader *dyndisk_header;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
bool use_chs;
|
||||
uint8_t buf[HEADER_SIZE];
|
||||
uint32_t checksum;
|
||||
uint64_t computed_size;
|
||||
@@ -220,21 +172,6 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int disk_type = VHD_DYNAMIC;
|
||||
int ret;
|
||||
|
||||
opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vpc_parse_options(bs, opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -280,36 +217,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
bs->total_sectors = (int64_t)
|
||||
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
||||
|
||||
/* Microsoft Virtual PC and Microsoft Hyper-V produce and read
|
||||
* VHD image sizes differently. VPC will rely on CHS geometry,
|
||||
* while Hyper-V and disk2vhd use the size specified in the footer.
|
||||
*
|
||||
* We use a couple of approaches to try and determine the correct method:
|
||||
* look at the Creator App field, and look for images that have CHS
|
||||
* geometry that is the maximum value.
|
||||
*
|
||||
* If the CHS geometry is the maximum CHS geometry, then we assume that
|
||||
* the size is the footer->current_size to avoid truncation. Otherwise,
|
||||
* we follow the table based on footer->creator_app:
|
||||
*
|
||||
* Known creator apps:
|
||||
* 'vpc ' : CHS Virtual PC (uses disk geometry)
|
||||
* 'qemu' : CHS QEMU (uses disk geometry)
|
||||
* 'qem2' : current_size QEMU (uses current_size)
|
||||
* 'win ' : current_size Hyper-V
|
||||
* 'd2v ' : current_size Disk2vhd
|
||||
*
|
||||
* The user can override the table values via drive options, however
|
||||
* even with an override we will still use current_size for images
|
||||
* that have CHS geometry of the maximum size.
|
||||
*/
|
||||
use_chs = (!!strncmp(footer->creator_app, "win ", 4) &&
|
||||
!!strncmp(footer->creator_app, "qem2", 4) &&
|
||||
!!strncmp(footer->creator_app, "d2v ", 4)) || s->force_use_chs;
|
||||
|
||||
if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) {
|
||||
/* Images that have exactly the maximum geometry are probably bigger and
|
||||
* would be truncated if we adhered to the geometry for them. Rely on
|
||||
* footer->current_size for them. */
|
||||
if (bs->total_sectors == VHD_MAX_GEOMETRY) {
|
||||
bs->total_sectors = be64_to_cpu(footer->current_size) /
|
||||
BDRV_SECTOR_SIZE;
|
||||
BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
@@ -665,7 +578,7 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
VHDFooter *footer = (VHDFooter*) s->footer_buf;
|
||||
@@ -675,7 +588,6 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
*pnum = nb_sectors;
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
@@ -697,7 +609,6 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
|
||||
/* *pnum can't be greater than one block for allocated
|
||||
* sectors since there is always a bitmap in between. */
|
||||
if (allocated) {
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
||||
}
|
||||
if (nb_sectors == 0) {
|
||||
@@ -759,7 +670,7 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||
static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t total_sectors)
|
||||
{
|
||||
VHDDynDiskHeader *dyndisk_header =
|
||||
@@ -773,13 +684,13 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||
block_size = 0x200000;
|
||||
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
|
||||
|
||||
ret = blk_pwrite(blk, offset, buf, HEADER_SIZE);
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
|
||||
ret = blk_pwrite(blk, offset, buf, HEADER_SIZE);
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -789,7 +700,7 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||
|
||||
memset(buf, 0xFF, 512);
|
||||
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
|
||||
ret = blk_pwrite(blk, offset, buf, 512);
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, 512);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -816,7 +727,7 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||
// Write the header
|
||||
offset = 512;
|
||||
|
||||
ret = blk_pwrite(blk, offset, buf, 1024);
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, 1024);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -825,7 +736,7 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||
static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t total_size)
|
||||
{
|
||||
int ret;
|
||||
@@ -833,12 +744,12 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||
/* Add footer to total size */
|
||||
total_size += HEADER_SIZE;
|
||||
|
||||
ret = blk_truncate(blk, total_size);
|
||||
ret = bdrv_truncate(bs, total_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
||||
ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -859,9 +770,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
bool force_size;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
@@ -880,44 +790,30 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
disk_type = VHD_DYNAMIC;
|
||||
}
|
||||
|
||||
force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/*
|
||||
* Calculate matching total_size and geometry. Increase the number of
|
||||
* sectors requested until we get enough (or fail). This ensures that
|
||||
* qemu-img convert doesn't truncate images, but rather rounds up.
|
||||
*
|
||||
* If the image size can't be represented by a spec conformant CHS geometry,
|
||||
* If the image size can't be represented by a spec conform CHS geometry,
|
||||
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
||||
* the image size from the VHD footer to calculate total_sectors.
|
||||
*/
|
||||
if (force_size) {
|
||||
/* This will force the use of total_size for sector count, below */
|
||||
cyls = VHD_CHS_MAX_C;
|
||||
heads = VHD_CHS_MAX_H;
|
||||
secs_per_cyl = VHD_CHS_MAX_S;
|
||||
} else {
|
||||
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
||||
}
|
||||
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
||||
}
|
||||
|
||||
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
|
||||
@@ -936,11 +832,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
if (force_size) {
|
||||
memcpy(footer->creator_app, "qem2", 4);
|
||||
} else {
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
}
|
||||
/* TODO Check if "qemu" creator_app is ok for VPC */
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
memcpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = cpu_to_be32(0x02);
|
||||
@@ -970,13 +863,13 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
ret = create_dynamic_disk(blk, buf, total_sectors);
|
||||
ret = create_dynamic_disk(bs, buf, total_sectors);
|
||||
} else {
|
||||
ret = create_fixed_disk(blk, buf, total_size);
|
||||
ret = create_fixed_disk(bs, buf, total_size);
|
||||
}
|
||||
|
||||
out:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
g_free(disk_type_param);
|
||||
return ret;
|
||||
}
|
||||
@@ -1021,13 +914,6 @@ static QemuOptsList vpc_create_opts = {
|
||||
"Type of virtual hard disk format. Supported formats are "
|
||||
"{dynamic (default) | fixed} "
|
||||
},
|
||||
{
|
||||
.name = VPC_OPT_FORCE_SIZE,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Force disk size calculation to use the actual size "
|
||||
"specified, rather than using the nearest CHS-based "
|
||||
"calculation"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
@@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
@@ -2884,7 +2884,7 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file)
|
||||
int64_t sector_num, int nb_sectors, int* n)
|
||||
{
|
||||
BDRVVVFATState* s = bs->opaque;
|
||||
*n = s->sector_count - sector_num;
|
||||
|
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "block/block_int.h"
|
||||
|
@@ -10,7 +10,6 @@
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/write-threshold.h"
|
||||
|
163
blockdev-nbd.c
163
blockdev-nbd.c
@@ -9,7 +9,6 @@
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/block/block.h"
|
||||
@@ -18,128 +17,58 @@
|
||||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
#include "block/nbd.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
||||
typedef struct NBDServerData {
|
||||
QIOChannelSocket *listen_ioc;
|
||||
int watch;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
} NBDServerData;
|
||||
static int server_fd = -1;
|
||||
|
||||
static NBDServerData *nbd_server;
|
||||
|
||||
|
||||
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||
gpointer opaque)
|
||||
static void nbd_accept(void *opaque)
|
||||
{
|
||||
QIOChannelSocket *cioc;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
|
||||
if (!nbd_server) {
|
||||
return FALSE;
|
||||
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
|
||||
if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) {
|
||||
shutdown(fd, 2);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
|
||||
NULL);
|
||||
if (!cioc) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
nbd_client_new(NULL, cioc,
|
||||
nbd_server->tlscreds, NULL,
|
||||
nbd_client_put);
|
||||
object_unref(OBJECT(cioc));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void nbd_server_free(NBDServerData *server)
|
||||
void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
|
||||
{
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (server->watch != -1) {
|
||||
g_source_remove(server->watch);
|
||||
}
|
||||
object_unref(OBJECT(server->listen_ioc));
|
||||
if (server->tlscreds) {
|
||||
object_unref(OBJECT(server->tlscreds));
|
||||
}
|
||||
|
||||
g_free(server);
|
||||
}
|
||||
|
||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
{
|
||||
Object *obj;
|
||||
QCryptoTLSCreds *creds;
|
||||
|
||||
obj = object_resolve_path_component(
|
||||
object_get_objects_root(), id);
|
||||
if (!obj) {
|
||||
error_setg(errp, "No TLS credentials with id '%s'",
|
||||
id);
|
||||
return NULL;
|
||||
}
|
||||
creds = (QCryptoTLSCreds *)
|
||||
object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
|
||||
if (!creds) {
|
||||
error_setg(errp, "Object with id '%s' is not TLS credentials",
|
||||
id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
error_setg(errp,
|
||||
"Expecting TLS credentials with a server endpoint");
|
||||
return NULL;
|
||||
}
|
||||
object_ref(obj);
|
||||
return creds;
|
||||
}
|
||||
|
||||
|
||||
void qmp_nbd_server_start(SocketAddress *addr,
|
||||
bool has_tls_creds, const char *tls_creds,
|
||||
Error **errp)
|
||||
{
|
||||
if (nbd_server) {
|
||||
if (server_fd != -1) {
|
||||
error_setg(errp, "NBD server already running");
|
||||
return;
|
||||
}
|
||||
|
||||
nbd_server = g_new0(NBDServerData, 1);
|
||||
nbd_server->watch = -1;
|
||||
nbd_server->listen_ioc = qio_channel_socket_new();
|
||||
if (qio_channel_socket_listen_sync(
|
||||
nbd_server->listen_ioc, addr, errp) < 0) {
|
||||
goto error;
|
||||
server_fd = socket_listen(addr, errp);
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_tls_creds) {
|
||||
nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
|
||||
if (!nbd_server->tlscreds) {
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* Hook into the BlockBackend notifiers to close the export when the
|
||||
* backend is closed.
|
||||
*/
|
||||
typedef struct NBDCloseNotifier {
|
||||
Notifier n;
|
||||
NBDExport *exp;
|
||||
QTAILQ_ENTRY(NBDCloseNotifier) next;
|
||||
} NBDCloseNotifier;
|
||||
|
||||
if (addr->type != SOCKET_ADDRESS_KIND_INET) {
|
||||
error_setg(errp, "TLS is only supported with IPv4/IPv6");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers =
|
||||
QTAILQ_HEAD_INITIALIZER(close_notifiers);
|
||||
|
||||
nbd_server->watch = qio_channel_add_watch(
|
||||
QIO_CHANNEL(nbd_server->listen_ioc),
|
||||
G_IO_IN,
|
||||
nbd_accept,
|
||||
NULL,
|
||||
NULL);
|
||||
static void nbd_close_notifier(Notifier *n, void *data)
|
||||
{
|
||||
NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n);
|
||||
|
||||
return;
|
||||
notifier_remove(&cn->n);
|
||||
QTAILQ_REMOVE(&close_notifiers, cn, next);
|
||||
|
||||
error:
|
||||
nbd_server_free(nbd_server);
|
||||
nbd_server = NULL;
|
||||
nbd_export_close(cn->exp);
|
||||
nbd_export_put(cn->exp);
|
||||
g_free(cn);
|
||||
}
|
||||
|
||||
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||
@@ -147,8 +76,9 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||
{
|
||||
BlockBackend *blk;
|
||||
NBDExport *exp;
|
||||
NBDCloseNotifier *n;
|
||||
|
||||
if (!nbd_server) {
|
||||
if (server_fd == -1) {
|
||||
error_setg(errp, "NBD server not running");
|
||||
return;
|
||||
}
|
||||
@@ -184,16 +114,23 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||
|
||||
nbd_export_set_name(exp, device);
|
||||
|
||||
/* The list of named exports has a strong reference to this export now and
|
||||
* our only way of accessing it is through nbd_export_find(), so we can drop
|
||||
* the strong reference that is @exp. */
|
||||
nbd_export_put(exp);
|
||||
n = g_new0(NBDCloseNotifier, 1);
|
||||
n->n.notify = nbd_close_notifier;
|
||||
n->exp = exp;
|
||||
blk_add_close_notifier(blk, &n->n);
|
||||
QTAILQ_INSERT_TAIL(&close_notifiers, n, next);
|
||||
}
|
||||
|
||||
void qmp_nbd_server_stop(Error **errp)
|
||||
{
|
||||
nbd_export_close_all();
|
||||
while (!QTAILQ_EMPTY(&close_notifiers)) {
|
||||
NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers);
|
||||
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
|
||||
}
|
||||
|
||||
nbd_server_free(nbd_server);
|
||||
nbd_server = NULL;
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler(server_fd, NULL, NULL, NULL);
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
}
|
||||
}
|
||||
|
470
blockdev.c
470
blockdev.c
@@ -30,7 +30,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/block/block.h"
|
||||
@@ -51,9 +50,6 @@
|
||||
#include "trace.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
|
||||
static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
|
||||
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
|
||||
|
||||
static const char *const if_name[IF_COUNT] = {
|
||||
[IF_NONE] = "none",
|
||||
[IF_IDE] = "ide",
|
||||
@@ -343,6 +339,28 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
||||
{
|
||||
if (throttle_conflicting(cfg)) {
|
||||
error_setg(errp, "bps/iops/max total values and read/write values"
|
||||
" cannot be used at the same time");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!throttle_is_valid(cfg)) {
|
||||
error_setg(errp, "bps/iops/maxs values must be 0 or greater");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (throttle_max_is_missing_limit(cfg)) {
|
||||
error_setg(errp, "bps_max/iops_max require corresponding"
|
||||
" bps/iops values");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
||||
|
||||
/* All parameters but @opts are optional and may be set to NULL. */
|
||||
@@ -369,6 +387,16 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
|
||||
*bdrv_flags |= BDRV_O_CACHE_WB;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
*bdrv_flags |= BDRV_O_NOCACHE;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
*bdrv_flags |= BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
if ((aio = qemu_opt_get(opts, "aio")) != NULL) {
|
||||
if (!strcmp(aio, "native")) {
|
||||
*bdrv_flags |= BDRV_O_NATIVE_AIO;
|
||||
@@ -387,7 +415,7 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
||||
}
|
||||
|
||||
if (throttle_cfg) {
|
||||
throttle_config_init(throttle_cfg);
|
||||
memset(throttle_cfg, 0, sizeof(*throttle_cfg));
|
||||
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg =
|
||||
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
||||
throttle_cfg->buckets[THROTTLE_BPS_READ].avg =
|
||||
@@ -414,23 +442,10 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
||||
throttle_cfg->buckets[THROTTLE_OPS_WRITE].max =
|
||||
qemu_opt_get_number(opts, "throttling.iops-write-max", 0);
|
||||
|
||||
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
|
||||
qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1);
|
||||
throttle_cfg->buckets[THROTTLE_BPS_READ].burst_length =
|
||||
qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1);
|
||||
throttle_cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
|
||||
qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1);
|
||||
throttle_cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
|
||||
qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1);
|
||||
throttle_cfg->buckets[THROTTLE_OPS_READ].burst_length =
|
||||
qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1);
|
||||
throttle_cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
|
||||
qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1);
|
||||
|
||||
throttle_cfg->op_size =
|
||||
qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
||||
|
||||
if (!throttle_is_valid(throttle_cfg, errp)) {
|
||||
if (!check_throttle_config(throttle_cfg, errp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -439,7 +454,7 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
||||
*detect_zeroes =
|
||||
qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
|
||||
qemu_opt_get(opts, "detect-zeroes"),
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
|
||||
&local_error);
|
||||
if (local_error) {
|
||||
@@ -475,6 +490,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
QDict *interval_dict = NULL;
|
||||
QList *interval_list = NULL;
|
||||
const char *id;
|
||||
bool has_driver_specific_opts;
|
||||
BlockdevDetectZeroesOptions detect_zeroes =
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
||||
const char *throttling_group = NULL;
|
||||
@@ -498,6 +514,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
qdict_del(bs_opts, "id");
|
||||
}
|
||||
|
||||
has_driver_specific_opts = !!qdict_size(bs_opts);
|
||||
|
||||
/* extract parameters */
|
||||
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
||||
|
||||
@@ -554,11 +572,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
}
|
||||
|
||||
if (snapshot) {
|
||||
bdrv_flags |= BDRV_O_SNAPSHOT;
|
||||
/* always use cache=unsafe with snapshot */
|
||||
bdrv_flags &= ~BDRV_O_CACHE_MASK;
|
||||
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
||||
}
|
||||
|
||||
/* init */
|
||||
if ((!file || !*file) && !qdict_size(bs_opts)) {
|
||||
if ((!file || !*file) && !has_driver_specific_opts) {
|
||||
BlockBackendRootState *blk_rs;
|
||||
|
||||
blk = blk_new(qemu_opts_id(opts), errp);
|
||||
@@ -586,17 +606,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
||||
* with other callers) rather than what we want as the real defaults.
|
||||
* Apply the defaults here instead. */
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INACTIVE;
|
||||
}
|
||||
|
||||
blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
|
||||
errp);
|
||||
if (!blk) {
|
||||
@@ -675,17 +684,6 @@ static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
||||
* with other callers) rather than what we want as the real defaults.
|
||||
* Apply the defaults here instead. */
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INACTIVE;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, NULL, NULL, bs_opts, bdrv_flags, errp);
|
||||
if (ret < 0) {
|
||||
@@ -704,19 +702,6 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void blockdev_close_all_bdrv_states(void)
|
||||
{
|
||||
BlockDriverState *bs, *next_bs;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
bdrv_unref(bs);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -1202,11 +1187,15 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
}
|
||||
|
||||
static void blockdev_do_action(TransactionAction *action, Error **errp)
|
||||
static void blockdev_do_action(TransactionActionKind type, void *data,
|
||||
Error **errp)
|
||||
{
|
||||
TransactionAction action;
|
||||
TransactionActionList list;
|
||||
|
||||
list.value = action;
|
||||
action.type = type;
|
||||
action.u.data = data;
|
||||
list.value = &action;
|
||||
list.next = NULL;
|
||||
qmp_transaction(&list, false, NULL, errp);
|
||||
}
|
||||
@@ -1232,11 +1221,8 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
|
||||
.has_mode = has_mode,
|
||||
.mode = mode,
|
||||
};
|
||||
TransactionAction action = {
|
||||
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
||||
.u.blockdev_snapshot_sync = &snapshot,
|
||||
};
|
||||
blockdev_do_action(&action, errp);
|
||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
||||
&snapshot, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
||||
@@ -1246,11 +1232,9 @@ void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
||||
.node = (char *) node,
|
||||
.overlay = (char *) overlay
|
||||
};
|
||||
TransactionAction action = {
|
||||
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
||||
.u.blockdev_snapshot = &snapshot_data,
|
||||
};
|
||||
blockdev_do_action(&action, errp);
|
||||
|
||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
||||
&snapshot_data, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_snapshot_internal_sync(const char *device,
|
||||
@@ -1261,11 +1245,9 @@ void qmp_blockdev_snapshot_internal_sync(const char *device,
|
||||
.device = (char *) device,
|
||||
.name = (char *) name
|
||||
};
|
||||
TransactionAction action = {
|
||||
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
||||
.u.blockdev_snapshot_internal_sync = &snapshot,
|
||||
};
|
||||
blockdev_do_action(&action, errp);
|
||||
|
||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
||||
&snapshot, errp);
|
||||
}
|
||||
|
||||
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
||||
@@ -1601,11 +1583,13 @@ static void internal_snapshot_abort(BlkActionState *common)
|
||||
}
|
||||
|
||||
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
|
||||
error_reportf_err(local_error,
|
||||
"Failed to delete snapshot with id '%s' and "
|
||||
"name '%s' on device '%s' in abort: ",
|
||||
sn->id_str, sn->name,
|
||||
bdrv_get_device_name(bs));
|
||||
error_report("Failed to delete snapshot with id '%s' and name '%s' on "
|
||||
"device '%s' in abort: %s",
|
||||
sn->id_str,
|
||||
sn->name,
|
||||
bdrv_get_device_name(bs),
|
||||
error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1732,15 +1716,10 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
/* create new image w/backing file */
|
||||
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||
int64_t size = bdrv_getlength(state->old_bs);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
||||
return;
|
||||
}
|
||||
bdrv_img_create(new_image_file, format,
|
||||
state->old_bs->filename,
|
||||
state->old_bs->drv->format_name,
|
||||
NULL, size, flags, &local_err, false);
|
||||
NULL, -1, flags, &local_err, false);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -2521,8 +2500,6 @@ void qmp_blockdev_change_medium(const char *device, const char *filename,
|
||||
}
|
||||
|
||||
bdrv_flags = blk_get_open_flags_from_root_state(blk);
|
||||
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
|
||||
BDRV_O_PROTOCOL);
|
||||
|
||||
if (!has_read_only) {
|
||||
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
|
||||
@@ -2608,18 +2585,6 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
int64_t iops_rd_max,
|
||||
bool has_iops_wr_max,
|
||||
int64_t iops_wr_max,
|
||||
bool has_bps_max_length,
|
||||
int64_t bps_max_length,
|
||||
bool has_bps_rd_max_length,
|
||||
int64_t bps_rd_max_length,
|
||||
bool has_bps_wr_max_length,
|
||||
int64_t bps_wr_max_length,
|
||||
bool has_iops_max_length,
|
||||
int64_t iops_max_length,
|
||||
bool has_iops_rd_max_length,
|
||||
int64_t iops_rd_max_length,
|
||||
bool has_iops_wr_max_length,
|
||||
int64_t iops_wr_max_length,
|
||||
bool has_iops_size,
|
||||
int64_t iops_size,
|
||||
bool has_group,
|
||||
@@ -2646,7 +2611,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
throttle_config_init(&cfg);
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
|
||||
cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
|
||||
@@ -2674,30 +2639,11 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
|
||||
}
|
||||
|
||||
if (has_bps_max_length) {
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = bps_max_length;
|
||||
}
|
||||
if (has_bps_rd_max_length) {
|
||||
cfg.buckets[THROTTLE_BPS_READ].burst_length = bps_rd_max_length;
|
||||
}
|
||||
if (has_bps_wr_max_length) {
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].burst_length = bps_wr_max_length;
|
||||
}
|
||||
if (has_iops_max_length) {
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = iops_max_length;
|
||||
}
|
||||
if (has_iops_rd_max_length) {
|
||||
cfg.buckets[THROTTLE_OPS_READ].burst_length = iops_rd_max_length;
|
||||
}
|
||||
if (has_iops_wr_max_length) {
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].burst_length = iops_wr_max_length;
|
||||
}
|
||||
|
||||
if (has_iops_size) {
|
||||
cfg.op_size = iops_size;
|
||||
}
|
||||
|
||||
if (!throttle_is_valid(&cfg, errp)) {
|
||||
if (!check_throttle_config(&cfg, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2824,15 +2770,6 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
||||
bs = bdrv_find_node(id);
|
||||
if (bs) {
|
||||
qmp_x_blockdev_del(false, NULL, true, id, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
blk = blk_by_name(id);
|
||||
if (!blk) {
|
||||
error_report("Device '%s' not found", id);
|
||||
@@ -2856,7 +2793,7 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
||||
return;
|
||||
}
|
||||
|
||||
blk_remove_bs(blk);
|
||||
bdrv_close(bs);
|
||||
}
|
||||
|
||||
/* if we have a device attached to this BlockDriverState
|
||||
@@ -3381,23 +3318,29 @@ void qmp_blockdev_backup(const char *device, const char *target,
|
||||
NULL, errp);
|
||||
}
|
||||
|
||||
/* Parameter check and block job starting for drive mirroring.
|
||||
* Caller should hold @device and @target's aio context (must be the same).
|
||||
**/
|
||||
static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
BlockDriverState *target,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error,
|
||||
BlockdevOnError on_source_error,
|
||||
bool has_on_target_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
void qmp_drive_mirror(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
bool has_node_name, const char *node_name,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error, BlockdevOnError on_source_error,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *source, *target_bs;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
QDict *options;
|
||||
int flags;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
if (!has_speed) {
|
||||
speed = 0;
|
||||
@@ -3408,6 +3351,9 @@ static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
if (!has_on_target_error) {
|
||||
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
}
|
||||
if (!has_mode) {
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
if (!has_granularity) {
|
||||
granularity = 0;
|
||||
}
|
||||
@@ -3429,55 +3375,6 @@ static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
|
||||
return;
|
||||
}
|
||||
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
|
||||
return;
|
||||
}
|
||||
if (target->blk) {
|
||||
error_setg(errp, "Cannot mirror to an attached block device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) {
|
||||
sync = MIRROR_SYNC_MODE_FULL;
|
||||
}
|
||||
|
||||
/* pass the node name to replace to mirror start since it's loose coupling
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(bs, target,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync,
|
||||
on_source_error, on_target_error, unmap,
|
||||
block_job_cb, bs, errp);
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
bool has_node_name, const char *node_name,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error, BlockdevOnError on_source_error,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *source, *target_bs;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
QDict *options = NULL;
|
||||
int flags;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
@@ -3493,14 +3390,15 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
goto out;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
if (!has_mode) {
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
|
||||
if (!has_format) {
|
||||
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = bs->open_flags | BDRV_O_RDWR;
|
||||
source = backing_bs(bs);
|
||||
if (!source && sync == MIRROR_SYNC_MODE_TOP) {
|
||||
@@ -3595,81 +3493,25 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
blockdev_mirror_common(bs, target_bs,
|
||||
has_replaces, replaces, sync,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
has_buf_size, buf_size,
|
||||
has_on_source_error, on_source_error,
|
||||
has_on_target_error, on_target_error,
|
||||
has_unmap, unmap,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
/* pass the node name to replace to mirror start since it's loose coupling
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(bs, target_bs,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync,
|
||||
on_source_error, on_target_error,
|
||||
unmap,
|
||||
block_job_cb, bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
bdrv_unref(target_bs);
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_blockdev_mirror(const char *device, const char *target,
|
||||
bool has_replaces, const char *replaces,
|
||||
MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error,
|
||||
BlockdevOnError on_source_error,
|
||||
bool has_on_target_error,
|
||||
BlockdevOnError on_target_error,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *target_bs;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
|
||||
if (!bs) {
|
||||
error_setg(errp, "Device '%s' has no media", device);
|
||||
return;
|
||||
}
|
||||
|
||||
target_bs = bdrv_lookup_bs(target, target, errp);
|
||||
if (!target_bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_ref(target_bs);
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
blockdev_mirror_common(bs, target_bs,
|
||||
has_replaces, replaces, sync,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
has_buf_size, buf_size,
|
||||
has_on_source_error, on_source_error,
|
||||
has_on_target_error, on_target_error,
|
||||
true, true,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
bdrv_unref(target_bs);
|
||||
}
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
/* Get the block job for a given device name and acquire its AioContext */
|
||||
static BlockJob *find_block_job(const char *device, AioContext **aio_context,
|
||||
Error **errp)
|
||||
@@ -3884,36 +3726,6 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void hmp_drive_add_node(Monitor *mon, const char *optstr)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
QDict *qdict;
|
||||
Error *local_err = NULL;
|
||||
|
||||
opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false);
|
||||
if (!opts) {
|
||||
return;
|
||||
}
|
||||
|
||||
qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
|
||||
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||
error_report("'node-name' needs to be specified");
|
||||
goto out;
|
||||
}
|
||||
|
||||
BlockDriverState *bs = bds_tree_init(qdict, &local_err);
|
||||
if (!bs) {
|
||||
error_report_err(local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
||||
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||
@@ -3938,8 +3750,8 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options,
|
||||
&local_err);
|
||||
visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
|
||||
&options, NULL, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
@@ -3969,15 +3781,12 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
if (!bs) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
||||
}
|
||||
|
||||
if (bs && bdrv_key_required(bs)) {
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
} else {
|
||||
QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list);
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
error_setg(errp, "blockdev-add doesn't support encrypted devices");
|
||||
@@ -4037,13 +3846,7 @@ void qmp_x_blockdev_del(bool has_id, const char *id,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!blk && !bs->monitor_list.tqe_prev) {
|
||||
error_setg(errp, "Node %s is not owned by the monitor",
|
||||
bs->node_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bs->refcnt > 1) {
|
||||
if (bs->refcnt > 1 || !QLIST_EMPTY(&bs->parents)) {
|
||||
error_setg(errp, "Block device %s is in use",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
goto out;
|
||||
@@ -4053,7 +3856,6 @@ void qmp_x_blockdev_del(bool has_id, const char *id,
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
} else {
|
||||
QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list);
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
|
||||
@@ -4096,6 +3898,18 @@ QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "discard",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = BDRV_OPT_CACHE_WB,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables writeback mode for any caches",
|
||||
},{
|
||||
.name = BDRV_OPT_CACHE_DIRECT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
||||
},{
|
||||
.name = BDRV_OPT_CACHE_NO_FLUSH,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "ignore any flush requests for the device",
|
||||
},{
|
||||
.name = "aio",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -4164,30 +3978,6 @@ QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "throttling.bps-write-max",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "total bytes write burst",
|
||||
},{
|
||||
.name = "throttling.iops-total-max-length",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "length of the iops-total-max burst period, in seconds",
|
||||
},{
|
||||
.name = "throttling.iops-read-max-length",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "length of the iops-read-max burst period, in seconds",
|
||||
},{
|
||||
.name = "throttling.iops-write-max-length",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "length of the iops-write-max burst period, in seconds",
|
||||
},{
|
||||
.name = "throttling.bps-total-max-length",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "length of the bps-total-max burst period, in seconds",
|
||||
},{
|
||||
.name = "throttling.bps-read-max-length",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "length of the bps-read-max burst period, in seconds",
|
||||
},{
|
||||
.name = "throttling.bps-write-max-length",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "length of the bps-write-max burst period, in seconds",
|
||||
},{
|
||||
.name = "throttling.iops-size",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
@@ -4227,6 +4017,18 @@ static QemuOptsList qemu_root_bds_opts = {
|
||||
.name = "discard",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = "cache.writeback",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables writeback mode for any caches",
|
||||
},{
|
||||
.name = "cache.direct",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
||||
},{
|
||||
.name = "cache.no-flush",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "ignore any flush requests for the device",
|
||||
},{
|
||||
.name = "aio",
|
||||
.type = QEMU_OPT_STRING,
|
||||
|
16
blockjob.c
16
blockjob.c
@@ -23,7 +23,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "config-host.h"
|
||||
#include "qemu-common.h"
|
||||
#include "trace.h"
|
||||
#include "block/block.h"
|
||||
@@ -278,6 +278,14 @@ void block_job_iostatus_reset(BlockJob *job)
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockFinishData {
|
||||
BlockJob *job;
|
||||
BlockCompletionFunc *cb;
|
||||
void *opaque;
|
||||
bool cancelled;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static int block_job_finish_sync(BlockJob *job,
|
||||
void (*finish)(BlockJob *, Error **errp),
|
||||
Error **errp)
|
||||
@@ -296,9 +304,7 @@ static int block_job_finish_sync(BlockJob *job,
|
||||
return -EBUSY;
|
||||
}
|
||||
while (!job->completed) {
|
||||
aio_poll(job->deferred_to_main_loop ? qemu_get_aio_context() :
|
||||
bdrv_get_aio_context(bs),
|
||||
true);
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
||||
block_job_unref(job);
|
||||
@@ -472,7 +478,6 @@ static void block_job_defer_to_main_loop_bh(void *opaque)
|
||||
aio_context = bdrv_get_aio_context(data->job->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
data->job->deferred_to_main_loop = false;
|
||||
data->fn(data->job, data->opaque);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
@@ -492,7 +497,6 @@ void block_job_defer_to_main_loop(BlockJob *job,
|
||||
data->aio_context = bdrv_get_aio_context(job->bs);
|
||||
data->fn = fn;
|
||||
data->opaque = opaque;
|
||||
job->deferred_to_main_loop = true;
|
||||
|
||||
qemu_bh_schedule(data->bh);
|
||||
}
|
||||
|
13
bootdevice.c
13
bootdevice.c
@@ -22,7 +22,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -271,21 +270,21 @@ typedef struct {
|
||||
DeviceState *dev;
|
||||
} BootIndexProperty;
|
||||
|
||||
static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
static void device_get_bootindex(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
BootIndexProperty *prop = opaque;
|
||||
visit_type_int32(v, name, prop->bootindex, errp);
|
||||
visit_type_int32(v, prop->bootindex, name, errp);
|
||||
}
|
||||
|
||||
static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
static void device_set_bootindex(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
BootIndexProperty *prop = opaque;
|
||||
int32_t boot_index;
|
||||
Error *local_err = NULL;
|
||||
|
||||
visit_type_int32(v, name, &boot_index, &local_err);
|
||||
visit_type_int32(v, &boot_index, name, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user