Compare commits
119
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f053f6b83d | ||
|
|
3d79eb5b30 | ||
|
|
6ec48b09b7 | ||
|
|
543347215c | ||
|
|
3c548f6700 | ||
|
|
d0d83e8fe7 | ||
|
|
98103fa736 | ||
|
|
e5f0eb06a8 | ||
|
|
43ac708d4c | ||
|
|
8e09e2013d | ||
|
|
520b341264 | ||
|
|
e0efb023c0 | ||
|
|
d56b0b85c8 | ||
|
|
750f169519 | ||
|
|
046e357379 | ||
|
|
b1251db258 | ||
|
|
29cffd368a | ||
|
|
5a782bbcae | ||
|
|
ad0d183175 | ||
|
|
210ec8f757 | ||
|
|
55103ab3b4 | ||
|
|
3bb84a6c98 | ||
|
|
48935f029f | ||
|
|
3477445077 | ||
|
|
7be09afb61 | ||
|
|
cab7dfcb06 | ||
|
|
b5706a74b1 | ||
|
|
41971817a6 | ||
|
|
3f977a5be8 | ||
|
|
80cfe4a43f | ||
|
|
01083f16de | ||
|
|
9221efd775 | ||
|
|
4ce91be0c3 | ||
|
|
b2f0e9240b | ||
|
|
f9ac1dc507 | ||
|
|
a1d82075ab | ||
|
|
e4b3a2b349 | ||
|
|
84461c74b7 | ||
|
|
552e70d844 | ||
|
|
6ef0b7a992 | ||
|
|
aa69eda237 | ||
|
|
8dedaf0f70 | ||
|
|
eb3eb3dd36 | ||
|
|
88efef64db | ||
|
|
ab139bf9d2 | ||
|
|
d728dafe5a | ||
|
|
0f00455b4c | ||
|
|
6ea6bd5d75 | ||
|
|
86cfc10440 | ||
|
|
fe7e98c46e | ||
|
|
ba980a52d6 | ||
|
|
df54f5efed | ||
|
|
0d38666664 | ||
|
|
27fb65dd1b | ||
|
|
6ea80edc01 | ||
|
|
f99329cd82 | ||
|
|
207f61dc28 | ||
|
|
d1567e2980 | ||
|
|
5e3322eec3 | ||
|
|
5a0913f782 | ||
|
|
40a3fb54b2 | ||
|
|
98646a11d0 | ||
|
|
53bdfb5b62 | ||
|
|
a3e3f0964d | ||
|
|
7812cbe580 | ||
|
|
12d5fc6ab4 | ||
|
|
b203bba0ce | ||
|
|
91148decd2 | ||
|
|
ae2e18e0ca | ||
|
|
2dbd09ff5c | ||
|
|
3d5acbeb71 | ||
|
|
a4b73ed3f3 | ||
|
|
a3967c74de | ||
|
|
5c85998739 | ||
|
|
d234c8f1f6 | ||
|
|
3ba1e617e7 | ||
|
|
96e7f7a911 | ||
|
|
c230ab2ba2 | ||
|
|
274c96e4e1 | ||
|
|
32c113c182 | ||
|
|
912d9cc089 | ||
|
|
074f673a9a | ||
|
|
83b1dc162c | ||
|
|
c2d37222ae | ||
|
|
e40585f8f1 | ||
|
|
837e02ef07 | ||
|
|
46a1b0e489 | ||
|
|
a14d42919f | ||
|
|
2ac9549453 | ||
|
|
2f18e44b0c | ||
|
|
4c1e06408d | ||
|
|
4a7a497509 | ||
|
|
7dfa87e9cb | ||
|
|
7b0387ad90 | ||
|
|
1624861997 | ||
|
|
fb039011ff | ||
|
|
5ad12b3235 | ||
|
|
15c35dfd92 | ||
|
|
e7ff13929f | ||
|
|
894f179e8d | ||
|
|
2265c71b35 | ||
|
|
95d9149ed3 | ||
|
|
eb55958e18 | ||
|
|
1124696193 | ||
|
|
4e48018ae9 | ||
|
|
7297dba271 | ||
|
|
9c01a91c7b | ||
|
|
7cf5f5d087 | ||
|
|
f7ef3baa49 | ||
|
|
3c6066172f | ||
|
|
d4c9095900 | ||
|
|
331c549b2d | ||
|
|
b24cfb0f1e | ||
|
|
7872f3e77e | ||
|
|
390252193c | ||
|
|
49e8918a4d | ||
|
|
6067df7a5d | ||
|
|
9ee8ab5503 | ||
|
|
c8723d46c1 |
+16
-8
@@ -4,17 +4,12 @@
|
||||
/config-host.*
|
||||
/config-target.*
|
||||
/config.status
|
||||
/config-temp
|
||||
/trace/generated-tracers.h
|
||||
/trace/generated-tracers.c
|
||||
/trace/generated-tracers-dtrace.h
|
||||
/trace/generated-tracers.dtrace
|
||||
/trace/generated-events.h
|
||||
/trace/generated-events.c
|
||||
/trace/generated-helpers-wrappers.h
|
||||
/trace/generated-helpers.h
|
||||
/trace/generated-helpers.c
|
||||
/trace/generated-tcg-tracers.h
|
||||
/trace/generated-ust-provider.h
|
||||
/trace/generated-ust.c
|
||||
/libcacard/trace/generated-tracers.c
|
||||
@@ -23,14 +18,13 @@
|
||||
/*-darwin-user
|
||||
/*-linux-user
|
||||
/*-bsd-user
|
||||
/libdis*
|
||||
/libuser
|
||||
libdis*
|
||||
libuser
|
||||
/linux-headers/asm
|
||||
/qga/qapi-generated
|
||||
/qapi-generated
|
||||
/qapi-types.[ch]
|
||||
/qapi-visit.[ch]
|
||||
/qapi-event.[ch]
|
||||
/qmp-commands.h
|
||||
/qmp-marshal.c
|
||||
/qemu-doc.html
|
||||
@@ -55,9 +49,19 @@
|
||||
/qemu-monitor.texi
|
||||
/qmp-commands.txt
|
||||
/vscclient
|
||||
/test-bitops
|
||||
/test-coroutine
|
||||
/test-int128
|
||||
/test-opts-visitor
|
||||
/test-qmp-input-visitor
|
||||
/test-qmp-output-visitor
|
||||
/test-string-input-visitor
|
||||
/test-string-output-visitor
|
||||
/test-visitor-serialization
|
||||
/fsdev/virtfs-proxy-helper
|
||||
/fsdev/virtfs-proxy-helper.1
|
||||
/fsdev/virtfs-proxy-helper.pod
|
||||
/.gdbinit
|
||||
*.a
|
||||
*.aux
|
||||
*.cp
|
||||
@@ -86,8 +90,12 @@
|
||||
*.pc
|
||||
.libs
|
||||
.sdk
|
||||
*.swp
|
||||
*.orig
|
||||
.pc
|
||||
*.gcda
|
||||
*.gcno
|
||||
patches
|
||||
/pc-bios/bios-pq/status
|
||||
/pc-bios/vgabios-pq/status
|
||||
/pc-bios/optionrom/linuxboot.asm
|
||||
|
||||
@@ -28,6 +28,3 @@
|
||||
[submodule "dtc"]
|
||||
path = dtc
|
||||
url = git://git.qemu-project.org/dtc.git
|
||||
[submodule "roms/u-boot"]
|
||||
path = roms/u-boot
|
||||
url = git://git.qemu-project.org/u-boot.git
|
||||
|
||||
+22
-41
@@ -12,7 +12,7 @@ notifications:
|
||||
on_failure: always
|
||||
env:
|
||||
global:
|
||||
- TEST_CMD=""
|
||||
- TEST_CMD="make check"
|
||||
- EXTRA_CONFIG=""
|
||||
# Development packages, EXTRA_PKGS saved for additional builds
|
||||
- CORE_PKGS="libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev"
|
||||
@@ -20,51 +20,31 @@ env:
|
||||
- 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
|
||||
- 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=m68k-softmmu,m68k-linux-user
|
||||
- TARGETS=microblaze-softmmu,microblazeel-softmmu,microblaze-linux-user,microblazeel-linux-user
|
||||
- TARGETS=arm-softmmu,arm-linux-user
|
||||
- TARGETS=aarch64-softmmu,aarch64-linux-user
|
||||
- TARGETS=cris-softmmu
|
||||
- TARGETS=i386-softmmu,x86_64-softmmu
|
||||
- TARGETS=lm32-softmmu
|
||||
- TARGETS=m68k-softmmu
|
||||
- TARGETS=microblaze-softmmu,microblazeel-softmmu
|
||||
- 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:
|
||||
# we want to do this ourselves
|
||||
submodules: false
|
||||
- TARGETS=moxie-softmmu
|
||||
- TARGETS=or32-softmmu,
|
||||
- TARGETS=ppc-softmmu,ppc64-softmmu,ppcemb-softmmu
|
||||
- TARGETS=s390x-softmmu
|
||||
- TARGETS=sh4-softmmu,sh4eb-softmmu
|
||||
- TARGETS=sparc-softmmu,sparc64-softmmu
|
||||
- TARGETS=unicore32-softmmu
|
||||
- TARGETS=xtensa-softmmu,xtensaeb-softmmu
|
||||
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:
|
||||
- make -j2 && ${TEST_CMD}
|
||||
script: "./configure --target-list=${TARGETS} ${EXTRA_CONFIG} && make && ${TEST_CMD}"
|
||||
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=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-debug"
|
||||
@@ -86,15 +66,16 @@ matrix:
|
||||
compiler: gcc
|
||||
# All the trace backends (apart from dtrace)
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=stderr"
|
||||
EXTRA_CONFIG="--enable-trace-backend=stderr"
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=simple"
|
||||
EXTRA_CONFIG="--enable-trace-backend=simple"
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-trace-backends=ftrace"
|
||||
EXTRA_CONFIG="--enable-trace-backend=ftrace"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_PKGS="liblttng-ust-dev liburcu-dev"
|
||||
EXTRA_CONFIG="--enable-trace-backends=ust"
|
||||
EXTRA_CONFIG="--enable-trace-backend=ust"
|
||||
compiler: gcc
|
||||
|
||||
@@ -91,17 +91,3 @@ Mixed declarations (interleaving statements and declarations within blocks)
|
||||
are not allowed; declarations should be at the beginning of blocks. In other
|
||||
words, the code should not generate warnings if using GCC's
|
||||
-Wdeclaration-after-statement option.
|
||||
|
||||
6. Conditional statements
|
||||
|
||||
When comparing a variable for (in)equality with a constant, list the
|
||||
constant on the right, as in:
|
||||
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
|
||||
Besides, good compilers already warn users when '==' is mis-typed as '=',
|
||||
even when the constant is on the right.
|
||||
|
||||
+17
-161
@@ -51,34 +51,14 @@ Descriptions of section entries:
|
||||
General Project Administration
|
||||
------------------------------
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
||||
Responsible Disclosure, Reporting Security Issues
|
||||
------------------------------
|
||||
W: http://wiki.qemu.org/SecurityProcess
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
L: secalert@redhat.com
|
||||
|
||||
Guest CPU cores (TCG):
|
||||
----------------------
|
||||
Overall
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Odd fixes
|
||||
F: cpu-exec.c
|
||||
F: cputlb.c
|
||||
F: softmmu_template.h
|
||||
F: translate-all.c
|
||||
F: include/exec/cpu_ldst.h
|
||||
F: include/exec/cpu_ldst_template.h
|
||||
F: include/exec/helper*.h
|
||||
|
||||
Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: target-alpha/
|
||||
F: hw/alpha/
|
||||
F: tests/tcg/alpha/
|
||||
|
||||
ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -92,7 +72,6 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: target-cris/
|
||||
F: hw/cris/
|
||||
F: tests/tcg/cris/
|
||||
|
||||
LM32
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
@@ -100,7 +79,6 @@ S: Maintained
|
||||
F: target-lm32/
|
||||
F: hw/lm32/
|
||||
F: hw/char/lm32_*
|
||||
F: tests/tcg/lm32/
|
||||
|
||||
M68K
|
||||
S: Orphan
|
||||
@@ -115,11 +93,9 @@ F: hw/microblaze/
|
||||
|
||||
MIPS
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Leon Alrae <leon.alrae@imgtec.com>
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: target-mips/
|
||||
F: hw/mips/
|
||||
F: tests/tcg/mips/
|
||||
|
||||
Moxie
|
||||
M: Anthony Green <green@moxielogic.com>
|
||||
@@ -131,7 +107,6 @@ M: Jia Liu <proljc@gmail.com>
|
||||
S: Maintained
|
||||
F: target-openrisc/
|
||||
F: hw/openrisc/
|
||||
F: tests/tcg/openrisc/
|
||||
|
||||
PowerPC
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
@@ -167,8 +142,7 @@ F: target-unicore32/
|
||||
F: hw/unicore32/
|
||||
|
||||
X86
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: target-i386/
|
||||
F: hw/i386/
|
||||
@@ -179,13 +153,6 @@ W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
|
||||
S: Maintained
|
||||
F: target-xtensa/
|
||||
F: hw/xtensa/
|
||||
F: tests/tcg/xtensa/
|
||||
|
||||
TriCore
|
||||
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
|
||||
S: Maintained
|
||||
F: target-tricore/
|
||||
F: hw/tricore/
|
||||
|
||||
Guest CPU Cores (KVM):
|
||||
----------------------
|
||||
@@ -202,11 +169,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: target-arm/kvm.c
|
||||
|
||||
MIPS
|
||||
M: James Hogan <james.hogan@imgtec.com>
|
||||
S: Maintained
|
||||
F: target-mips/kvm.c
|
||||
|
||||
PPC
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
@@ -218,12 +180,9 @@ M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target-s390x/kvm.c
|
||||
F: hw/intc/s390_flic.c
|
||||
F: hw/intc/s390_flic_kvm.c
|
||||
F: include/hw/s390x/s390_flic.h
|
||||
F: hw/intc/s390_flic.[hc]
|
||||
|
||||
X86
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Marcelo Tosatti <mtosatti@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
@@ -277,8 +236,8 @@ S: Maintained
|
||||
F: hw/*/exynos*
|
||||
|
||||
Calxeda Highbank
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
S: Maintained
|
||||
M: Mark Langsdorf <mark.langsdorf@calxeda.com>
|
||||
S: Supported
|
||||
F: hw/arm/highbank.c
|
||||
F: hw/net/xgmac.c
|
||||
|
||||
@@ -289,7 +248,7 @@ F: include/hw/arm/digic.h
|
||||
F: hw/*/digic*
|
||||
|
||||
Gumstix
|
||||
L: qemu-devel@nongnu.org
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/arm/gumstix.c
|
||||
|
||||
@@ -305,7 +264,7 @@ S: Maintained
|
||||
F: hw/arm/integratorcp.c
|
||||
|
||||
Mainstone
|
||||
L: qemu-devel@nongnu.org
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/arm/mainstone.c
|
||||
|
||||
@@ -411,7 +370,7 @@ S: Maintained
|
||||
F: hw/mips/mips_malta.c
|
||||
|
||||
Mipssim
|
||||
L: qemu-devel@nongnu.org
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/mips/mips_mipssim.c
|
||||
|
||||
@@ -544,8 +503,6 @@ F: hw/s390x/s390-virtio-ccw.c
|
||||
F: hw/s390x/css.[hc]
|
||||
F: hw/s390x/sclp*.[hc]
|
||||
F: hw/s390x/ipl*.[hc]
|
||||
F: include/hw/s390x/
|
||||
F: pc-bios/s390-ccw/
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
UniCore32 Machines
|
||||
@@ -583,19 +540,17 @@ Xtensa Machines
|
||||
sim
|
||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/xtensa/sim.c
|
||||
F: hw/xtensa/xtensa_sim.c
|
||||
|
||||
XTFPGA (LX60, LX200, ML605, KC705)
|
||||
Avnet LX60
|
||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/xtensa/xtfpga.c
|
||||
F: hw/net/opencores_eth.c
|
||||
F: hw/xtensa/xtensa_lx60.c
|
||||
|
||||
Devices
|
||||
-------
|
||||
IDE
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: include/hw/ide.h
|
||||
F: hw/ide/
|
||||
@@ -646,13 +601,6 @@ USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb/*
|
||||
F: tests/usb-*-test.c
|
||||
|
||||
USB (serial adapter)
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
S: Maintained
|
||||
F: hw/usb/dev-serial.c
|
||||
|
||||
VFIO
|
||||
M: Alex Williamson <alex.williamson@redhat.com>
|
||||
@@ -703,12 +651,6 @@ S: Supported
|
||||
F: hw/block/nvme*
|
||||
F: tests/nvme-test.c
|
||||
|
||||
megasas
|
||||
M: Hannes Reinecke <hare@suse.de>
|
||||
S: Supported
|
||||
F: hw/scsi/megasas.c
|
||||
F: hw/scsi/mfi.h
|
||||
|
||||
Xilinx EDK
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
@@ -716,12 +658,6 @@ S: Maintained
|
||||
F: hw/*/xilinx_*
|
||||
F: include/hw/xilinx.h
|
||||
|
||||
Vmware
|
||||
M: Dmitry Fleytman <dmitry@daynix.com>
|
||||
S: Maintained
|
||||
F: hw/net/vmxnet*
|
||||
F: hw/scsi/vmw_pvscsi*
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
@@ -730,38 +666,21 @@ M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: audio/
|
||||
F: hw/audio/
|
||||
F: tests/ac97-test.c
|
||||
F: tests/es1370-test.c
|
||||
F: tests/intel-hda-test.c
|
||||
|
||||
Block
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Supported
|
||||
F: async.c
|
||||
F: aio-*.c
|
||||
F: block*
|
||||
F: block/
|
||||
F: hw/block/
|
||||
F: qemu-img*
|
||||
F: qemu-io*
|
||||
F: tests/image-fuzzer/
|
||||
F: tests/qemu-iotests/
|
||||
T: git git://repo.or.cz/qemu/kevin.git block
|
||||
T: git git://github.com/stefanha/qemu.git block
|
||||
|
||||
Character Devices
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: qemu-char.c
|
||||
F: backends/msmouse.c
|
||||
F: backends/testdev.c
|
||||
|
||||
Character Devices (Braille)
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
S: Maintained
|
||||
F: backends/baum.c
|
||||
|
||||
CPU
|
||||
M: Andreas Färber <afaerber@suse.de>
|
||||
@@ -783,21 +702,11 @@ S: Maintained
|
||||
F: device_tree.[ch]
|
||||
|
||||
GDB stub
|
||||
L: qemu-devel@nongnu.org
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: gdbstub*
|
||||
F: gdb-xml/
|
||||
|
||||
Memory API
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Supported
|
||||
F: include/exec/ioport.h
|
||||
F: ioport.c
|
||||
F: include/exec/memory.h
|
||||
F: memory.c
|
||||
F: include/exec/memory-internal.h
|
||||
F: exec.c
|
||||
|
||||
SPICE
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
@@ -820,11 +729,7 @@ F: ui/cocoa.m
|
||||
|
||||
Main loop
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: cpus.c
|
||||
F: main-loop.c
|
||||
F: qemu-timer.c
|
||||
S: Supported
|
||||
F: vl.c
|
||||
|
||||
Human Monitor (HMP)
|
||||
@@ -863,7 +768,6 @@ M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
F: qapi/
|
||||
F: tests/qapi-schema/
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
QAPI Schema
|
||||
@@ -874,29 +778,6 @@ S: Supported
|
||||
F: qapi-schema.json
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
QObject
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
S: Maintained
|
||||
F: qobject/
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
QEMU Guest Agent
|
||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
F: qga/
|
||||
T: git git://github.com/mdroth/qemu.git qga
|
||||
|
||||
QOM
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Andreas Färber <afaerber@suse.de>
|
||||
S: Supported
|
||||
T: git git://github.com/afaerber/qemu-cpu.git qom-next
|
||||
F: include/qom/
|
||||
X: include/qom/cpu.h
|
||||
F: qom/
|
||||
X: qom/cpu.c
|
||||
F: tests/qom-test.c
|
||||
|
||||
QMP
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
S: Maintained
|
||||
@@ -926,29 +807,14 @@ M: Blue Swirl <blauwirbel@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: scripts/checkpatch.pl
|
||||
|
||||
Migration
|
||||
M: Juan Quintela <quintela@redhat.com>
|
||||
S: Maintained
|
||||
F: include/migration/
|
||||
F: migration*
|
||||
F: savevm.c
|
||||
F: arch_init.c
|
||||
F: vmstate.c
|
||||
|
||||
Seccomp
|
||||
M: Eduardo Otubo <eduardo.otubo@profitbricks.com>
|
||||
M: Eduardo Otubo <otubo@linux.vnet.ibm.com>
|
||||
S: Supported
|
||||
F: qemu-seccomp.c
|
||||
F: include/sysemu/seccomp.h
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
M: Riku Voipio <riku.voipio@iki.fi>
|
||||
S: Maintained
|
||||
F: thunk.c
|
||||
F: user-exec.c
|
||||
|
||||
BSD user
|
||||
M: Blue Swirl <blauwirbel@gmail.com>
|
||||
S: Maintained
|
||||
@@ -962,6 +828,7 @@ F: linux-user/
|
||||
Tiny Code Generator (TCG)
|
||||
-------------------------
|
||||
Common code
|
||||
M: qemu-devel@nongnu.org
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: tcg/
|
||||
@@ -978,7 +845,7 @@ S: Maintained
|
||||
F: tcg/arm/
|
||||
|
||||
i386 target
|
||||
L: qemu-devel@nongnu.org
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Maintained
|
||||
F: tcg/i386/
|
||||
|
||||
@@ -1055,7 +922,7 @@ S: Supported
|
||||
F: block/rbd.c
|
||||
|
||||
Sheepdog
|
||||
M: Hitoshi Mitake <mitake.hitoshi@lab.ntt.co.jp>
|
||||
M: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
|
||||
M: Liu Yuan <namei.unix@gmail.com>
|
||||
L: sheepdog@lists.wpkg.org
|
||||
S: Supported
|
||||
@@ -1087,14 +954,3 @@ SSH
|
||||
M: Richard W.M. Jones <rjones@redhat.com>
|
||||
S: Supported
|
||||
F: block/ssh.c
|
||||
|
||||
ARCHIPELAGO
|
||||
M: Chrysostomos Nanakos <cnanakos@grnet.gr>
|
||||
M: Chrysostomos Nanakos <chris@include.gr>
|
||||
S: Maintained
|
||||
F: block/archipelago.c
|
||||
|
||||
Bootdevice
|
||||
M: Gonglei <arei.gonglei@huawei.com>
|
||||
S: Maintained
|
||||
F: bootdevice.c
|
||||
|
||||
@@ -45,25 +45,19 @@ endif
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS = config-host.h qemu-options.def
|
||||
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
|
||||
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-events.h
|
||||
GENERATED_SOURCES += trace/generated-events.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-tracers.h
|
||||
ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace)
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
GENERATED_HEADERS += trace/generated-tracers-dtrace.h
|
||||
endif
|
||||
GENERATED_SOURCES += trace/generated-tracers.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-tcg-tracers.h
|
||||
|
||||
GENERATED_HEADERS += trace/generated-helpers-wrappers.h
|
||||
GENERATED_HEADERS += trace/generated-helpers.h
|
||||
GENERATED_SOURCES += trace/generated-helpers.c
|
||||
|
||||
ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust)
|
||||
ifeq ($(TRACE_BACKEND),ust)
|
||||
GENERATED_HEADERS += trace/generated-ust-provider.h
|
||||
GENERATED_SOURCES += trace/generated-ust.c
|
||||
endif
|
||||
@@ -154,6 +148,10 @@ endif
|
||||
|
||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||
|
||||
config-host.h: config-host.h-timestamp
|
||||
config-host.h-timestamp: config-host.mak
|
||||
qemu-options.def: $(SRC_PATH)/qemu-options.hx
|
||||
@@ -197,6 +195,8 @@ ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
|
||||
|
||||
recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
|
||||
|
||||
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
|
||||
|
||||
$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc $(BUILD_DIR)/config-host.h | $(BUILD_DIR)/version.lo
|
||||
$(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<," RC version.o")
|
||||
$(BUILD_DIR)/version.lo: $(SRC_PATH)/version.rc $(BUILD_DIR)/config-host.h
|
||||
@@ -208,7 +208,7 @@ Makefile: $(version-obj-y) $(version-lobj-y)
|
||||
# Build libraries
|
||||
|
||||
libqemustub.a: $(stub-obj-y)
|
||||
libqemuutil.a: $(util-obj-y)
|
||||
libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
|
||||
|
||||
block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL
|
||||
util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
|
||||
@@ -238,44 +238,23 @@ qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
|
||||
|
||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
|
||||
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
|
||||
$(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \
|
||||
" GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@")
|
||||
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\
|
||||
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
|
||||
$(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \
|
||||
" GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@")
|
||||
qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
|
||||
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
|
||||
$(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \
|
||||
" GEN $@")
|
||||
|
||||
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
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@")
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
|
||||
$(gen-out-type) -o "." -b -i $<, \
|
||||
" GEN $@")
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." -b < $<, " GEN $@")
|
||||
qapi-visit.c qapi-visit.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
|
||||
$(gen-out-type) -o "." -b -i $<, \
|
||||
" GEN $@")
|
||||
qapi-event.c qapi-event.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
|
||||
$(gen-out-type) -o "." -b -i $<, \
|
||||
" GEN $@")
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, " GEN $@")
|
||||
qmp-commands.h qmp-marshal.c :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
|
||||
$(gen-out-type) -o "." -m -i $<, \
|
||||
" GEN $@")
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, " GEN $@")
|
||||
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
|
||||
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
|
||||
@@ -350,8 +329,7 @@ multiboot.bin linuxboot.bin kvmvapic.bin \
|
||||
s390-zipl.rom \
|
||||
s390-ccw.img \
|
||||
spapr-rtas.bin slof.bin \
|
||||
palcode-clipper \
|
||||
u-boot.e500
|
||||
palcode-clipper
|
||||
else
|
||||
BLOBS=
|
||||
endif
|
||||
@@ -392,19 +370,19 @@ install-sysconfig: install-datadir install-confdir
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig \
|
||||
install-datadir install-localstatedir
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(bindir)"
|
||||
ifneq ($(TOOLS),)
|
||||
$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir))
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
endif
|
||||
ifneq ($(CONFIG_MODULES),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
|
||||
for s in $(modules-m:.mo=$(DSOSUF)); do \
|
||||
t="$(DESTDIR)$(qemu_moddir)/$$(echo $$s | tr / -)"; \
|
||||
$(INSTALL_LIB) $$s "$$t"; \
|
||||
test -z "$(STRIP)" || $(STRIP) "$$t"; \
|
||||
for s in $(patsubst %.mo,%$(DSOSUF),$(modules-m)); do \
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $$s "$(DESTDIR)$(qemu_moddir)/$$(echo $$s | tr / -)"; \
|
||||
done
|
||||
endif
|
||||
ifneq ($(HELPERS-y),)
|
||||
$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
|
||||
endif
|
||||
ifneq ($(BLOBS),)
|
||||
set -e; for x in $(BLOBS); do \
|
||||
@@ -418,7 +396,6 @@ endif
|
||||
set -e; for x in $(KEYMAPS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
|
||||
done
|
||||
$(INSTALL_DATA) $(SRC_PATH)/trace-events "$(DESTDIR)$(qemu_datadir)/trace-events"
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
+9
-13
@@ -1,7 +1,7 @@
|
||||
#######################################################################
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/
|
||||
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
|
||||
util-obj-y = util/ qobject/ qapi/ trace/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
@@ -12,6 +12,7 @@ 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
|
||||
block-obj-y += block/
|
||||
block-obj-y += qapi-types.o qapi-visit.o
|
||||
block-obj-y += qemu-io-cmds.o
|
||||
|
||||
block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
||||
@@ -30,8 +31,6 @@ libcacard-y += libcacard/vcard_emul_nss.o
|
||||
libcacard-y += libcacard/vcard_emul_type.o
|
||||
libcacard-y += libcacard/card_7816.o
|
||||
libcacard-y += libcacard/vcardt.o
|
||||
libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS)
|
||||
libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS)
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
@@ -50,7 +49,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration.o migration-tcp.o
|
||||
common-obj-y += vmstate.o
|
||||
common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
|
||||
common-obj-y += qemu-file.o
|
||||
common-obj-$(CONFIG_RDMA) += migration-rdma.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += block-migration.o
|
||||
@@ -62,15 +61,12 @@ common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
||||
|
||||
common-obj-y += audio/
|
||||
common-obj-y += hw/
|
||||
common-obj-y += accel.o
|
||||
|
||||
common-obj-y += ui/
|
||||
common-obj-y += bt-host.o bt-vhci.o
|
||||
bt-host.o-cflags := $(BLUEZ_CFLAGS)
|
||||
|
||||
common-obj-y += dma-helpers.o
|
||||
common-obj-y += vl.o
|
||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
||||
common-obj-y += tpm.o
|
||||
|
||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
||||
@@ -88,6 +84,11 @@ common-obj-y += qmp-marshal.o
|
||||
common-obj-y += qmp.o hmp.o
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# some qapi visitors are used by both system and user emulation:
|
||||
|
||||
common-obj-y += qapi-visit.o qapi-types.o
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
common-obj-y += qemu-log.o
|
||||
@@ -101,15 +102,10 @@ common-obj-y += disas/
|
||||
version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
|
||||
version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
|
||||
|
||||
######################################################################
|
||||
# tracing
|
||||
util-obj-y += trace/
|
||||
target-obj-y += trace/
|
||||
|
||||
######################################################################
|
||||
# guest agent
|
||||
|
||||
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
|
||||
# by libqemuutil.a. These should be moved to a separate .json schema.
|
||||
qga-obj-y = qga/
|
||||
qga-obj-y = qga/ qapi-types.o qapi-visit.o
|
||||
qga-vss-dll-obj-y = qga/
|
||||
|
||||
+40
-44
@@ -16,29 +16,26 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/include
|
||||
ifdef CONFIG_USER_ONLY
|
||||
# user emulator name
|
||||
QEMU_PROG=qemu-$(TARGET_NAME)
|
||||
QEMU_PROG_BUILD = $(QEMU_PROG)
|
||||
else
|
||||
# system emulator name
|
||||
QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF)
|
||||
ifneq (,$(findstring -mwindows,$(libs_softmmu)))
|
||||
# Terminate program name with a 'w' because the linker builds a windows executable.
|
||||
QEMU_PROGW=qemu-system-$(TARGET_NAME)w$(EXESUF)
|
||||
$(QEMU_PROG): $(QEMU_PROGW)
|
||||
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
|
||||
QEMU_PROG_BUILD = $(QEMU_PROGW)
|
||||
else
|
||||
QEMU_PROG_BUILD = $(QEMU_PROG)
|
||||
endif
|
||||
endif # windows executable
|
||||
QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF)
|
||||
endif
|
||||
|
||||
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
||||
PROGS=$(QEMU_PROG)
|
||||
ifdef QEMU_PROGW
|
||||
PROGS+=$(QEMU_PROGW)
|
||||
endif
|
||||
STPFILES=
|
||||
|
||||
config-target.h: config-target.h-timestamp
|
||||
config-target.h-timestamp: config-target.mak
|
||||
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
|
||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
TARGET_TYPE=user
|
||||
@@ -49,7 +46,7 @@ endif
|
||||
$(QEMU_PROG).stp-installed: $(SRC_PATH)/trace-events
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
--binary=$(bindir)/$(QEMU_PROG) \
|
||||
--target-name=$(TARGET_NAME) \
|
||||
--target-type=$(TARGET_TYPE) \
|
||||
@@ -58,19 +55,12 @@ $(QEMU_PROG).stp-installed: $(SRC_PATH)/trace-events
|
||||
$(QEMU_PROG).stp: $(SRC_PATH)/trace-events
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
--binary=$(realpath .)/$(QEMU_PROG) \
|
||||
--target-name=$(TARGET_NAME) \
|
||||
--target-type=$(TARGET_TYPE) \
|
||||
< $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp")
|
||||
|
||||
$(QEMU_PROG)-simpletrace.stp: $(SRC_PATH)/trace-events
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=simpletrace-stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
||||
< $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
|
||||
|
||||
else
|
||||
stap:
|
||||
endif
|
||||
@@ -92,12 +82,6 @@ obj-y += disas.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal32.o
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal64.o
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal128.o
|
||||
|
||||
#########################################################
|
||||
# Linux user emulator target
|
||||
|
||||
@@ -115,8 +99,7 @@ endif #CONFIG_LINUX_USER
|
||||
|
||||
ifdef CONFIG_BSD_USER
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR) \
|
||||
-I$(SRC_PATH)/bsd-user/$(HOST_VARIANT_DIR)
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR)
|
||||
|
||||
obj-y += bsd-user/
|
||||
obj-y += gdbstub.o user-exec.o
|
||||
@@ -126,8 +109,8 @@ endif #CONFIG_BSD_USER
|
||||
#########################################################
|
||||
# System emulator target
|
||||
ifdef CONFIG_SOFTMMU
|
||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||
obj-y += qtest.o bootdevice.o
|
||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o
|
||||
obj-y += qtest.o
|
||||
obj-y += hw/
|
||||
obj-$(CONFIG_FDT) += device_tree.o
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
@@ -137,10 +120,8 @@ obj-y += dump.o
|
||||
LIBS+=$(libs_softmmu)
|
||||
|
||||
# xen support
|
||||
obj-$(CONFIG_XEN) += xen-common.o
|
||||
obj-$(CONFIG_XEN_I386) += xen-hvm.o xen-mapcache.o
|
||||
obj-$(call lnot,$(CONFIG_XEN)) += xen-common-stub.o
|
||||
obj-$(call lnot,$(CONFIG_XEN_I386)) += xen-hvm-stub.o
|
||||
obj-$(CONFIG_XEN) += xen-all.o xen-mapcache.o
|
||||
obj-$(call lnot,$(CONFIG_XEN)) += xen-stub.o
|
||||
|
||||
# Hardware support
|
||||
ifeq ($(TARGET_NAME), sparc64)
|
||||
@@ -157,27 +138,40 @@ endif # CONFIG_SOFTMMU
|
||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
||||
|
||||
dummy := $(call unnest-vars,,obj-y)
|
||||
all-obj-y := $(obj-y)
|
||||
|
||||
target-obj-y :=
|
||||
# we are making another call to unnest-vars with different vars, protect obj-y,
|
||||
# it can be overriden in subdir Makefile.objs
|
||||
obj-y-save := $(obj-y)
|
||||
|
||||
block-obj-y :=
|
||||
common-obj-y :=
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
dummy := $(call unnest-vars,,target-obj-y)
|
||||
target-obj-y-save := $(target-obj-y)
|
||||
dummy := $(call unnest-vars,.., \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
target-obj-y := $(target-obj-y-save)
|
||||
all-obj-y += $(common-obj-y)
|
||||
all-obj-y += $(target-obj-y)
|
||||
|
||||
# Now restore obj-y
|
||||
obj-y := $(obj-y-save)
|
||||
|
||||
all-obj-y = $(obj-y) $(common-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||
|
||||
# build either PROG or PROGW
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
ifndef CONFIG_HAIKU
|
||||
LIBS+=-lm
|
||||
endif
|
||||
|
||||
ifdef QEMU_PROGW
|
||||
# The linker builds a windows executable. Make also a console executable.
|
||||
$(QEMU_PROGW): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
$(call LINK,$^)
|
||||
$(QEMU_PROG): $(QEMU_PROGW)
|
||||
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
|
||||
else
|
||||
$(QEMU_PROG): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
$(call LINK,$^)
|
||||
endif
|
||||
|
||||
gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh
|
||||
$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
|
||||
@@ -198,12 +192,14 @@ endif
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
$(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
|
||||
$(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
|
||||
ifneq ($(STRIP),)
|
||||
$(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS))
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
||||
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
|
||||
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS += config-target.h
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator, accelerator interfaces
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
* Copyright (c) 2014 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 "sysemu/accel.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
int tcg_tb_size;
|
||||
static bool tcg_allowed = true;
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const TypeInfo accel_type = {
|
||||
.name = TYPE_ACCEL,
|
||||
.parent = TYPE_OBJECT,
|
||||
.class_size = sizeof(AccelClass),
|
||||
.instance_size = sizeof(AccelState),
|
||||
};
|
||||
|
||||
/* Lookup AccelClass from opt_name. Returns NULL if not found */
|
||||
static AccelClass *accel_find(const char *opt_name)
|
||||
{
|
||||
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
|
||||
AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
|
||||
g_free(class_name);
|
||||
return ac;
|
||||
}
|
||||
|
||||
static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
||||
{
|
||||
ObjectClass *oc = OBJECT_CLASS(acc);
|
||||
const char *cname = object_class_get_name(oc);
|
||||
AccelState *accel = ACCEL(object_new(cname));
|
||||
int ret;
|
||||
ms->accelerator = accel;
|
||||
*(acc->allowed) = true;
|
||||
ret = acc->init_machine(ms);
|
||||
if (ret < 0) {
|
||||
ms->accelerator = NULL;
|
||||
*(acc->allowed) = false;
|
||||
object_unref(OBJECT(accel));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int configure_accelerator(MachineState *ms)
|
||||
{
|
||||
const char *p;
|
||||
char buf[10];
|
||||
int ret;
|
||||
bool accel_initialised = false;
|
||||
bool init_failed = false;
|
||||
AccelClass *acc = NULL;
|
||||
|
||||
p = qemu_opt_get(qemu_get_machine_opts(), "accel");
|
||||
if (p == NULL) {
|
||||
/* Use the default "accelerator", tcg */
|
||||
p = "tcg";
|
||||
}
|
||||
|
||||
while (!accel_initialised && *p != '\0') {
|
||||
if (*p == ':') {
|
||||
p++;
|
||||
}
|
||||
p = get_opt_name(buf, sizeof(buf), p, ':');
|
||||
acc = accel_find(buf);
|
||||
if (!acc) {
|
||||
fprintf(stderr, "\"%s\" accelerator not found.\n", buf);
|
||||
continue;
|
||||
}
|
||||
if (acc->available && !acc->available()) {
|
||||
printf("%s not supported for this target\n",
|
||||
acc->name);
|
||||
continue;
|
||||
}
|
||||
ret = accel_init_machine(acc, ms);
|
||||
if (ret < 0) {
|
||||
init_failed = true;
|
||||
fprintf(stderr, "failed to initialize %s: %s\n",
|
||||
acc->name,
|
||||
strerror(-ret));
|
||||
} else {
|
||||
accel_initialised = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!accel_initialised) {
|
||||
if (!init_failed) {
|
||||
fprintf(stderr, "No accelerator found!\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (init_failed) {
|
||||
fprintf(stderr, "Back to %s accelerator.\n", acc->name);
|
||||
}
|
||||
|
||||
return !accel_initialised;
|
||||
}
|
||||
|
||||
|
||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->allowed = &tcg_allowed;
|
||||
}
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
|
||||
static const TypeInfo tcg_accel_type = {
|
||||
.name = TYPE_TCG_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = tcg_accel_class_init,
|
||||
};
|
||||
|
||||
static void register_accel_types(void)
|
||||
{
|
||||
type_register_static(&accel_type);
|
||||
type_register_static(&tcg_accel_type);
|
||||
}
|
||||
|
||||
type_init(register_accel_types);
|
||||
+19
-31
@@ -100,11 +100,6 @@ void aio_set_event_notifier(AioContext *ctx,
|
||||
(IOHandler *)io_read, NULL, notifier);
|
||||
}
|
||||
|
||||
bool aio_prepare(AioContext *ctx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool aio_pending(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
@@ -124,22 +119,13 @@ bool aio_pending(AioContext *ctx)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool aio_dispatch(AioContext *ctx)
|
||||
static bool aio_dispatch(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool progress = false;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call them.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for aio_poll loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* We have to walk very carefully in case qemu_aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
@@ -189,24 +175,28 @@ bool aio_dispatch(AioContext *ctx)
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool was_dispatching;
|
||||
int ret;
|
||||
bool progress;
|
||||
|
||||
was_dispatching = ctx->dispatching;
|
||||
progress = false;
|
||||
|
||||
/* aio_notify can avoid the expensive event_notifier_set if
|
||||
* everything (file descriptors, bottom halves, timers) will
|
||||
* be re-evaluated before the next blocking poll(). This is
|
||||
* already true when aio_poll is called with blocking == false;
|
||||
* if blocking == true, it is only true after poll() returns.
|
||||
*
|
||||
* If we're in a nested event loop, ctx->dispatching might be true.
|
||||
* In that case we can restore it just before returning, but we
|
||||
* have to clear it now.
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call them.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
aio_set_dispatching(ctx, !blocking);
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
progress = true;
|
||||
}
|
||||
|
||||
if (aio_dispatch(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
if (progress && !blocking) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
@@ -230,7 +220,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
/* wait until next event */
|
||||
ret = qemu_poll_ns((GPollFD *)ctx->pollfds->data,
|
||||
ctx->pollfds->len,
|
||||
blocking ? aio_compute_timeout(ctx) : 0);
|
||||
blocking ? timerlistgroup_deadline_ns(&ctx->tlg) : 0);
|
||||
|
||||
/* if we have any readable fds, dispatch event */
|
||||
if (ret > 0) {
|
||||
@@ -244,11 +234,9 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
}
|
||||
|
||||
/* Run dispatch even if there were no readable fds to run timers */
|
||||
aio_set_dispatching(ctx, true);
|
||||
if (aio_dispatch(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
aio_set_dispatching(ctx, was_dispatching);
|
||||
return progress;
|
||||
}
|
||||
|
||||
+69
-199
@@ -22,80 +22,12 @@
|
||||
|
||||
struct AioHandler {
|
||||
EventNotifier *e;
|
||||
IOHandler *io_read;
|
||||
IOHandler *io_write;
|
||||
EventNotifierHandler *io_notify;
|
||||
GPollFD pfd;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
void aio_set_fd_handler(AioContext *ctx,
|
||||
int fd,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
/* fd is a SOCKET in our case */
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->pfd.fd == fd && !node->deleted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Are we deleting the fd handler? */
|
||||
if (!io_read && !io_write) {
|
||||
if (node) {
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (ctx->walking_handlers) {
|
||||
node->deleted = 1;
|
||||
node->pfd.revents = 0;
|
||||
} else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HANDLE event;
|
||||
|
||||
if (node == NULL) {
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = g_malloc0(sizeof(AioHandler));
|
||||
node->pfd.fd = fd;
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
}
|
||||
|
||||
node->pfd.events = 0;
|
||||
if (node->io_read) {
|
||||
node->pfd.events |= G_IO_IN;
|
||||
}
|
||||
if (node->io_write) {
|
||||
node->pfd.events |= G_IO_OUT;
|
||||
}
|
||||
|
||||
node->e = &ctx->notifier;
|
||||
|
||||
/* Update handler with latest information */
|
||||
node->opaque = opaque;
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
|
||||
event = event_notifier_get_handle(&ctx->notifier);
|
||||
WSAEventSelect(node->pfd.fd, event,
|
||||
FD_READ | FD_ACCEPT | FD_CLOSE |
|
||||
FD_CONNECT | FD_WRITE | FD_OOB);
|
||||
}
|
||||
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *e,
|
||||
EventNotifierHandler *io_notify)
|
||||
@@ -144,43 +76,6 @@ void aio_set_event_notifier(AioContext *ctx,
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
bool aio_prepare(AioContext *ctx)
|
||||
{
|
||||
static struct timeval tv0;
|
||||
AioHandler *node;
|
||||
bool have_select_revents = false;
|
||||
fd_set rfds, wfds;
|
||||
|
||||
/* fill fd sets */
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->io_read) {
|
||||
FD_SET ((SOCKET)node->pfd.fd, &rfds);
|
||||
}
|
||||
if (node->io_write) {
|
||||
FD_SET ((SOCKET)node->pfd.fd, &wfds);
|
||||
}
|
||||
}
|
||||
|
||||
if (select(0, &rfds, &wfds, NULL, &tv0) > 0) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
node->pfd.revents = 0;
|
||||
if (FD_ISSET(node->pfd.fd, &rfds)) {
|
||||
node->pfd.revents |= G_IO_IN;
|
||||
have_select_revents = true;
|
||||
}
|
||||
|
||||
if (FD_ISSET(node->pfd.fd, &wfds)) {
|
||||
node->pfd.revents |= G_IO_OUT;
|
||||
have_select_revents = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return have_select_revents;
|
||||
}
|
||||
|
||||
bool aio_pending(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
@@ -189,37 +84,47 @@ bool aio_pending(AioContext *ctx)
|
||||
if (node->pfd.revents && node->io_notify) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((node->pfd.revents & G_IO_IN) && node->io_read) {
|
||||
return true;
|
||||
}
|
||||
if ((node->pfd.revents & G_IO_OUT) && node->io_write) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool progress = false;
|
||||
HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
|
||||
bool progress;
|
||||
int count;
|
||||
int timeout;
|
||||
|
||||
progress = false;
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* If there are callbacks left that have been queued, we need to call then.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/* Run timers */
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
|
||||
/*
|
||||
* Then dispatch any pending callbacks from the GSource.
|
||||
*
|
||||
* We have to walk very carefully in case qemu_aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
int revents = node->pfd.revents;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (!node->deleted &&
|
||||
(revents || event_notifier_get_handle(node->e) == event) &&
|
||||
node->io_notify) {
|
||||
if (node->pfd.revents && node->io_notify) {
|
||||
node->pfd.revents = 0;
|
||||
node->io_notify(node->e);
|
||||
|
||||
@@ -229,28 +134,6 @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
|
||||
}
|
||||
}
|
||||
|
||||
if (!node->deleted &&
|
||||
(node->io_read || node->io_write)) {
|
||||
node->pfd.revents = 0;
|
||||
if ((revents & G_IO_IN) && node->io_read) {
|
||||
node->io_read(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
if ((revents & G_IO_OUT) && node->io_write) {
|
||||
node->io_write(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/* if the next select() will return an event, we have progressed */
|
||||
if (event == event_notifier_get_handle(&ctx->notifier)) {
|
||||
WSANETWORKEVENTS ev;
|
||||
WSAEnumNetworkEvents(node->pfd.fd, event, &ev);
|
||||
if (ev.lNetworkEvents) {
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
@@ -262,47 +145,10 @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
|
||||
}
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
bool aio_dispatch(AioContext *ctx)
|
||||
{
|
||||
bool progress;
|
||||
|
||||
progress = aio_bh_poll(ctx);
|
||||
progress |= aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE);
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
return progress;
|
||||
}
|
||||
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
AioHandler *node;
|
||||
HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
|
||||
bool was_dispatching, progress, have_select_revents, first;
|
||||
int count;
|
||||
int timeout;
|
||||
|
||||
have_select_revents = aio_prepare(ctx);
|
||||
if (have_select_revents) {
|
||||
blocking = false;
|
||||
if (progress && !blocking) {
|
||||
return true;
|
||||
}
|
||||
|
||||
was_dispatching = ctx->dispatching;
|
||||
progress = false;
|
||||
|
||||
/* aio_notify can avoid the expensive event_notifier_set if
|
||||
* everything (file descriptors, bottom halves, timers) will
|
||||
* be re-evaluated before the next blocking poll(). This is
|
||||
* already true when aio_poll is called with blocking == false;
|
||||
* if blocking == true, it is only true after poll() returns.
|
||||
*
|
||||
* If we're in a nested event loop, ctx->dispatching might be true.
|
||||
* In that case we can restore it just before returning, but we
|
||||
* have to clear it now.
|
||||
*/
|
||||
aio_set_dispatching(ctx, !blocking);
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
/* fill fd sets */
|
||||
@@ -314,40 +160,64 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
}
|
||||
|
||||
ctx->walking_handlers--;
|
||||
first = true;
|
||||
|
||||
/* wait until next event */
|
||||
while (count > 0) {
|
||||
HANDLE event;
|
||||
int ret;
|
||||
|
||||
timeout = blocking
|
||||
? qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)) : 0;
|
||||
timeout = blocking ?
|
||||
qemu_timeout_ns_to_ms(timerlistgroup_deadline_ns(&ctx->tlg)) : 0;
|
||||
ret = WaitForMultipleObjects(count, events, FALSE, timeout);
|
||||
aio_set_dispatching(ctx, true);
|
||||
|
||||
if (first && aio_bh_poll(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
first = false;
|
||||
|
||||
/* if we have any signaled events, dispatch event */
|
||||
event = NULL;
|
||||
if ((DWORD) (ret - WAIT_OBJECT_0) < count) {
|
||||
event = events[ret - WAIT_OBJECT_0];
|
||||
events[ret - WAIT_OBJECT_0] = events[--count];
|
||||
} else if (!have_select_revents) {
|
||||
if ((DWORD) (ret - WAIT_OBJECT_0) >= count) {
|
||||
break;
|
||||
}
|
||||
|
||||
have_select_revents = false;
|
||||
blocking = false;
|
||||
|
||||
progress |= aio_dispatch_handlers(ctx, event);
|
||||
/* we have to walk very carefully in case
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (!node->deleted &&
|
||||
event_notifier_get_handle(node->e) == events[ret - WAIT_OBJECT_0] &&
|
||||
node->io_notify) {
|
||||
node->io_notify(node->e);
|
||||
|
||||
/* aio_notify() does not count as progress */
|
||||
if (node->e != &ctx->notifier) {
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try again, but only call each handler once. */
|
||||
events[ret - WAIT_OBJECT_0] = events[--count];
|
||||
}
|
||||
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
if (blocking) {
|
||||
/* Run the timers a second time. We do this because otherwise aio_wait
|
||||
* will not note progress - and will stop a drain early - if we have
|
||||
* a timer that was not ready to run entering g_poll but is ready
|
||||
* after g_poll. This will only do anything if a timer has expired.
|
||||
*/
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
}
|
||||
|
||||
aio_set_dispatching(ctx, was_dispatching);
|
||||
return progress;
|
||||
}
|
||||
|
||||
+176
-237
@@ -45,7 +45,6 @@
|
||||
#include "hw/audio/pcspk.h"
|
||||
#include "migration/page_cache.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
#include "exec/cpu-all.h"
|
||||
@@ -104,8 +103,6 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#elif defined(TARGET_TRICORE)
|
||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
|
||||
#endif
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
@@ -113,8 +110,6 @@ static bool mig_throttle_on;
|
||||
static int dirty_rate_high_cnt;
|
||||
static void check_guest_throttling(void);
|
||||
|
||||
static uint64_t bitmap_sync_count;
|
||||
|
||||
/***********************************************************/
|
||||
/* ram save/restore */
|
||||
|
||||
@@ -172,8 +167,11 @@ static struct {
|
||||
/* Cache for XBZRLE, Protected by lock. */
|
||||
PageCache *cache;
|
||||
QemuMutex lock;
|
||||
} XBZRLE;
|
||||
|
||||
} XBZRLE = {
|
||||
.encoded_buf = NULL,
|
||||
.current_buf = NULL,
|
||||
.cache = NULL,
|
||||
};
|
||||
/* buffer used for XBZRLE decoding */
|
||||
static uint8_t *xbzrle_decoded_buf;
|
||||
|
||||
@@ -189,44 +187,41 @@ static void XBZRLE_cache_unlock(void)
|
||||
qemu_mutex_unlock(&XBZRLE.lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* called from qmp_migrate_set_cache_size in main thread, possibly while
|
||||
* a migration is in progress.
|
||||
* A running migration maybe using the cache and might finish during this
|
||||
* call, hence changes to the cache are protected by XBZRLE.lock().
|
||||
*/
|
||||
int64_t xbzrle_cache_resize(int64_t new_size)
|
||||
{
|
||||
PageCache *new_cache;
|
||||
int64_t ret;
|
||||
PageCache *new_cache, *cache_to_free;
|
||||
|
||||
if (new_size < TARGET_PAGE_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
|
||||
/* no need to lock, the current thread holds qemu big lock */
|
||||
if (XBZRLE.cache != NULL) {
|
||||
/* check XBZRLE.cache again later */
|
||||
if (pow2floor(new_size) == migrate_xbzrle_cache_size()) {
|
||||
goto out_new_size;
|
||||
return pow2floor(new_size);
|
||||
}
|
||||
new_cache = cache_init(new_size / TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (!new_cache) {
|
||||
error_report("Error creating cache");
|
||||
ret = -1;
|
||||
goto out;
|
||||
DPRINTF("Error creating cache\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cache_fini(XBZRLE.cache);
|
||||
XBZRLE.cache = new_cache;
|
||||
XBZRLE_cache_lock();
|
||||
/* the XBZRLE.cache may have be destroyed, check it again */
|
||||
if (XBZRLE.cache != NULL) {
|
||||
cache_to_free = XBZRLE.cache;
|
||||
XBZRLE.cache = new_cache;
|
||||
} else {
|
||||
cache_to_free = new_cache;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
|
||||
cache_fini(cache_to_free);
|
||||
}
|
||||
|
||||
out_new_size:
|
||||
ret = pow2floor(new_size);
|
||||
out:
|
||||
XBZRLE_cache_unlock();
|
||||
return ret;
|
||||
return pow2floor(new_size);
|
||||
}
|
||||
|
||||
/* accounting for migration statistics */
|
||||
@@ -238,7 +233,6 @@ typedef struct AccountingInfo {
|
||||
uint64_t xbzrle_bytes;
|
||||
uint64_t xbzrle_pages;
|
||||
uint64_t xbzrle_cache_miss;
|
||||
double xbzrle_cache_miss_rate;
|
||||
uint64_t xbzrle_overflows;
|
||||
} AccountingInfo;
|
||||
|
||||
@@ -294,11 +288,6 @@ uint64_t xbzrle_mig_pages_cache_miss(void)
|
||||
return acct_info.xbzrle_cache_miss;
|
||||
}
|
||||
|
||||
double xbzrle_mig_cache_miss_rate(void)
|
||||
{
|
||||
return acct_info.xbzrle_cache_miss_rate;
|
||||
}
|
||||
|
||||
uint64_t xbzrle_mig_pages_overflow(void)
|
||||
{
|
||||
return acct_info.xbzrle_overflows;
|
||||
@@ -351,7 +340,7 @@ static void xbzrle_cache_zero_page(ram_addr_t current_addr)
|
||||
|
||||
#define ENCODING_FLAG_XBZRLE 0x1
|
||||
|
||||
static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
|
||||
static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
||||
ram_addr_t current_addr, RAMBlock *block,
|
||||
ram_addr_t offset, int cont, bool last_stage)
|
||||
{
|
||||
@@ -359,23 +348,19 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
|
||||
uint8_t *prev_cached_page;
|
||||
|
||||
if (!cache_is_cached(XBZRLE.cache, current_addr)) {
|
||||
acct_info.xbzrle_cache_miss++;
|
||||
if (!last_stage) {
|
||||
if (cache_insert(XBZRLE.cache, current_addr, *current_data) == -1) {
|
||||
if (cache_insert(XBZRLE.cache, current_addr, current_data) == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
/* update *current_data when the page has been
|
||||
inserted into cache */
|
||||
*current_data = get_cached_data(XBZRLE.cache, current_addr);
|
||||
}
|
||||
}
|
||||
acct_info.xbzrle_cache_miss++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);
|
||||
|
||||
/* save current buffer into memory */
|
||||
memcpy(XBZRLE.current_buf, *current_data, TARGET_PAGE_SIZE);
|
||||
memcpy(XBZRLE.current_buf, current_data, TARGET_PAGE_SIZE);
|
||||
|
||||
/* XBZRLE encoding (if there is no overflow) */
|
||||
encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf,
|
||||
@@ -388,10 +373,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
|
||||
DPRINTF("Overflow\n");
|
||||
acct_info.xbzrle_overflows++;
|
||||
/* update data in the cache */
|
||||
if (!last_stage) {
|
||||
memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE);
|
||||
*current_data = prev_cached_page;
|
||||
}
|
||||
memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -486,29 +468,17 @@ static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length)
|
||||
|
||||
|
||||
/* Needs iothread lock! */
|
||||
/* Fix me: there are too many global variables used in migration process. */
|
||||
static int64_t start_time;
|
||||
static int64_t bytes_xfer_prev;
|
||||
static int64_t num_dirty_pages_period;
|
||||
|
||||
static void migration_bitmap_sync_init(void)
|
||||
{
|
||||
start_time = 0;
|
||||
bytes_xfer_prev = 0;
|
||||
num_dirty_pages_period = 0;
|
||||
}
|
||||
|
||||
static void migration_bitmap_sync(void)
|
||||
{
|
||||
RAMBlock *block;
|
||||
uint64_t num_dirty_pages_init = migration_dirty_pages;
|
||||
MigrationState *s = migrate_get_current();
|
||||
static int64_t start_time;
|
||||
static int64_t bytes_xfer_prev;
|
||||
static int64_t num_dirty_pages_period;
|
||||
int64_t end_time;
|
||||
int64_t bytes_xfer_now;
|
||||
static uint64_t xbzrle_cache_miss_prev;
|
||||
static uint64_t iterations_prev;
|
||||
|
||||
bitmap_sync_count++;
|
||||
|
||||
if (!bytes_xfer_prev) {
|
||||
bytes_xfer_prev = ram_bytes_transferred();
|
||||
@@ -550,113 +520,29 @@ static void migration_bitmap_sync(void)
|
||||
} else {
|
||||
mig_throttle_on = false;
|
||||
}
|
||||
if (migrate_use_xbzrle()) {
|
||||
if (iterations_prev != 0) {
|
||||
acct_info.xbzrle_cache_miss_rate =
|
||||
(double)(acct_info.xbzrle_cache_miss -
|
||||
xbzrle_cache_miss_prev) /
|
||||
(acct_info.iterations - iterations_prev);
|
||||
}
|
||||
iterations_prev = acct_info.iterations;
|
||||
xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss;
|
||||
}
|
||||
s->dirty_pages_rate = num_dirty_pages_period * 1000
|
||||
/ (end_time - start_time);
|
||||
s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE;
|
||||
start_time = end_time;
|
||||
num_dirty_pages_period = 0;
|
||||
s->dirty_sync_count = bitmap_sync_count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ram_save_page: Send the given page to the stream
|
||||
*
|
||||
* Returns: Number of bytes written.
|
||||
*/
|
||||
static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset,
|
||||
bool last_stage)
|
||||
{
|
||||
int bytes_sent;
|
||||
int cont;
|
||||
ram_addr_t current_addr;
|
||||
MemoryRegion *mr = block->mr;
|
||||
uint8_t *p;
|
||||
int ret;
|
||||
bool send_async = true;
|
||||
|
||||
cont = (block == last_sent_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
|
||||
|
||||
p = memory_region_get_ram_ptr(mr) + offset;
|
||||
|
||||
/* In doubt sent page as normal */
|
||||
bytes_sent = -1;
|
||||
ret = ram_control_save_page(f, block->offset,
|
||||
offset, TARGET_PAGE_SIZE, &bytes_sent);
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
|
||||
current_addr = block->offset + offset;
|
||||
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
||||
if (bytes_sent > 0) {
|
||||
acct_info.norm_pages++;
|
||||
} else if (bytes_sent == 0) {
|
||||
acct_info.dup_pages++;
|
||||
}
|
||||
}
|
||||
} else if (is_zero_range(p, TARGET_PAGE_SIZE)) {
|
||||
acct_info.dup_pages++;
|
||||
bytes_sent = save_block_hdr(f, block, offset, cont,
|
||||
RAM_SAVE_FLAG_COMPRESS);
|
||||
qemu_put_byte(f, 0);
|
||||
bytes_sent++;
|
||||
/* Must let xbzrle know, otherwise a previous (now 0'd) cached
|
||||
* page would be stale
|
||||
*/
|
||||
xbzrle_cache_zero_page(current_addr);
|
||||
} else if (!ram_bulk_stage && migrate_use_xbzrle()) {
|
||||
bytes_sent = save_xbzrle_page(f, &p, current_addr, block,
|
||||
offset, cont, last_stage);
|
||||
if (!last_stage) {
|
||||
/* Can't send this cached data async, since the cache page
|
||||
* might get updated before it gets to the wire
|
||||
*/
|
||||
send_async = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* XBZRLE overflow or normal page */
|
||||
if (bytes_sent == -1) {
|
||||
bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
|
||||
if (send_async) {
|
||||
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
|
||||
} else {
|
||||
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
|
||||
}
|
||||
bytes_sent += TARGET_PAGE_SIZE;
|
||||
acct_info.norm_pages++;
|
||||
}
|
||||
|
||||
XBZRLE_cache_unlock();
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* ram_find_and_save_block: Finds a page to send and sends it to f
|
||||
* ram_save_block: Writes a page of memory to the stream f
|
||||
*
|
||||
* Returns: The number of bytes written.
|
||||
* 0 means no dirty pages
|
||||
*/
|
||||
|
||||
static int ram_find_and_save_block(QEMUFile *f, bool last_stage)
|
||||
static int ram_save_block(QEMUFile *f, bool last_stage)
|
||||
{
|
||||
RAMBlock *block = last_seen_block;
|
||||
ram_addr_t offset = last_offset;
|
||||
bool complete_round = false;
|
||||
int bytes_sent = 0;
|
||||
MemoryRegion *mr;
|
||||
ram_addr_t current_addr;
|
||||
|
||||
if (!block)
|
||||
block = QTAILQ_FIRST(&ram_list.blocks);
|
||||
@@ -677,8 +563,70 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage)
|
||||
ram_bulk_stage = false;
|
||||
}
|
||||
} else {
|
||||
bytes_sent = ram_save_page(f, block, offset, last_stage);
|
||||
int ret;
|
||||
uint8_t *p;
|
||||
bool send_async = true;
|
||||
int cont = (block == last_sent_block) ?
|
||||
RAM_SAVE_FLAG_CONTINUE : 0;
|
||||
|
||||
p = memory_region_get_ram_ptr(mr) + offset;
|
||||
|
||||
/* In doubt sent page as normal */
|
||||
bytes_sent = -1;
|
||||
ret = ram_control_save_page(f, block->offset,
|
||||
offset, TARGET_PAGE_SIZE, &bytes_sent);
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
|
||||
current_addr = block->offset + offset;
|
||||
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
||||
if (bytes_sent > 0) {
|
||||
acct_info.norm_pages++;
|
||||
} else if (bytes_sent == 0) {
|
||||
acct_info.dup_pages++;
|
||||
}
|
||||
}
|
||||
} else if (is_zero_range(p, TARGET_PAGE_SIZE)) {
|
||||
acct_info.dup_pages++;
|
||||
bytes_sent = save_block_hdr(f, block, offset, cont,
|
||||
RAM_SAVE_FLAG_COMPRESS);
|
||||
qemu_put_byte(f, 0);
|
||||
bytes_sent++;
|
||||
/* Must let xbzrle know, otherwise a previous (now 0'd) cached
|
||||
* page would be stale
|
||||
*/
|
||||
xbzrle_cache_zero_page(current_addr);
|
||||
} else if (!ram_bulk_stage && migrate_use_xbzrle()) {
|
||||
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
|
||||
offset, cont, last_stage);
|
||||
if (!last_stage) {
|
||||
/* We must send exactly what's in the xbzrle cache
|
||||
* even if the page wasn't xbzrle compressed, so that
|
||||
* it's right next time.
|
||||
*/
|
||||
p = get_cached_data(XBZRLE.cache, current_addr);
|
||||
|
||||
/* Can't send this cached data async, since the cache page
|
||||
* might get updated before it gets to the wire
|
||||
*/
|
||||
send_async = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* XBZRLE overflow or normal page */
|
||||
if (bytes_sent == -1) {
|
||||
bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
|
||||
if (send_async) {
|
||||
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
|
||||
} else {
|
||||
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
|
||||
}
|
||||
bytes_sent += TARGET_PAGE_SIZE;
|
||||
acct_info.norm_pages++;
|
||||
}
|
||||
|
||||
XBZRLE_cache_unlock();
|
||||
/* if page is unmodified, continue to the next */
|
||||
if (bytes_sent > 0) {
|
||||
last_sent_block = block;
|
||||
@@ -749,6 +697,7 @@ static void migration_end(void)
|
||||
XBZRLE_cache_lock();
|
||||
if (XBZRLE.cache) {
|
||||
cache_fini(XBZRLE.cache);
|
||||
g_free(XBZRLE.cache);
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
g_free(XBZRLE.current_buf);
|
||||
XBZRLE.cache = NULL;
|
||||
@@ -777,35 +726,37 @@ static void reset_ram_globals(void)
|
||||
static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||
{
|
||||
RAMBlock *block;
|
||||
int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */
|
||||
int64_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||
|
||||
migration_bitmap = bitmap_new(ram_pages);
|
||||
bitmap_set(migration_bitmap, 0, ram_pages);
|
||||
migration_dirty_pages = ram_pages;
|
||||
mig_throttle_on = false;
|
||||
dirty_rate_high_cnt = 0;
|
||||
bitmap_sync_count = 0;
|
||||
migration_bitmap_sync_init();
|
||||
|
||||
if (migrate_use_xbzrle()) {
|
||||
XBZRLE_cache_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
||||
TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.cache) {
|
||||
XBZRLE_cache_unlock();
|
||||
error_report("Error creating cache");
|
||||
qemu_mutex_unlock_iothread();
|
||||
DPRINTF("Error creating cache\n");
|
||||
return -1;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
qemu_mutex_init(&XBZRLE.lock);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
/* We prefer not to abort if there is no memory */
|
||||
XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.encoded_buf) {
|
||||
error_report("Error allocating encoded_buf");
|
||||
DPRINTF("Error allocating encoded_buf\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.current_buf) {
|
||||
error_report("Error allocating current_buf");
|
||||
DPRINTF("Error allocating current_buf\n");
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
return -1;
|
||||
@@ -819,22 +770,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||
bytes_transferred = 0;
|
||||
reset_ram_globals();
|
||||
|
||||
ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||
migration_bitmap = bitmap_new(ram_bitmap_pages);
|
||||
bitmap_set(migration_bitmap, 0, ram_bitmap_pages);
|
||||
|
||||
/*
|
||||
* Count the total number of pages used by ram blocks not including any
|
||||
* gaps due to alignment or unplugs.
|
||||
*/
|
||||
migration_dirty_pages = 0;
|
||||
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
|
||||
uint64_t block_pages;
|
||||
|
||||
block_pages = block->length >> TARGET_PAGE_BITS;
|
||||
migration_dirty_pages += block_pages;
|
||||
}
|
||||
|
||||
memory_global_dirty_log_start();
|
||||
migration_bitmap_sync();
|
||||
qemu_mutex_unlock_iothread();
|
||||
@@ -877,7 +812,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||
while ((ret = qemu_file_rate_limit(f)) == 0) {
|
||||
int bytes_sent;
|
||||
|
||||
bytes_sent = ram_find_and_save_block(f, false);
|
||||
bytes_sent = ram_save_block(f, false);
|
||||
/* no more blocks to sent */
|
||||
if (bytes_sent == 0) {
|
||||
break;
|
||||
@@ -939,7 +874,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||
while (true) {
|
||||
int bytes_sent;
|
||||
|
||||
bytes_sent = ram_find_and_save_block(f, true);
|
||||
bytes_sent = ram_save_block(f, true);
|
||||
/* no more blocks to sent */
|
||||
if (bytes_sent == 0) {
|
||||
break;
|
||||
@@ -973,6 +908,7 @@ static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
|
||||
|
||||
static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
|
||||
{
|
||||
int ret, rc = 0;
|
||||
unsigned int xh_len;
|
||||
int xh_flags;
|
||||
|
||||
@@ -985,25 +921,30 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
|
||||
xh_len = qemu_get_be16(f);
|
||||
|
||||
if (xh_flags != ENCODING_FLAG_XBZRLE) {
|
||||
error_report("Failed to load XBZRLE page - wrong compression!");
|
||||
fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xh_len > TARGET_PAGE_SIZE) {
|
||||
error_report("Failed to load XBZRLE page - len overflow!");
|
||||
fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n");
|
||||
return -1;
|
||||
}
|
||||
/* load data and decode */
|
||||
qemu_get_buffer(f, xbzrle_decoded_buf, xh_len);
|
||||
|
||||
/* decode RLE */
|
||||
if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host,
|
||||
TARGET_PAGE_SIZE) == -1) {
|
||||
error_report("Failed to load XBZRLE page - decode error!");
|
||||
return -1;
|
||||
ret = xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
|
||||
rc = -1;
|
||||
} else if (ret > TARGET_PAGE_SIZE) {
|
||||
fprintf(stderr, "Failed to load XBZRLE page - size %d exceeds %d!\n",
|
||||
ret, TARGET_PAGE_SIZE);
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
@@ -1015,8 +956,8 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
uint8_t len;
|
||||
|
||||
if (flags & RAM_SAVE_FLAG_CONTINUE) {
|
||||
if (!block || block->length <= offset) {
|
||||
error_report("Ack, bad migration stream!");
|
||||
if (!block) {
|
||||
fprintf(stderr, "Ack, bad migration stream!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1028,12 +969,11 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
id[len] = 0;
|
||||
|
||||
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
|
||||
if (!strncmp(id, block->idstr, sizeof(id)) && block->length > offset) {
|
||||
if (!strncmp(id, block->idstr, sizeof(id)))
|
||||
return memory_region_get_ram_ptr(block->mr) + offset;
|
||||
}
|
||||
}
|
||||
|
||||
error_report("Can't find block %s!", id);
|
||||
fprintf(stderr, "Can't find block %s!\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1050,7 +990,8 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
|
||||
|
||||
static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
int flags = 0, ret = 0;
|
||||
ram_addr_t addr;
|
||||
int flags, ret = 0;
|
||||
static uint64_t seq_iter;
|
||||
|
||||
seq_iter++;
|
||||
@@ -1059,24 +1000,21 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
|
||||
ram_addr_t addr, total_ram_bytes;
|
||||
void *host;
|
||||
uint8_t ch;
|
||||
|
||||
while (!ret) {
|
||||
addr = qemu_get_be64(f);
|
||||
|
||||
flags = addr & ~TARGET_PAGE_MASK;
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
|
||||
switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
|
||||
case RAM_SAVE_FLAG_MEM_SIZE:
|
||||
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
|
||||
/* Synchronize RAM block list */
|
||||
total_ram_bytes = addr;
|
||||
while (!ret && total_ram_bytes) {
|
||||
char id[256];
|
||||
ram_addr_t length;
|
||||
ram_addr_t total_ram_bytes = addr;
|
||||
|
||||
while (total_ram_bytes) {
|
||||
RAMBlock *block;
|
||||
uint8_t len;
|
||||
char id[256];
|
||||
ram_addr_t length;
|
||||
|
||||
len = qemu_get_byte(f);
|
||||
qemu_get_buffer(f, (uint8_t *)id, len);
|
||||
@@ -1086,9 +1024,10 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
|
||||
if (!strncmp(id, block->idstr, sizeof(id))) {
|
||||
if (block->length != length) {
|
||||
error_report("Length mismatch: %s: 0x" RAM_ADDR_FMT
|
||||
" in != 0x" RAM_ADDR_FMT, id, length,
|
||||
block->length);
|
||||
fprintf(stderr,
|
||||
"Length mismatch: %s: " RAM_ADDR_FMT
|
||||
" in != " RAM_ADDR_FMT "\n", id, length,
|
||||
block->length);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
@@ -1096,15 +1035,20 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
}
|
||||
|
||||
if (!block) {
|
||||
error_report("Unknown ramblock \"%s\", cannot "
|
||||
"accept migration", id);
|
||||
fprintf(stderr, "Unknown ramblock \"%s\", cannot "
|
||||
"accept migration\n", id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
total_ram_bytes -= length;
|
||||
}
|
||||
break;
|
||||
case RAM_SAVE_FLAG_COMPRESS:
|
||||
} else if (flags & RAM_SAVE_FLAG_COMPRESS) {
|
||||
void *host;
|
||||
uint8_t ch;
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
@@ -1114,8 +1058,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
||||
ch = qemu_get_byte(f);
|
||||
ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
|
||||
break;
|
||||
case RAM_SAVE_FLAG_PAGE:
|
||||
} else if (flags & RAM_SAVE_FLAG_PAGE) {
|
||||
void *host;
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
@@ -1124,9 +1069,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
}
|
||||
|
||||
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
|
||||
break;
|
||||
case RAM_SAVE_FLAG_XBZRLE:
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
|
||||
void *host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
ret = -EINVAL;
|
||||
@@ -1139,22 +1083,17 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RAM_SAVE_FLAG_EOS:
|
||||
} else if (flags & RAM_SAVE_FLAG_HOOK) {
|
||||
ram_control_load_hook(f, flags);
|
||||
} else if (flags & RAM_SAVE_FLAG_EOS) {
|
||||
/* normal exit */
|
||||
break;
|
||||
default:
|
||||
if (flags & RAM_SAVE_FLAG_HOOK) {
|
||||
ram_control_load_hook(f, flags);
|
||||
} else {
|
||||
error_report("Unknown combination of migration flags: %#x",
|
||||
flags);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
ret = qemu_file_get_error(f);
|
||||
} else {
|
||||
error_report("Unknown migration flags: %#x", flags);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = qemu_file_get_error(f);
|
||||
}
|
||||
|
||||
DPRINTF("Completed load of VM with exit code %d seq iteration "
|
||||
@@ -1162,7 +1101,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SaveVMHandlers savevm_ram_handlers = {
|
||||
SaveVMHandlers savevm_ram_handlers = {
|
||||
.save_live_setup = ram_save_setup,
|
||||
.save_live_iterate = ram_save_iterate,
|
||||
.save_live_complete = ram_save_complete,
|
||||
@@ -1171,12 +1110,6 @@ static SaveVMHandlers savevm_ram_handlers = {
|
||||
.cancel = ram_migration_cancel,
|
||||
};
|
||||
|
||||
void ram_mig_init(void)
|
||||
{
|
||||
qemu_mutex_init(&XBZRLE.lock);
|
||||
register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);
|
||||
}
|
||||
|
||||
struct soundhw {
|
||||
const char *name;
|
||||
const char *descr;
|
||||
@@ -1259,11 +1192,12 @@ void select_soundhw(const char *optarg)
|
||||
|
||||
if (!c->name) {
|
||||
if (l > 80) {
|
||||
error_report("Unknown sound card name (too big to show)");
|
||||
fprintf(stderr,
|
||||
"Unknown sound card name (too big to show)\n");
|
||||
}
|
||||
else {
|
||||
error_report("Unknown sound card name `%.*s'",
|
||||
(int) l, p);
|
||||
fprintf(stderr, "Unknown sound card name `%.*s'\n",
|
||||
(int) l, p);
|
||||
}
|
||||
bad_card = 1;
|
||||
}
|
||||
@@ -1286,13 +1220,13 @@ void audio_init(void)
|
||||
if (c->enabled) {
|
||||
if (c->isa) {
|
||||
if (!isa_bus) {
|
||||
error_report("ISA bus not available for %s", c->name);
|
||||
fprintf(stderr, "ISA bus not available for %s\n", c->name);
|
||||
exit(1);
|
||||
}
|
||||
c->init.init_isa(isa_bus);
|
||||
} else {
|
||||
if (!pci_bus) {
|
||||
error_report("PCI bus not available for %s", c->name);
|
||||
fprintf(stderr, "PCI bus not available for %s\n", c->name);
|
||||
exit(1);
|
||||
}
|
||||
c->init.init_pci(pci_bus);
|
||||
@@ -1349,6 +1283,11 @@ void cpudef_init(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
int tcg_available(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_available(void)
|
||||
{
|
||||
#ifdef CONFIG_KVM
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "block/aio.h"
|
||||
#include "block/thread-pool.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
/***********************************************************/
|
||||
/* bottom halves (can be seen as timers which expire ASAP) */
|
||||
@@ -152,48 +151,39 @@ void qemu_bh_delete(QEMUBH *bh)
|
||||
bh->deleted = 1;
|
||||
}
|
||||
|
||||
int64_t
|
||||
aio_compute_timeout(AioContext *ctx)
|
||||
static gboolean
|
||||
aio_ctx_prepare(GSource *source, gint *timeout)
|
||||
{
|
||||
int64_t deadline;
|
||||
int timeout = -1;
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
QEMUBH *bh;
|
||||
int deadline;
|
||||
|
||||
/* We assume there is no timeout already supplied */
|
||||
*timeout = -1;
|
||||
for (bh = ctx->first_bh; bh; bh = bh->next) {
|
||||
if (!bh->deleted && bh->scheduled) {
|
||||
if (bh->idle) {
|
||||
/* idle bottom halves will be polled at least
|
||||
* every 10ms */
|
||||
timeout = 10000000;
|
||||
*timeout = 10;
|
||||
} else {
|
||||
/* non-idle bottom halves will be executed
|
||||
* immediately */
|
||||
return 0;
|
||||
*timeout = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deadline = timerlistgroup_deadline_ns(&ctx->tlg);
|
||||
deadline = qemu_timeout_ns_to_ms(timerlistgroup_deadline_ns(&ctx->tlg));
|
||||
if (deadline == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return qemu_soonest_timeout(timeout, deadline);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
aio_ctx_prepare(GSource *source, gint *timeout)
|
||||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
|
||||
/* We assume there is no timeout already supplied */
|
||||
*timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx));
|
||||
|
||||
if (aio_prepare(ctx)) {
|
||||
*timeout = 0;
|
||||
return true;
|
||||
} else {
|
||||
*timeout = qemu_soonest_timeout(*timeout, deadline);
|
||||
}
|
||||
|
||||
return *timeout == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -218,7 +208,7 @@ aio_ctx_dispatch(GSource *source,
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
|
||||
assert(callback == NULL);
|
||||
aio_dispatch(ctx);
|
||||
aio_poll(ctx, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -257,25 +247,9 @@ ThreadPool *aio_get_thread_pool(AioContext *ctx)
|
||||
return ctx->thread_pool;
|
||||
}
|
||||
|
||||
void aio_set_dispatching(AioContext *ctx, bool dispatching)
|
||||
{
|
||||
ctx->dispatching = dispatching;
|
||||
if (!dispatching) {
|
||||
/* Write ctx->dispatching before reading e.g. bh->scheduled.
|
||||
* Optimization: this is only needed when we're entering the "unsafe"
|
||||
* phase where other threads must call event_notifier_set.
|
||||
*/
|
||||
smp_mb();
|
||||
}
|
||||
}
|
||||
|
||||
void aio_notify(AioContext *ctx)
|
||||
{
|
||||
/* Write e.g. bh->scheduled before reading ctx->dispatching. */
|
||||
smp_mb();
|
||||
if (!ctx->dispatching) {
|
||||
event_notifier_set(&ctx->notifier);
|
||||
}
|
||||
event_notifier_set(&ctx->notifier);
|
||||
}
|
||||
|
||||
static void aio_timerlist_notify(void *opaque)
|
||||
@@ -289,24 +263,18 @@ static void aio_rfifolock_cb(void *opaque)
|
||||
aio_notify(opaque);
|
||||
}
|
||||
|
||||
AioContext *aio_context_new(Error **errp)
|
||||
AioContext *aio_context_new(void)
|
||||
{
|
||||
int ret;
|
||||
AioContext *ctx;
|
||||
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
|
||||
ret = event_notifier_init(&ctx->notifier, false);
|
||||
if (ret < 0) {
|
||||
g_source_destroy(&ctx->source);
|
||||
error_setg_errno(errp, -ret, "Failed to initialize event notifier");
|
||||
return NULL;
|
||||
}
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_test_and_clear);
|
||||
ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
|
||||
ctx->thread_pool = NULL;
|
||||
qemu_mutex_init(&ctx->bh_lock);
|
||||
rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
|
||||
event_notifier_init(&ctx->notifier, false);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_test_and_clear);
|
||||
timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
|
||||
|
||||
return ctx;
|
||||
|
||||
+1
-1
@@ -14,4 +14,4 @@ common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
|
||||
common-obj-y += wavcapture.o
|
||||
|
||||
$(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
|
||||
sdlaudio.o-cflags := $(SDL_CFLAGS)
|
||||
$(obj)/sdlaudio.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
|
||||
+8
-4
@@ -815,8 +815,10 @@ static void alsa_fini_out (HWVoiceOut *hw)
|
||||
ldebug ("alsa_fini\n");
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
|
||||
g_free(alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
if (alsa->pcm_buf) {
|
||||
g_free (alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
@@ -976,8 +978,10 @@ static void alsa_fini_in (HWVoiceIn *hw)
|
||||
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
|
||||
g_free(alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
if (alsa->pcm_buf) {
|
||||
g_free (alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_run_in (HWVoiceIn *hw)
|
||||
|
||||
+2
-1
@@ -1812,7 +1812,8 @@ static const VMStateDescription vmstate_audio = {
|
||||
.name = "audio",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
+12
-5
@@ -71,7 +71,10 @@ static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
|
||||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
g_free (HWBUF);
|
||||
if (HWBUF) {
|
||||
g_free (HWBUF);
|
||||
}
|
||||
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
||||
@@ -89,7 +92,9 @@ static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
|
||||
|
||||
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
g_free (sw->buf);
|
||||
if (sw->buf) {
|
||||
g_free (sw->buf);
|
||||
}
|
||||
|
||||
if (sw->rate) {
|
||||
st_rate_stop (sw->rate);
|
||||
@@ -167,8 +172,10 @@ static int glue (audio_pcm_sw_init_, TYPE) (
|
||||
static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
|
||||
{
|
||||
glue (audio_pcm_sw_free_resources_, TYPE) (sw);
|
||||
g_free (sw->name);
|
||||
sw->name = NULL;
|
||||
if (sw->name) {
|
||||
g_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
|
||||
@@ -191,9 +198,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
|
||||
audio_detach_capture (hw);
|
||||
#endif
|
||||
QLIST_REMOVE (hw, entries);
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
glue (s->nb_hw_voices_, TYPE) += 1;
|
||||
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
g_free (hw);
|
||||
*hwp = NULL;
|
||||
}
|
||||
|
||||
+4
-2
@@ -736,8 +736,10 @@ static void oss_fini_in (HWVoiceIn *hw)
|
||||
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
g_free(oss->pcm_buf);
|
||||
oss->pcm_buf = NULL;
|
||||
if (oss->pcm_buf) {
|
||||
g_free (oss->pcm_buf);
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_run_in (HWVoiceIn *hw)
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
|
||||
bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
|
||||
samples = (bytes - rate->bytes_sent) >> info->shift;
|
||||
if (samples < 0 || samples > 65536) {
|
||||
error_report("Resetting rate control (%" PRId64 " samples)", samples);
|
||||
fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
|
||||
rate_start (rate);
|
||||
samples = 0;
|
||||
}
|
||||
|
||||
+2
-1
@@ -63,7 +63,8 @@ static void wav_destroy (void *opaque)
|
||||
}
|
||||
doclose:
|
||||
if (fclose (wav->f)) {
|
||||
error_report("wav_destroy: fclose failed: %s", strerror(errno));
|
||||
fprintf (stderr, "wav_destroy: fclose failed: %s",
|
||||
strerror (errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
common-obj-y += rng.o rng-egd.o
|
||||
common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
|
||||
common-obj-y += msmouse.o testdev.o
|
||||
common-obj-y += msmouse.o
|
||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||
baum.o-cflags := $(SDL_CFLAGS)
|
||||
$(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += hostmem.o hostmem-ram.o
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
||||
|
||||
+2
-2
@@ -574,7 +574,7 @@ CharDriverState *chr_baum_init(void)
|
||||
int tty;
|
||||
|
||||
baum = g_malloc0(sizeof(BaumDriverState));
|
||||
baum->chr = chr = qemu_chr_alloc();
|
||||
baum->chr = chr = g_malloc0(sizeof(CharDriverState));
|
||||
|
||||
chr->opaque = baum;
|
||||
chr->chr_write = baum_write;
|
||||
@@ -629,7 +629,7 @@ fail_handle:
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL);
|
||||
register_char_driver_qapi("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* QEMU Host Memory Backend for hugetlbfs
|
||||
*
|
||||
* Copyright (C) 2013-2014 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
/* hostmem-file.c */
|
||||
/**
|
||||
* @TYPE_MEMORY_BACKEND_FILE:
|
||||
* name of backend that uses mmap on a file descriptor
|
||||
*/
|
||||
#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file"
|
||||
|
||||
#define MEMORY_BACKEND_FILE(obj) \
|
||||
OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE)
|
||||
|
||||
typedef struct HostMemoryBackendFile HostMemoryBackendFile;
|
||||
|
||||
struct HostMemoryBackendFile {
|
||||
HostMemoryBackend parent_obj;
|
||||
|
||||
bool share;
|
||||
char *mem_path;
|
||||
};
|
||||
|
||||
static void
|
||||
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
|
||||
|
||||
if (!backend->size) {
|
||||
error_setg(errp, "can't create backend with size 0");
|
||||
return;
|
||||
}
|
||||
if (!fb->mem_path) {
|
||||
error_setg(errp, "mem_path property not set");
|
||||
return;
|
||||
}
|
||||
#ifndef CONFIG_LINUX
|
||||
error_setg(errp, "-mem-path not supported on this host");
|
||||
#else
|
||||
if (!memory_region_size(&backend->mr)) {
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
object_get_canonical_path(OBJECT(backend)),
|
||||
backend->size, fb->share,
|
||||
fb->mem_path, errp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
file_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
bc->alloc = file_backend_memory_alloc;
|
||||
}
|
||||
|
||||
static char *get_mem_path(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
return g_strdup(fb->mem_path);
|
||||
}
|
||||
|
||||
static void set_mem_path(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
if (memory_region_size(&backend->mr)) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
if (fb->mem_path) {
|
||||
g_free(fb->mem_path);
|
||||
}
|
||||
fb->mem_path = g_strdup(str);
|
||||
}
|
||||
|
||||
static bool file_memory_backend_get_share(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
return fb->share;
|
||||
}
|
||||
|
||||
static void file_memory_backend_set_share(Object *o, bool value, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
if (memory_region_size(&backend->mr)) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
fb->share = value;
|
||||
}
|
||||
|
||||
static void
|
||||
file_backend_instance_init(Object *o)
|
||||
{
|
||||
object_property_add_bool(o, "share",
|
||||
file_memory_backend_get_share,
|
||||
file_memory_backend_set_share, NULL);
|
||||
object_property_add_str(o, "mem-path", get_mem_path,
|
||||
set_mem_path, NULL);
|
||||
}
|
||||
|
||||
static const TypeInfo file_backend_info = {
|
||||
.name = TYPE_MEMORY_BACKEND_FILE,
|
||||
.parent = TYPE_MEMORY_BACKEND,
|
||||
.class_init = file_backend_class_init,
|
||||
.instance_init = file_backend_instance_init,
|
||||
.instance_size = sizeof(HostMemoryBackendFile),
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&file_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* QEMU Host Memory Backend
|
||||
*
|
||||
* Copyright (C) 2013-2014 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Igor Mammedov <imammedo@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
|
||||
|
||||
|
||||
static void
|
||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
char *path;
|
||||
|
||||
if (!backend->size) {
|
||||
error_setg(errp, "can't create backend with size 0");
|
||||
return;
|
||||
}
|
||||
|
||||
path = object_get_canonical_path_component(OBJECT(backend));
|
||||
memory_region_init_ram(&backend->mr, OBJECT(backend), path,
|
||||
backend->size, errp);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
static void
|
||||
ram_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
bc->alloc = ram_backend_memory_alloc;
|
||||
}
|
||||
|
||||
static const TypeInfo ram_backend_info = {
|
||||
.name = TYPE_MEMORY_BACKEND_RAM,
|
||||
.parent = TYPE_MEMORY_BACKEND,
|
||||
.class_init = ram_backend_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&ram_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -1,365 +0,0 @@
|
||||
/*
|
||||
* QEMU Host Memory Backend
|
||||
*
|
||||
* Copyright (C) 2013-2014 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Igor Mammedov <imammedo@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi-types.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
#include <numaif.h>
|
||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_DEFAULT != MPOL_DEFAULT);
|
||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_PREFERRED != MPOL_PREFERRED);
|
||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
|
||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
|
||||
#endif
|
||||
|
||||
static void
|
||||
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, &value, name, errp);
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
uint64_t value;
|
||||
|
||||
if (memory_region_size(&backend->mr)) {
|
||||
error_setg(&local_err, "cannot change property value");
|
||||
goto out;
|
||||
}
|
||||
|
||||
visit_type_size(v, &value, name, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
if (!value) {
|
||||
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
|
||||
PRIu64 "'", object_get_typename(obj), name, value);
|
||||
goto out;
|
||||
}
|
||||
backend->size = value;
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
uint16List **node = &host_nodes;
|
||||
unsigned long value;
|
||||
|
||||
value = find_first_bit(backend->host_nodes, MAX_NODES);
|
||||
if (value == MAX_NODES) {
|
||||
return;
|
||||
}
|
||||
|
||||
*node = g_malloc0(sizeof(**node));
|
||||
(*node)->value = value;
|
||||
node = &(*node)->next;
|
||||
|
||||
do {
|
||||
value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1);
|
||||
if (value == MAX_NODES) {
|
||||
break;
|
||||
}
|
||||
|
||||
*node = g_malloc0(sizeof(**node));
|
||||
(*node)->value = value;
|
||||
node = &(*node)->next;
|
||||
} while (true);
|
||||
|
||||
visit_type_uint16List(v, &host_nodes, name, errp);
|
||||
}
|
||||
|
||||
static void
|
||||
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, &l, name, errp);
|
||||
|
||||
while (l) {
|
||||
bitmap_set(backend->host_nodes, l->value, 1);
|
||||
l = l->next;
|
||||
}
|
||||
#else
|
||||
error_setg(errp, "NUMA node binding are not supported by this QEMU");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_get_policy(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
int policy = backend->policy;
|
||||
|
||||
visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp);
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_set_policy(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
int policy;
|
||||
|
||||
visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp);
|
||||
backend->policy = policy;
|
||||
|
||||
#ifndef CONFIG_NUMA
|
||||
if (policy != HOST_MEM_POLICY_DEFAULT) {
|
||||
error_setg(errp, "NUMA policies are not supported by this QEMU");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool host_memory_backend_get_merge(Object *obj, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
return backend->merge;
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
if (!memory_region_size(&backend->mr)) {
|
||||
backend->merge = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != backend->merge) {
|
||||
void *ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
uint64_t sz = memory_region_size(&backend->mr);
|
||||
|
||||
qemu_madvise(ptr, sz,
|
||||
value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE);
|
||||
backend->merge = value;
|
||||
}
|
||||
}
|
||||
|
||||
static bool host_memory_backend_get_dump(Object *obj, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
return backend->dump;
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
if (!memory_region_size(&backend->mr)) {
|
||||
backend->dump = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != backend->dump) {
|
||||
void *ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
uint64_t sz = memory_region_size(&backend->mr);
|
||||
|
||||
qemu_madvise(ptr, sz,
|
||||
value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP);
|
||||
backend->dump = value;
|
||||
}
|
||||
}
|
||||
|
||||
static bool host_memory_backend_get_prealloc(Object *obj, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
return backend->prealloc || backend->force_prealloc;
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
if (backend->force_prealloc) {
|
||||
if (value) {
|
||||
error_setg(errp,
|
||||
"remove -mem-prealloc to use the prealloc property");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!memory_region_size(&backend->mr)) {
|
||||
backend->prealloc = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value && !backend->prealloc) {
|
||||
int fd = memory_region_get_fd(&backend->mr);
|
||||
void *ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
uint64_t sz = memory_region_size(&backend->mr);
|
||||
|
||||
os_mem_prealloc(fd, ptr, sz);
|
||||
backend->prealloc = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void host_memory_backend_init(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
backend->merge = qemu_opt_get_bool(qemu_get_machine_opts(),
|
||||
"mem-merge", true);
|
||||
backend->dump = qemu_opt_get_bool(qemu_get_machine_opts(),
|
||||
"dump-guest-core", true);
|
||||
backend->prealloc = mem_prealloc;
|
||||
|
||||
object_property_add_bool(obj, "merge",
|
||||
host_memory_backend_get_merge,
|
||||
host_memory_backend_set_merge, NULL);
|
||||
object_property_add_bool(obj, "dump",
|
||||
host_memory_backend_get_dump,
|
||||
host_memory_backend_set_dump, NULL);
|
||||
object_property_add_bool(obj, "prealloc",
|
||||
host_memory_backend_get_prealloc,
|
||||
host_memory_backend_set_prealloc, NULL);
|
||||
object_property_add(obj, "size", "int",
|
||||
host_memory_backend_get_size,
|
||||
host_memory_backend_set_size, NULL, NULL, NULL);
|
||||
object_property_add(obj, "host-nodes", "int",
|
||||
host_memory_backend_get_host_nodes,
|
||||
host_memory_backend_set_host_nodes, NULL, NULL, NULL);
|
||||
object_property_add(obj, "policy", "str",
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
MemoryRegion *
|
||||
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
return memory_region_size(&backend->mr) ? &backend->mr : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
|
||||
Error *local_err = NULL;
|
||||
void *ptr;
|
||||
uint64_t sz;
|
||||
|
||||
if (bc->alloc) {
|
||||
bc->alloc(backend, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
sz = memory_region_size(&backend->mr);
|
||||
|
||||
if (backend->merge) {
|
||||
qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE);
|
||||
}
|
||||
if (!backend->dump) {
|
||||
qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP);
|
||||
}
|
||||
#ifdef CONFIG_NUMA
|
||||
unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES);
|
||||
/* lastbit == MAX_NODES means maxnode = 0 */
|
||||
unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1);
|
||||
/* ensure policy won't be ignored in case memory is preallocated
|
||||
* before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so
|
||||
* this doesn't catch hugepage case. */
|
||||
unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE;
|
||||
|
||||
/* check for invalid host-nodes and policies and give more verbose
|
||||
* error messages than mbind(). */
|
||||
if (maxnode && backend->policy == MPOL_DEFAULT) {
|
||||
error_setg(errp, "host-nodes must be empty for policy default,"
|
||||
" or you should explicitly specify a policy other"
|
||||
" than default");
|
||||
return;
|
||||
} else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) {
|
||||
error_setg(errp, "host-nodes must be set for policy %s",
|
||||
HostMemPolicy_lookup[backend->policy]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We can have up to MAX_NODES nodes, but we need to pass maxnode+1
|
||||
* as argument to mbind() due to an old Linux bug (feature?) which
|
||||
* cuts off the last specified node. This means backend->host_nodes
|
||||
* must have MAX_NODES+1 bits available.
|
||||
*/
|
||||
assert(sizeof(backend->host_nodes) >=
|
||||
BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
|
||||
assert(maxnode <= MAX_NODES);
|
||||
if (mbind(ptr, sz, backend->policy,
|
||||
maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) {
|
||||
error_setg_errno(errp, errno,
|
||||
"cannot bind memory to host NUMA nodes");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* Preallocate memory after the NUMA policy has been instantiated.
|
||||
* This is necessary to guarantee memory is allocated with
|
||||
* specified NUMA policy in place.
|
||||
*/
|
||||
if (backend->prealloc) {
|
||||
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = host_memory_backend_memory_complete;
|
||||
}
|
||||
|
||||
static const TypeInfo host_memory_backend_info = {
|
||||
.name = TYPE_MEMORY_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(HostMemoryBackendClass),
|
||||
.class_init = host_memory_backend_class_init,
|
||||
.instance_size = sizeof(HostMemoryBackend),
|
||||
.instance_init = host_memory_backend_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&host_memory_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
+2
-2
@@ -67,7 +67,7 @@ CharDriverState *qemu_chr_open_msmouse(void)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
|
||||
chr = qemu_chr_alloc();
|
||||
chr = g_malloc0(sizeof(CharDriverState));
|
||||
chr->chr_write = msmouse_chr_write;
|
||||
chr->chr_close = msmouse_chr_close;
|
||||
chr->explicit_be_open = true;
|
||||
@@ -79,7 +79,7 @@ CharDriverState *qemu_chr_open_msmouse(void)
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL);
|
||||
register_char_driver_qapi("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
@@ -169,7 +169,6 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
|
||||
if (b->opened) {
|
||||
error_set(errp, QERR_PERMISSION_DENIED);
|
||||
} else {
|
||||
g_free(s->chr_name);
|
||||
s->chr_name = g_strdup(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,10 @@ static void rng_random_set_filename(Object *obj, const char *filename,
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(s->filename);
|
||||
if (s->filename) {
|
||||
g_free(s->filename);
|
||||
}
|
||||
|
||||
s->filename = g_strdup(filename);
|
||||
}
|
||||
|
||||
|
||||
+4
-7
@@ -50,7 +50,6 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
RngBackend *s = RNG_BACKEND(obj);
|
||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (value == s->opened) {
|
||||
return;
|
||||
@@ -62,14 +61,12 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
}
|
||||
|
||||
if (k->opened) {
|
||||
k->opened(s, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
k->opened(s, errp);
|
||||
}
|
||||
|
||||
s->opened = true;
|
||||
if (!error_is_set(errp)) {
|
||||
s->opened = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void rng_backend_init(Object *obj)
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* QEMU Char Device for testsuite control
|
||||
*
|
||||
* Copyright (c) 2014 Red Hat, Inc.
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#define BUF_SIZE 32
|
||||
|
||||
typedef struct {
|
||||
CharDriverState *chr;
|
||||
uint8_t in_buf[32];
|
||||
int in_buf_used;
|
||||
} TestdevCharState;
|
||||
|
||||
/* Try to interpret a whole incoming packet */
|
||||
static int testdev_eat_packet(TestdevCharState *testdev)
|
||||
{
|
||||
const uint8_t *cur = testdev->in_buf;
|
||||
int len = testdev->in_buf_used;
|
||||
uint8_t c;
|
||||
int arg;
|
||||
|
||||
#define EAT(c) do { \
|
||||
if (!len--) { \
|
||||
return 0; \
|
||||
} \
|
||||
c = *cur++; \
|
||||
} while (0)
|
||||
|
||||
EAT(c);
|
||||
|
||||
while (isspace(c)) {
|
||||
EAT(c);
|
||||
}
|
||||
|
||||
arg = 0;
|
||||
while (isdigit(c)) {
|
||||
arg = arg * 10 + c - '0';
|
||||
EAT(c);
|
||||
}
|
||||
|
||||
while (isspace(c)) {
|
||||
EAT(c);
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'q':
|
||||
exit((arg << 1) | 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cur - testdev->in_buf;
|
||||
}
|
||||
|
||||
/* The other end is writing some data. Store it and try to interpret */
|
||||
static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
TestdevCharState *testdev = chr->opaque;
|
||||
int tocopy, eaten, orig_len = len;
|
||||
|
||||
while (len) {
|
||||
/* Complete our buffer as much as possible */
|
||||
tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used);
|
||||
|
||||
memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy);
|
||||
testdev->in_buf_used += tocopy;
|
||||
buf += tocopy;
|
||||
len -= tocopy;
|
||||
|
||||
/* Interpret it as much as possible */
|
||||
while (testdev->in_buf_used > 0 &&
|
||||
(eaten = testdev_eat_packet(testdev)) > 0) {
|
||||
memmove(testdev->in_buf, testdev->in_buf + eaten,
|
||||
testdev->in_buf_used - eaten);
|
||||
testdev->in_buf_used -= eaten;
|
||||
}
|
||||
}
|
||||
return orig_len;
|
||||
}
|
||||
|
||||
static void testdev_close(struct CharDriverState *chr)
|
||||
{
|
||||
TestdevCharState *testdev = chr->opaque;
|
||||
|
||||
g_free(testdev);
|
||||
}
|
||||
|
||||
CharDriverState *chr_testdev_init(void)
|
||||
{
|
||||
TestdevCharState *testdev;
|
||||
CharDriverState *chr;
|
||||
|
||||
testdev = g_malloc0(sizeof(TestdevCharState));
|
||||
testdev->chr = chr = g_malloc0(sizeof(CharDriverState));
|
||||
|
||||
chr->opaque = testdev;
|
||||
chr->chr_write = testdev_write;
|
||||
chr->chr_close = testdev_close;
|
||||
|
||||
return chr;
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
+4
-7
@@ -112,7 +112,6 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (value == s->opened) {
|
||||
return;
|
||||
@@ -124,14 +123,12 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
}
|
||||
|
||||
if (k->opened) {
|
||||
k->opened(s, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
k->opened(s, errp);
|
||||
}
|
||||
|
||||
s->opened = true;
|
||||
if (!error_is_set(errp)) {
|
||||
s->opened = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void tpm_backend_instance_init(Object *obj)
|
||||
|
||||
@@ -81,6 +81,19 @@ static int qemu_balloon_status(BalloonInfo *info)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void qemu_balloon_changed(int64_t actual)
|
||||
{
|
||||
QObject *data;
|
||||
|
||||
data = qobject_from_jsonf("{ 'actual': %" PRId64 " }",
|
||||
actual);
|
||||
|
||||
monitor_protocol_event(QEVENT_BALLOON_CHANGE, data);
|
||||
|
||||
qobject_decref(data);
|
||||
}
|
||||
|
||||
|
||||
BalloonInfo *qmp_query_balloon(Error **errp)
|
||||
{
|
||||
BalloonInfo *info;
|
||||
|
||||
+51
-84
@@ -14,9 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "block/block_int.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/timer.h"
|
||||
@@ -61,7 +59,6 @@ typedef struct BlkMigDevState {
|
||||
unsigned long *aio_bitmap;
|
||||
int64_t completed_sectors;
|
||||
BdrvDirtyBitmap *dirty_bitmap;
|
||||
Error *blocker;
|
||||
} BlkMigDevState;
|
||||
|
||||
typedef struct BlkMigBlock {
|
||||
@@ -72,7 +69,7 @@ typedef struct BlkMigBlock {
|
||||
int nr_sectors;
|
||||
struct iovec iov;
|
||||
QEMUIOVector qiov;
|
||||
BlockAIOCB *aiocb;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
|
||||
/* Protected by block migration lock. */
|
||||
int ret;
|
||||
@@ -132,9 +129,9 @@ static void blk_send(QEMUFile *f, BlkMigBlock * blk)
|
||||
| flags);
|
||||
|
||||
/* device name */
|
||||
len = strlen(bdrv_get_device_name(blk->bmds->bs));
|
||||
len = strlen(blk->bmds->bs->device_name);
|
||||
qemu_put_byte(f, len);
|
||||
qemu_put_buffer(f, (uint8_t *)bdrv_get_device_name(blk->bmds->bs), len);
|
||||
qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len);
|
||||
|
||||
/* if a block is zero we need to flush here since the network
|
||||
* bandwidth is now a lot higher than the storage device bandwidth.
|
||||
@@ -188,7 +185,7 @@ static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
|
||||
{
|
||||
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
|
||||
if (sector < bdrv_nb_sectors(bmds->bs)) {
|
||||
if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) {
|
||||
return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] &
|
||||
(1UL << (chunk % (sizeof(unsigned long) * 8))));
|
||||
} else {
|
||||
@@ -225,7 +222,8 @@ static void alloc_aio_bitmap(BlkMigDevState *bmds)
|
||||
BlockDriverState *bs = bmds->bs;
|
||||
int64_t bitmap_size;
|
||||
|
||||
bitmap_size = bdrv_nb_sectors(bs) + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
|
||||
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
|
||||
BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
|
||||
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
|
||||
|
||||
bmds->aio_bitmap = g_malloc0(bitmap_size);
|
||||
@@ -285,7 +283,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
nr_sectors = total_sectors - cur_sector;
|
||||
}
|
||||
|
||||
blk = g_new(BlkMigBlock, 1);
|
||||
blk = g_malloc(sizeof(BlkMigBlock));
|
||||
blk->buf = g_malloc(BLOCK_SIZE);
|
||||
blk->bmds = bmds;
|
||||
blk->sector = cur_sector;
|
||||
@@ -312,28 +310,13 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static int set_dirty_tracking(void)
|
||||
static void set_dirty_tracking(void)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
int ret;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
bmds->dirty_bitmap = bdrv_create_dirty_bitmap(bmds->bs, BLOCK_SIZE,
|
||||
NULL);
|
||||
if (!bmds->dirty_bitmap) {
|
||||
ret = -errno;
|
||||
goto fail;
|
||||
}
|
||||
bmds->dirty_bitmap = bdrv_create_dirty_bitmap(bmds->bs, BLOCK_SIZE);
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (bmds->dirty_bitmap) {
|
||||
bdrv_release_dirty_bitmap(bmds->bs, bmds->dirty_bitmap);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void unset_dirty_tracking(void)
|
||||
@@ -345,12 +328,42 @@ static void unset_dirty_tracking(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void init_blk_migration(QEMUFile *f)
|
||||
static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlkMigDevState *bmds;
|
||||
int64_t sectors;
|
||||
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
|
||||
if (sectors <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bmds = g_malloc0(sizeof(BlkMigDevState));
|
||||
bmds->bs = bs;
|
||||
bmds->bulk_completed = 0;
|
||||
bmds->total_sectors = sectors;
|
||||
bmds->completed_sectors = 0;
|
||||
bmds->shared_base = block_mig_state.shared_base;
|
||||
alloc_aio_bitmap(bmds);
|
||||
bdrv_set_in_use(bs, 1);
|
||||
bdrv_ref(bs);
|
||||
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
|
||||
if (bmds->shared_base) {
|
||||
DPRINTF("Start migration for %s with shared base image\n",
|
||||
bs->device_name);
|
||||
} else {
|
||||
DPRINTF("Start full migration for %s\n", bs->device_name);
|
||||
}
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_blk_migration(QEMUFile *f)
|
||||
{
|
||||
block_mig_state.submitted = 0;
|
||||
block_mig_state.read_done = 0;
|
||||
block_mig_state.transferred = 0;
|
||||
@@ -359,38 +372,7 @@ static void init_blk_migration(QEMUFile *f)
|
||||
block_mig_state.bulk_completed = 0;
|
||||
block_mig_state.zero_blocks = migrate_zero_blocks();
|
||||
|
||||
for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sectors = bdrv_nb_sectors(bs);
|
||||
if (sectors <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bmds = g_new0(BlkMigDevState, 1);
|
||||
bmds->bs = bs;
|
||||
bmds->bulk_completed = 0;
|
||||
bmds->total_sectors = sectors;
|
||||
bmds->completed_sectors = 0;
|
||||
bmds->shared_base = block_mig_state.shared_base;
|
||||
alloc_aio_bitmap(bmds);
|
||||
error_setg(&bmds->blocker, "block device is in use by migration");
|
||||
bdrv_op_block_all(bs, bmds->blocker);
|
||||
bdrv_ref(bs);
|
||||
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
|
||||
if (bmds->shared_base) {
|
||||
DPRINTF("Start migration for %s with shared base image\n",
|
||||
bdrv_get_device_name(bs));
|
||||
} else {
|
||||
DPRINTF("Start full migration for %s\n", bdrv_get_device_name(bs));
|
||||
}
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
|
||||
}
|
||||
bdrv_iterate(init_blk_migration_it, NULL);
|
||||
}
|
||||
|
||||
/* Called with no lock taken. */
|
||||
@@ -467,7 +449,7 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
} else {
|
||||
nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
}
|
||||
blk = g_new(BlkMigBlock, 1);
|
||||
blk = g_malloc(sizeof(BlkMigBlock));
|
||||
blk->buf = g_malloc(BLOCK_SIZE);
|
||||
blk->bmds = bmds;
|
||||
blk->sector = sector;
|
||||
@@ -602,8 +584,7 @@ static void blk_mig_cleanup(void)
|
||||
blk_mig_lock();
|
||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
||||
bdrv_op_unblock_all(bmds->bs, bmds->blocker);
|
||||
error_free(bmds->blocker);
|
||||
bdrv_set_in_use(bmds->bs, 0);
|
||||
bdrv_unref(bmds->bs);
|
||||
g_free(bmds->aio_bitmap);
|
||||
g_free(bmds);
|
||||
@@ -633,13 +614,7 @@ static int block_save_setup(QEMUFile *f, void *opaque)
|
||||
init_blk_migration(f);
|
||||
|
||||
/* start track dirty blocks */
|
||||
ret = set_dirty_tracking();
|
||||
|
||||
if (ret) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_dirty_tracking();
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
ret = flush_blks(f);
|
||||
@@ -653,7 +628,6 @@ static int block_save_iterate(QEMUFile *f, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
int64_t last_ftell = qemu_ftell(f);
|
||||
int64_t delta_ftell;
|
||||
|
||||
DPRINTF("Enter save live iterate submitted %d transferred %d\n",
|
||||
block_mig_state.submitted, block_mig_state.transferred);
|
||||
@@ -703,14 +677,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque)
|
||||
}
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
delta_ftell = qemu_ftell(f) - last_ftell;
|
||||
if (delta_ftell > 0) {
|
||||
return 1;
|
||||
} else if (delta_ftell < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return qemu_ftell(f) - last_ftell;
|
||||
}
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
@@ -765,8 +732,8 @@ static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
|
||||
block_mig_state.read_done * BLOCK_SIZE;
|
||||
|
||||
/* Report at least one block pending during bulk phase */
|
||||
if (pending <= max_size && !block_mig_state.bulk_completed) {
|
||||
pending = max_size + BLOCK_SIZE;
|
||||
if (pending == 0 && !block_mig_state.bulk_completed) {
|
||||
pending = BLOCK_SIZE;
|
||||
}
|
||||
blk_mig_unlock();
|
||||
qemu_mutex_unlock_iothread();
|
||||
@@ -808,7 +775,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
||||
if (bs != bs_prev) {
|
||||
bs_prev = bs;
|
||||
total_sectors = bdrv_nb_sectors(bs);
|
||||
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
|
||||
if (total_sectors <= 0) {
|
||||
error_report("Error getting length of block device %s",
|
||||
device_name);
|
||||
@@ -870,7 +837,7 @@ static bool block_is_active(void *opaque)
|
||||
return block_mig_state.blk_enable == 1;
|
||||
}
|
||||
|
||||
static SaveVMHandlers savevm_block_handlers = {
|
||||
SaveVMHandlers savevm_block_handlers = {
|
||||
.set_params = block_set_params,
|
||||
.save_live_setup = block_save_setup,
|
||||
.save_live_iterate = block_save_iterate,
|
||||
|
||||
+5
-6
@@ -1,28 +1,28 @@
|
||||
block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||
block-obj-y += raw_bsd.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
|
||||
block-obj-$(CONFIG_QUORUM) += quorum.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o
|
||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-y += snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-y += null.o mirror.o
|
||||
|
||||
ifeq ($(CONFIG_POSIX),y)
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||
block-obj-$(CONFIG_CURL) += curl.o
|
||||
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
|
||||
endif
|
||||
|
||||
common-obj-y += stream.o
|
||||
common-obj-y += commit.o
|
||||
common-obj-y += mirror.o
|
||||
common-obj-y += backup.o
|
||||
|
||||
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||
@@ -35,6 +35,5 @@ gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
||||
gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||
archipelago.o-libs := $(ARCHIPELAGO_LIBS)
|
||||
qcow.o-libs := -lz
|
||||
linux-aio.o-libs := -laio
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator block accounting
|
||||
*
|
||||
* Copyright (c) 2011 Christoph Hellwig
|
||||
*
|
||||
* 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 "block/accounting.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
int64_t bytes, enum BlockAcctType type)
|
||||
{
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
cookie->bytes = bytes;
|
||||
cookie->start_time_ns = get_clock();
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
stats->total_time_ns[cookie->type] += get_clock() - cookie->start_time_ns;
|
||||
}
|
||||
|
||||
|
||||
void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num,
|
||||
unsigned int nb_sectors)
|
||||
{
|
||||
if (stats->wr_highest_sector < sector_num + nb_sectors - 1) {
|
||||
stats->wr_highest_sector = sector_num + nb_sectors - 1;
|
||||
}
|
||||
}
|
||||
-1084
File diff suppressed because it is too large
Load Diff
+5
-22
@@ -227,25 +227,9 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
} BackupCompleteData;
|
||||
|
||||
static void backup_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
BackupCompleteData *data = opaque;
|
||||
|
||||
bdrv_unref(s->target);
|
||||
|
||||
block_job_completed(job, data->ret);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void coroutine_fn backup_run(void *opaque)
|
||||
{
|
||||
BackupBlockJob *job = opaque;
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = job->common.bs;
|
||||
BlockDriverState *target = job->target;
|
||||
BlockdevOnError on_target_error = job->on_target_error;
|
||||
@@ -323,7 +307,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
||||
i += n;
|
||||
|
||||
if (alloced == 1 || n == 0) {
|
||||
if (alloced == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -341,7 +325,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* Depending on error action, fail now or retry cluster */
|
||||
BlockErrorAction action =
|
||||
backup_error_action(job, error_is_read, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
if (action == BDRV_ACTION_REPORT) {
|
||||
break;
|
||||
} else {
|
||||
start--;
|
||||
@@ -360,17 +344,16 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
hbitmap_free(job->bitmap);
|
||||
|
||||
bdrv_iostatus_disable(target);
|
||||
bdrv_unref(target);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&job->common, backup_complete, data);
|
||||
block_job_completed(&job->common, ret);
|
||||
}
|
||||
|
||||
void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, MirrorSyncMode sync_mode,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
BlockDriverCompletionFunc *cb, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
|
||||
+22
-148
@@ -26,10 +26,6 @@
|
||||
#include "qemu/config-file.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qint.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
@@ -41,7 +37,7 @@ typedef struct BDRVBlkdebugState {
|
||||
} BDRVBlkdebugState;
|
||||
|
||||
typedef struct BlkdebugAIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int ret;
|
||||
} BlkdebugAIOCB;
|
||||
@@ -52,8 +48,11 @@ typedef struct BlkdebugSuspendedReq {
|
||||
QLIST_ENTRY(BlkdebugSuspendedReq) next;
|
||||
} BlkdebugSuspendedReq;
|
||||
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
|
||||
|
||||
static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlkdebugAIOCB),
|
||||
.aiocb_size = sizeof(BlkdebugAIOCB),
|
||||
.cancel = blkdebug_aio_cancel,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -195,8 +194,6 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||
[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)
|
||||
@@ -216,7 +213,6 @@ static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||
struct add_rule_data {
|
||||
BDRVBlkdebugState *s;
|
||||
int action;
|
||||
Error **errp;
|
||||
};
|
||||
|
||||
static int add_rule(QemuOpts *opts, void *opaque)
|
||||
@@ -229,11 +225,7 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
|
||||
/* Find the right event for the rule */
|
||||
event_name = qemu_opt_get(opts, "event");
|
||||
if (!event_name) {
|
||||
error_setg(d->errp, "Missing event name for rule");
|
||||
return -1;
|
||||
} else if (get_event_by_name(event_name, &event) < 0) {
|
||||
error_setg(d->errp, "Invalid event name \"%s\"", event_name);
|
||||
if (!event_name || get_event_by_name(event_name, &event) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -319,21 +311,10 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
|
||||
|
||||
d.s = s;
|
||||
d.action = ACTION_INJECT_ERROR;
|
||||
d.errp = &local_err;
|
||||
qemu_opts_foreach(&inject_error_opts, add_rule, &d, 1);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0);
|
||||
|
||||
d.action = ACTION_SET_STATE;
|
||||
qemu_opts_foreach(&set_state_opts, add_rule, &d, 1);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
qemu_opts_foreach(&set_state_opts, add_rule, &d, 0);
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
@@ -462,11 +443,17 @@ static void error_callback_bh(void *opaque)
|
||||
struct BlkdebugAIOCB *acb = opaque;
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static BlockAIOCB *inject_error(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
int error = rule->options.inject.error;
|
||||
@@ -484,16 +471,16 @@ static BlockAIOCB *inject_error(BlockDriverState *bs,
|
||||
acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
|
||||
acb->ret = -error;
|
||||
|
||||
bh = aio_bh_new(bdrv_get_aio_context(bs), error_callback_bh, acb);
|
||||
bh = qemu_bh_new(error_callback_bh, acb);
|
||||
acb->bh = bh;
|
||||
qemu_bh_schedule(bh);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
@@ -513,9 +500,9 @@ static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
|
||||
return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
@@ -535,25 +522,6 @@ static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
if (rule->options.inject.sector == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_aio_flush(bs->file, cb, opaque);
|
||||
}
|
||||
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
{
|
||||
@@ -719,98 +687,6 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||
return bdrv_getlength(bs->file);
|
||||
}
|
||||
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
QDict *opts;
|
||||
QList *inject_error_list = NULL, *set_state_list = NULL;
|
||||
QList *suspend_list = NULL;
|
||||
int event;
|
||||
|
||||
if (!bs->file->full_open_options) {
|
||||
/* The config file cannot be recreated, so creating a plain filename
|
||||
* is impossible */
|
||||
return;
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug")));
|
||||
|
||||
QINCREF(bs->file->full_open_options);
|
||||
qdict_put_obj(opts, "image", QOBJECT(bs->file->full_open_options));
|
||||
|
||||
for (event = 0; event < BLKDBG_EVENT_MAX; event++) {
|
||||
QLIST_FOREACH(rule, &s->rules[event], next) {
|
||||
if (rule->action == ACTION_INJECT_ERROR) {
|
||||
QDict *inject_error = qdict_new();
|
||||
|
||||
qdict_put_obj(inject_error, "event", QOBJECT(qstring_from_str(
|
||||
BlkdebugEvent_lookup[rule->event])));
|
||||
qdict_put_obj(inject_error, "state",
|
||||
QOBJECT(qint_from_int(rule->state)));
|
||||
qdict_put_obj(inject_error, "errno", QOBJECT(qint_from_int(
|
||||
rule->options.inject.error)));
|
||||
qdict_put_obj(inject_error, "sector", QOBJECT(qint_from_int(
|
||||
rule->options.inject.sector)));
|
||||
qdict_put_obj(inject_error, "once", QOBJECT(qbool_from_int(
|
||||
rule->options.inject.once)));
|
||||
qdict_put_obj(inject_error, "immediately",
|
||||
QOBJECT(qbool_from_int(
|
||||
rule->options.inject.immediately)));
|
||||
|
||||
if (!inject_error_list) {
|
||||
inject_error_list = qlist_new();
|
||||
}
|
||||
|
||||
qlist_append_obj(inject_error_list, QOBJECT(inject_error));
|
||||
} else if (rule->action == ACTION_SET_STATE) {
|
||||
QDict *set_state = qdict_new();
|
||||
|
||||
qdict_put_obj(set_state, "event", QOBJECT(qstring_from_str(
|
||||
BlkdebugEvent_lookup[rule->event])));
|
||||
qdict_put_obj(set_state, "state",
|
||||
QOBJECT(qint_from_int(rule->state)));
|
||||
qdict_put_obj(set_state, "new_state", QOBJECT(qint_from_int(
|
||||
rule->options.set_state.new_state)));
|
||||
|
||||
if (!set_state_list) {
|
||||
set_state_list = qlist_new();
|
||||
}
|
||||
|
||||
qlist_append_obj(set_state_list, QOBJECT(set_state));
|
||||
} else if (rule->action == ACTION_SUSPEND) {
|
||||
QDict *suspend = qdict_new();
|
||||
|
||||
qdict_put_obj(suspend, "event", QOBJECT(qstring_from_str(
|
||||
BlkdebugEvent_lookup[rule->event])));
|
||||
qdict_put_obj(suspend, "state",
|
||||
QOBJECT(qint_from_int(rule->state)));
|
||||
qdict_put_obj(suspend, "tag", QOBJECT(qstring_from_str(
|
||||
rule->options.suspend.tag)));
|
||||
|
||||
if (!suspend_list) {
|
||||
suspend_list = qlist_new();
|
||||
}
|
||||
|
||||
qlist_append_obj(suspend_list, QOBJECT(suspend));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inject_error_list) {
|
||||
qdict_put_obj(opts, "inject-error", QOBJECT(inject_error_list));
|
||||
}
|
||||
if (set_state_list) {
|
||||
qdict_put_obj(opts, "set-state", QOBJECT(set_state_list));
|
||||
}
|
||||
if (suspend_list) {
|
||||
qdict_put_obj(opts, "suspend", QOBJECT(suspend_list));
|
||||
}
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkdebug = {
|
||||
.format_name = "blkdebug",
|
||||
.protocol_name = "blkdebug",
|
||||
@@ -820,11 +696,9 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_file_open = blkdebug_open,
|
||||
.bdrv_close = blkdebug_close,
|
||||
.bdrv_getlength = blkdebug_getlength,
|
||||
.bdrv_refresh_filename = blkdebug_refresh_filename,
|
||||
|
||||
.bdrv_aio_readv = blkdebug_aio_readv,
|
||||
.bdrv_aio_writev = blkdebug_aio_writev,
|
||||
.bdrv_aio_flush = blkdebug_aio_flush,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
|
||||
+40
-72
@@ -10,8 +10,6 @@
|
||||
#include <stdarg.h>
|
||||
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
|
||||
typedef struct {
|
||||
BlockDriverState *test_file;
|
||||
@@ -19,7 +17,7 @@ typedef struct {
|
||||
|
||||
typedef struct BlkverifyAIOCB BlkverifyAIOCB;
|
||||
struct BlkverifyAIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
|
||||
/* Request metadata */
|
||||
@@ -29,6 +27,7 @@ struct BlkverifyAIOCB {
|
||||
|
||||
int ret; /* first completed request's result */
|
||||
unsigned int done; /* completion counter */
|
||||
bool *finished; /* completion signal for cancel */
|
||||
|
||||
QEMUIOVector *qiov; /* user I/O vector */
|
||||
QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */
|
||||
@@ -37,8 +36,21 @@ struct BlkverifyAIOCB {
|
||||
void (*verify)(BlkverifyAIOCB *acb);
|
||||
};
|
||||
|
||||
static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb;
|
||||
bool finished = false;
|
||||
|
||||
/* Wait until request completes, invokes its callback, and frees itself */
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo blkverify_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlkverifyAIOCB),
|
||||
.cancel = blkverify_aio_cancel,
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
|
||||
@@ -143,7 +155,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -165,7 +176,7 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
|
||||
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque);
|
||||
@@ -179,6 +190,7 @@ static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||
acb->qiov = qiov;
|
||||
acb->buf = NULL;
|
||||
acb->verify = NULL;
|
||||
acb->finished = NULL;
|
||||
return acb;
|
||||
}
|
||||
|
||||
@@ -192,7 +204,10 @@ static void blkverify_aio_bh(void *opaque)
|
||||
qemu_vfree(acb->buf);
|
||||
}
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
if (acb->finished) {
|
||||
*acb->finished = true;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static void blkverify_aio_cb(void *opaque, int ret)
|
||||
@@ -213,8 +228,7 @@ static void blkverify_aio_cb(void *opaque, int ret)
|
||||
acb->verify(acb);
|
||||
}
|
||||
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs),
|
||||
blkverify_aio_bh, acb);
|
||||
acb->bh = qemu_bh_new(blkverify_aio_bh, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
break;
|
||||
}
|
||||
@@ -229,9 +243,9 @@ static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
||||
}
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
|
||||
@@ -249,9 +263,9 @@ static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
|
||||
@@ -264,9 +278,9 @@ static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
@@ -288,67 +302,21 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
return bdrv_recurse_is_first_non_filter(s->test_file, candidate);
|
||||
}
|
||||
|
||||
/* Propagate AioContext changes to ->test_file */
|
||||
static void blkverify_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_detach_aio_context(s->test_file);
|
||||
}
|
||||
|
||||
static void blkverify_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_attach_aio_context(s->test_file, new_context);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
/* bs->file has already been refreshed */
|
||||
bdrv_refresh_filename(s->test_file);
|
||||
|
||||
if (bs->file->full_open_options && s->test_file->full_open_options) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify")));
|
||||
|
||||
QINCREF(bs->file->full_open_options);
|
||||
qdict_put_obj(opts, "raw", QOBJECT(bs->file->full_open_options));
|
||||
QINCREF(s->test_file->full_open_options);
|
||||
qdict_put_obj(opts, "test", QOBJECT(s->test_file->full_open_options));
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
if (bs->file->exact_filename[0] && s->test_file->exact_filename[0]) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkverify:%s:%s",
|
||||
bs->file->exact_filename, s->test_file->exact_filename);
|
||||
}
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkverify = {
|
||||
.format_name = "blkverify",
|
||||
.protocol_name = "blkverify",
|
||||
.instance_size = sizeof(BDRVBlkverifyState),
|
||||
.format_name = "blkverify",
|
||||
.protocol_name = "blkverify",
|
||||
.instance_size = sizeof(BDRVBlkverifyState),
|
||||
|
||||
.bdrv_parse_filename = blkverify_parse_filename,
|
||||
.bdrv_file_open = blkverify_open,
|
||||
.bdrv_close = blkverify_close,
|
||||
.bdrv_getlength = blkverify_getlength,
|
||||
.bdrv_refresh_filename = blkverify_refresh_filename,
|
||||
.bdrv_parse_filename = blkverify_parse_filename,
|
||||
.bdrv_file_open = blkverify_open,
|
||||
.bdrv_close = blkverify_close,
|
||||
.bdrv_getlength = blkverify_getlength,
|
||||
|
||||
.bdrv_aio_readv = blkverify_aio_readv,
|
||||
.bdrv_aio_writev = blkverify_aio_writev,
|
||||
.bdrv_aio_flush = blkverify_aio_flush,
|
||||
.bdrv_aio_readv = blkverify_aio_readv,
|
||||
.bdrv_aio_writev = blkverify_aio_writev,
|
||||
.bdrv_aio_flush = blkverify_aio_flush,
|
||||
|
||||
.bdrv_attach_aio_context = blkverify_attach_aio_context,
|
||||
.bdrv_detach_aio_context = blkverify_detach_aio_context,
|
||||
|
||||
.is_filter = true,
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,631 +0,0 @@
|
||||
/*
|
||||
* QEMU Block backends
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Markus Armbruster <armbru@redhat.com>,
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1
|
||||
* or later. See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
/* Number of coroutines to reserve per attached device model */
|
||||
#define COROUTINE_POOL_RESERVATION 64
|
||||
|
||||
struct BlockBackend {
|
||||
char *name;
|
||||
int refcnt;
|
||||
BlockDriverState *bs;
|
||||
DriveInfo *legacy_dinfo; /* null unless created by drive_new() */
|
||||
QTAILQ_ENTRY(BlockBackend) link; /* for blk_backends */
|
||||
|
||||
void *dev; /* attached device model, if any */
|
||||
/* TODO change to DeviceState when all users are qdevified */
|
||||
const BlockDevOps *dev_ops;
|
||||
void *dev_opaque;
|
||||
};
|
||||
|
||||
static void drive_info_del(DriveInfo *dinfo);
|
||||
|
||||
/* All the BlockBackends (except for hidden ones) */
|
||||
static QTAILQ_HEAD(, BlockBackend) blk_backends =
|
||||
QTAILQ_HEAD_INITIALIZER(blk_backends);
|
||||
|
||||
/*
|
||||
* Create a new BlockBackend with @name, with a reference count of one.
|
||||
* @name must not be null or empty.
|
||||
* Fail if a BlockBackend with this name already exists.
|
||||
* Store an error through @errp on failure, unless it's null.
|
||||
* Return the new BlockBackend on success, null on failure.
|
||||
*/
|
||||
BlockBackend *blk_new(const char *name, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
assert(name && name[0]);
|
||||
if (!id_wellformed(name)) {
|
||||
error_setg(errp, "Invalid device name");
|
||||
return NULL;
|
||||
}
|
||||
if (blk_by_name(name)) {
|
||||
error_setg(errp, "Device with id '%s' already exists", name);
|
||||
return NULL;
|
||||
}
|
||||
if (bdrv_find_node(name)) {
|
||||
error_setg(errp,
|
||||
"Device name '%s' conflicts with an existing node name",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk = g_new0(BlockBackend, 1);
|
||||
blk->name = g_strdup(name);
|
||||
blk->refcnt = 1;
|
||||
QTAILQ_INSERT_TAIL(&blk_backends, blk, link);
|
||||
return blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new BlockBackend with a new BlockDriverState attached.
|
||||
* Otherwise just like blk_new(), which see.
|
||||
*/
|
||||
BlockBackend *blk_new_with_bs(const char *name, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
|
||||
blk = blk_new(name, errp);
|
||||
if (!blk) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bs = bdrv_new_root();
|
||||
blk->bs = bs;
|
||||
bs->blk = blk;
|
||||
return blk;
|
||||
}
|
||||
|
||||
static void blk_delete(BlockBackend *blk)
|
||||
{
|
||||
assert(!blk->refcnt);
|
||||
assert(!blk->dev);
|
||||
if (blk->bs) {
|
||||
assert(blk->bs->blk == blk);
|
||||
blk->bs->blk = NULL;
|
||||
bdrv_unref(blk->bs);
|
||||
blk->bs = NULL;
|
||||
}
|
||||
/* Avoid double-remove after blk_hide_on_behalf_of_do_drive_del() */
|
||||
if (blk->name[0]) {
|
||||
QTAILQ_REMOVE(&blk_backends, blk, link);
|
||||
}
|
||||
g_free(blk->name);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
g_free(blk);
|
||||
}
|
||||
|
||||
static void drive_info_del(DriveInfo *dinfo)
|
||||
{
|
||||
if (!dinfo) {
|
||||
return;
|
||||
}
|
||||
qemu_opts_del(dinfo->opts);
|
||||
g_free(dinfo->serial);
|
||||
g_free(dinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment @blk's reference count.
|
||||
* @blk must not be null.
|
||||
*/
|
||||
void blk_ref(BlockBackend *blk)
|
||||
{
|
||||
blk->refcnt++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrement @blk's reference count.
|
||||
* If this drops it to zero, destroy @blk.
|
||||
* For convenience, do nothing if @blk is null.
|
||||
*/
|
||||
void blk_unref(BlockBackend *blk)
|
||||
{
|
||||
if (blk) {
|
||||
assert(blk->refcnt > 0);
|
||||
if (!--blk->refcnt) {
|
||||
blk_delete(blk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the BlockBackend after @blk.
|
||||
* If @blk is null, return the first one.
|
||||
* Else, return @blk's next sibling, which may be null.
|
||||
*
|
||||
* To iterate over all BlockBackends, do
|
||||
* for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
BlockBackend *blk_next(BlockBackend *blk)
|
||||
{
|
||||
return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&blk_backends);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return @blk's name, a non-null string.
|
||||
* Wart: the name is empty iff @blk has been hidden with
|
||||
* blk_hide_on_behalf_of_do_drive_del().
|
||||
*/
|
||||
const char *blk_name(BlockBackend *blk)
|
||||
{
|
||||
return blk->name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the BlockBackend with name @name if it exists, else null.
|
||||
* @name must not be null.
|
||||
*/
|
||||
BlockBackend *blk_by_name(const char *name)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
assert(name);
|
||||
QTAILQ_FOREACH(blk, &blk_backends, link) {
|
||||
if (!strcmp(name, blk->name)) {
|
||||
return blk;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the BlockDriverState attached to @blk if any, else null.
|
||||
*/
|
||||
BlockDriverState *blk_bs(BlockBackend *blk)
|
||||
{
|
||||
return blk->bs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return @blk's DriveInfo if any, else null.
|
||||
*/
|
||||
DriveInfo *blk_legacy_dinfo(BlockBackend *blk)
|
||||
{
|
||||
return blk->legacy_dinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set @blk's DriveInfo to @dinfo, and return it.
|
||||
* @blk must not have a DriveInfo set already.
|
||||
* No other BlockBackend may have the same DriveInfo set.
|
||||
*/
|
||||
DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo)
|
||||
{
|
||||
assert(!blk->legacy_dinfo);
|
||||
return blk->legacy_dinfo = dinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the BlockBackend with DriveInfo @dinfo.
|
||||
* It must exist.
|
||||
*/
|
||||
BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
QTAILQ_FOREACH(blk, &blk_backends, link) {
|
||||
if (blk->legacy_dinfo == dinfo) {
|
||||
return blk;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide @blk.
|
||||
* @blk must not have been hidden already.
|
||||
* Make attached BlockDriverState, if any, anonymous.
|
||||
* Once hidden, @blk is invisible to all functions that don't receive
|
||||
* it as argument. For example, blk_by_name() won't return it.
|
||||
* Strictly for use by do_drive_del().
|
||||
* TODO get rid of it!
|
||||
*/
|
||||
void blk_hide_on_behalf_of_do_drive_del(BlockBackend *blk)
|
||||
{
|
||||
QTAILQ_REMOVE(&blk_backends, blk, link);
|
||||
blk->name[0] = 0;
|
||||
if (blk->bs) {
|
||||
bdrv_make_anon(blk->bs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach device model @dev to @blk.
|
||||
* Return 0 on success, -EBUSY when a device model is attached already.
|
||||
*/
|
||||
int blk_attach_dev(BlockBackend *blk, void *dev)
|
||||
/* TODO change to DeviceState *dev when all users are qdevified */
|
||||
{
|
||||
if (blk->dev) {
|
||||
return -EBUSY;
|
||||
}
|
||||
blk_ref(blk);
|
||||
blk->dev = dev;
|
||||
bdrv_iostatus_reset(blk->bs);
|
||||
|
||||
/* We're expecting I/O from the device so bump up coroutine pool size */
|
||||
qemu_coroutine_adjust_pool_size(COROUTINE_POOL_RESERVATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach device model @dev to @blk.
|
||||
* @blk must not have a device model attached already.
|
||||
* TODO qdevified devices don't use this, remove when devices are qdevified
|
||||
*/
|
||||
void blk_attach_dev_nofail(BlockBackend *blk, void *dev)
|
||||
{
|
||||
if (blk_attach_dev(blk, dev) < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach device model @dev from @blk.
|
||||
* @dev must be currently attached to @blk.
|
||||
*/
|
||||
void blk_detach_dev(BlockBackend *blk, void *dev)
|
||||
/* TODO change to DeviceState *dev when all users are qdevified */
|
||||
{
|
||||
assert(blk->dev == dev);
|
||||
blk->dev = NULL;
|
||||
blk->dev_ops = NULL;
|
||||
blk->dev_opaque = NULL;
|
||||
bdrv_set_guest_block_size(blk->bs, 512);
|
||||
qemu_coroutine_adjust_pool_size(-COROUTINE_POOL_RESERVATION);
|
||||
blk_unref(blk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the device model attached to @blk if any, else null.
|
||||
*/
|
||||
void *blk_get_attached_dev(BlockBackend *blk)
|
||||
/* TODO change to return DeviceState * when all users are qdevified */
|
||||
{
|
||||
return blk->dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set @blk's device model callbacks to @ops.
|
||||
* @opaque is the opaque argument to pass to the callbacks.
|
||||
* This is for use by device models.
|
||||
*/
|
||||
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
|
||||
void *opaque)
|
||||
{
|
||||
blk->dev_ops = ops;
|
||||
blk->dev_opaque = opaque;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify @blk's attached device model of media change.
|
||||
* If @load is true, notify of media load.
|
||||
* Else, notify of media eject.
|
||||
* Also send DEVICE_TRAY_MOVED events as appropriate.
|
||||
*/
|
||||
void blk_dev_change_media_cb(BlockBackend *blk, bool load)
|
||||
{
|
||||
if (blk->dev_ops && blk->dev_ops->change_media_cb) {
|
||||
bool tray_was_closed = !blk_dev_is_tray_open(blk);
|
||||
|
||||
blk->dev_ops->change_media_cb(blk->dev_opaque, load);
|
||||
if (tray_was_closed) {
|
||||
/* tray open */
|
||||
qapi_event_send_device_tray_moved(blk_name(blk),
|
||||
true, &error_abort);
|
||||
}
|
||||
if (load) {
|
||||
/* tray close */
|
||||
qapi_event_send_device_tray_moved(blk_name(blk),
|
||||
false, &error_abort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does @blk's attached device model have removable media?
|
||||
* %true if no device model is attached.
|
||||
*/
|
||||
bool blk_dev_has_removable_media(BlockBackend *blk)
|
||||
{
|
||||
return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify @blk's attached device model of a media eject request.
|
||||
* If @force is true, the medium is about to be yanked out forcefully.
|
||||
*/
|
||||
void blk_dev_eject_request(BlockBackend *blk, bool force)
|
||||
{
|
||||
if (blk->dev_ops && blk->dev_ops->eject_request_cb) {
|
||||
blk->dev_ops->eject_request_cb(blk->dev_opaque, force);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does @blk's attached device model have a tray, and is it open?
|
||||
*/
|
||||
bool blk_dev_is_tray_open(BlockBackend *blk)
|
||||
{
|
||||
if (blk->dev_ops && blk->dev_ops->is_tray_open) {
|
||||
return blk->dev_ops->is_tray_open(blk->dev_opaque);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does @blk's attached device model have the medium locked?
|
||||
* %false if the device model has no such lock.
|
||||
*/
|
||||
bool blk_dev_is_medium_locked(BlockBackend *blk)
|
||||
{
|
||||
if (blk->dev_ops && blk->dev_ops->is_medium_locked) {
|
||||
return blk->dev_ops->is_medium_locked(blk->dev_opaque);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify @blk's attached device model of a backend size change.
|
||||
*/
|
||||
void blk_dev_resize_cb(BlockBackend *blk)
|
||||
{
|
||||
if (blk->dev_ops && blk->dev_ops->resize_cb) {
|
||||
blk->dev_ops->resize_cb(blk->dev_opaque);
|
||||
}
|
||||
}
|
||||
|
||||
void blk_iostatus_enable(BlockBackend *blk)
|
||||
{
|
||||
bdrv_iostatus_enable(blk->bs);
|
||||
}
|
||||
|
||||
int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
return bdrv_read(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
return bdrv_write(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags,
|
||||
cb, opaque);
|
||||
}
|
||||
|
||||
int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count)
|
||||
{
|
||||
return bdrv_pread(blk->bs, offset, buf, count);
|
||||
}
|
||||
|
||||
int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count)
|
||||
{
|
||||
return bdrv_pwrite(blk->bs, offset, buf, count);
|
||||
}
|
||||
|
||||
int64_t blk_getlength(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_getlength(blk->bs);
|
||||
}
|
||||
|
||||
void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr)
|
||||
{
|
||||
bdrv_get_geometry(blk->bs, nb_sectors_ptr);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
|
||||
QEMUIOVector *iov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num,
|
||||
QEMUIOVector *iov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_flush(blk->bs, cb, opaque);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_discard(BlockBackend *blk,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
void blk_aio_cancel(BlockAIOCB *acb)
|
||||
{
|
||||
bdrv_aio_cancel(acb);
|
||||
}
|
||||
|
||||
void blk_aio_cancel_async(BlockAIOCB *acb)
|
||||
{
|
||||
bdrv_aio_cancel_async(acb);
|
||||
}
|
||||
|
||||
int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs)
|
||||
{
|
||||
return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs);
|
||||
}
|
||||
|
||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
{
|
||||
return bdrv_ioctl(blk->bs, req, buf);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque);
|
||||
}
|
||||
|
||||
int blk_flush(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_flush(blk->bs);
|
||||
}
|
||||
|
||||
int blk_flush_all(void)
|
||||
{
|
||||
return bdrv_flush_all();
|
||||
}
|
||||
|
||||
void blk_drain_all(void)
|
||||
{
|
||||
bdrv_drain_all();
|
||||
}
|
||||
|
||||
BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read)
|
||||
{
|
||||
return bdrv_get_on_error(blk->bs, is_read);
|
||||
}
|
||||
|
||||
BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
|
||||
int error)
|
||||
{
|
||||
return bdrv_get_error_action(blk->bs, is_read, error);
|
||||
}
|
||||
|
||||
void blk_error_action(BlockBackend *blk, BlockErrorAction action,
|
||||
bool is_read, int error)
|
||||
{
|
||||
bdrv_error_action(blk->bs, action, is_read, error);
|
||||
}
|
||||
|
||||
int blk_is_read_only(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_is_read_only(blk->bs);
|
||||
}
|
||||
|
||||
int blk_is_sg(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_is_sg(blk->bs);
|
||||
}
|
||||
|
||||
int blk_enable_write_cache(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_enable_write_cache(blk->bs);
|
||||
}
|
||||
|
||||
void blk_set_enable_write_cache(BlockBackend *blk, bool wce)
|
||||
{
|
||||
bdrv_set_enable_write_cache(blk->bs, wce);
|
||||
}
|
||||
|
||||
int blk_is_inserted(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_is_inserted(blk->bs);
|
||||
}
|
||||
|
||||
void blk_lock_medium(BlockBackend *blk, bool locked)
|
||||
{
|
||||
bdrv_lock_medium(blk->bs, locked);
|
||||
}
|
||||
|
||||
void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||
{
|
||||
bdrv_eject(blk->bs, eject_flag);
|
||||
}
|
||||
|
||||
int blk_get_flags(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_get_flags(blk->bs);
|
||||
}
|
||||
|
||||
void blk_set_guest_block_size(BlockBackend *blk, int align)
|
||||
{
|
||||
bdrv_set_guest_block_size(blk->bs, align);
|
||||
}
|
||||
|
||||
void *blk_blockalign(BlockBackend *blk, size_t size)
|
||||
{
|
||||
return qemu_blockalign(blk ? blk->bs : NULL, size);
|
||||
}
|
||||
|
||||
bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp)
|
||||
{
|
||||
return bdrv_op_is_blocked(blk->bs, op, errp);
|
||||
}
|
||||
|
||||
void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason)
|
||||
{
|
||||
bdrv_op_unblock(blk->bs, op, reason);
|
||||
}
|
||||
|
||||
void blk_op_block_all(BlockBackend *blk, Error *reason)
|
||||
{
|
||||
bdrv_op_block_all(blk->bs, reason);
|
||||
}
|
||||
|
||||
void blk_op_unblock_all(BlockBackend *blk, Error *reason)
|
||||
{
|
||||
bdrv_op_unblock_all(blk->bs, reason);
|
||||
}
|
||||
|
||||
AioContext *blk_get_aio_context(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_get_aio_context(blk->bs);
|
||||
}
|
||||
|
||||
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
|
||||
{
|
||||
bdrv_set_aio_context(blk->bs, new_context);
|
||||
}
|
||||
|
||||
void blk_io_plug(BlockBackend *blk)
|
||||
{
|
||||
bdrv_io_plug(blk->bs);
|
||||
}
|
||||
|
||||
void blk_io_unplug(BlockBackend *blk)
|
||||
{
|
||||
bdrv_io_unplug(blk->bs);
|
||||
}
|
||||
|
||||
BlockAcctStats *blk_get_stats(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_get_stats(blk->bs);
|
||||
}
|
||||
|
||||
void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque);
|
||||
}
|
||||
+10
-19
@@ -131,11 +131,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
s->catalog_bitmap = g_try_new(uint32_t, s->catalog_size);
|
||||
if (s->catalog_size && s->catalog_bitmap == NULL) {
|
||||
error_setg(errp, "Could not allocate memory for catalog");
|
||||
return -ENOMEM;
|
||||
}
|
||||
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||
|
||||
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||
s->catalog_size * 4);
|
||||
@@ -191,14 +187,13 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
uint64_t offset = sector_num * 512;
|
||||
uint64_t extent_index, extent_offset, bitmap_offset;
|
||||
char bitmap_entry;
|
||||
int ret;
|
||||
|
||||
// seek to sector
|
||||
extent_index = offset / s->extent_size;
|
||||
extent_offset = (offset % s->extent_size) / 512;
|
||||
|
||||
if (s->catalog_bitmap[extent_index] == 0xffffffff) {
|
||||
return 0; /* not allocated */
|
||||
return -1; /* not allocated */
|
||||
}
|
||||
|
||||
bitmap_offset = s->data_offset +
|
||||
@@ -206,14 +201,13 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
(s->extent_blocks + s->bitmap_blocks));
|
||||
|
||||
/* read in bitmap for current extent */
|
||||
ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
|
||||
&bitmap_entry, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
|
||||
&bitmap_entry, 1) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
|
||||
return 0; /* not allocated */
|
||||
return -1; /* not allocated */
|
||||
}
|
||||
|
||||
return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
|
||||
@@ -226,16 +220,13 @@ static int bochs_read(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
int64_t block_offset = seek_to_sector(bs, sector_num);
|
||||
if (block_offset < 0) {
|
||||
return block_offset;
|
||||
} else if (block_offset > 0) {
|
||||
if (block_offset >= 0) {
|
||||
ret = bdrv_pread(bs->file, block_offset, buf, 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (ret != 512) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
memset(buf, 0, 512);
|
||||
}
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
|
||||
+9
-26
@@ -72,7 +72,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
s->block_size = be32_to_cpu(s->block_size);
|
||||
if (s->block_size % 512) {
|
||||
error_setg(errp, "block_size %" PRIu32 " must be a multiple of 512",
|
||||
error_setg(errp, "block_size %u must be a multiple of 512",
|
||||
s->block_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* need a buffer this big.
|
||||
*/
|
||||
if (s->block_size > MAX_BLOCK_SIZE) {
|
||||
error_setg(errp, "block_size %" PRIu32 " must be %u MB or less",
|
||||
error_setg(errp, "block_size %u must be %u MB or less",
|
||||
s->block_size,
|
||||
MAX_BLOCK_SIZE / (1024 * 1024));
|
||||
return -EINVAL;
|
||||
@@ -101,7 +101,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* read offsets */
|
||||
if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) {
|
||||
/* Prevent integer overflow */
|
||||
error_setg(errp, "n_blocks %" PRIu32 " must be %zu or less",
|
||||
error_setg(errp, "n_blocks %u must be %zu or less",
|
||||
s->n_blocks,
|
||||
(UINT32_MAX - 1) / sizeof(uint64_t));
|
||||
return -EINVAL;
|
||||
@@ -116,12 +116,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
"try increasing block size");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s->offsets = g_try_malloc(offsets_size);
|
||||
if (s->offsets == NULL) {
|
||||
error_setg(errp, "Could not allocate offsets table");
|
||||
return -ENOMEM;
|
||||
}
|
||||
s->offsets = g_malloc(offsets_size);
|
||||
|
||||
ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
|
||||
if (ret < 0) {
|
||||
@@ -138,7 +133,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
if (s->offsets[i] < s->offsets[i - 1]) {
|
||||
error_setg(errp, "offsets not monotonically increasing at "
|
||||
"index %" PRIu32 ", image file is corrupt", i);
|
||||
"index %u, image file is corrupt", i);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -151,8 +146,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* ridiculous s->compressed_block allocation.
|
||||
*/
|
||||
if (size > 2 * MAX_BLOCK_SIZE) {
|
||||
error_setg(errp, "invalid compressed block size at index %" PRIu32
|
||||
", image file is corrupt", i);
|
||||
error_setg(errp, "invalid compressed block size at index %u, "
|
||||
"image file is corrupt", i);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -163,20 +158,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
s->compressed_block = g_try_malloc(max_compressed_block_size + 1);
|
||||
if (s->compressed_block == NULL) {
|
||||
error_setg(errp, "Could not allocate compressed_block");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->uncompressed_block = g_try_malloc(s->block_size);
|
||||
if (s->uncompressed_block == NULL) {
|
||||
error_setg(errp, "Could not allocate uncompressed_block");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->compressed_block = g_malloc(max_compressed_block_size + 1);
|
||||
s->uncompressed_block = g_malloc(s->block_size);
|
||||
if (inflateInit(&s->zstream) != Z_OK) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
|
||||
+29
-50
@@ -37,7 +37,6 @@ typedef struct CommitBlockJob {
|
||||
BlockdevOnError on_error;
|
||||
int base_flags;
|
||||
int orig_overlay_flags;
|
||||
char *backing_file_str;
|
||||
} CommitBlockJob;
|
||||
|
||||
static int coroutine_fn commit_populate(BlockDriverState *bs,
|
||||
@@ -60,50 +59,17 @@ static int coroutine_fn commit_populate(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
} CommitCompleteData;
|
||||
|
||||
static void commit_complete(BlockJob *job, void *opaque)
|
||||
static void coroutine_fn commit_run(void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||
CommitCompleteData *data = opaque;
|
||||
CommitBlockJob *s = opaque;
|
||||
BlockDriverState *active = s->active;
|
||||
BlockDriverState *top = s->top;
|
||||
BlockDriverState *base = s->base;
|
||||
BlockDriverState *overlay_bs;
|
||||
int ret = data->ret;
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && ret == 0) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
|
||||
}
|
||||
|
||||
/* restore base open flags here if appropriate (e.g., change the base back
|
||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||
* even on failure here */
|
||||
if (s->base_flags != bdrv_get_flags(base)) {
|
||||
bdrv_reopen(base, s->base_flags, NULL);
|
||||
}
|
||||
overlay_bs = bdrv_find_overlay(active, top);
|
||||
if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
||||
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
||||
}
|
||||
g_free(s->backing_file_str);
|
||||
block_job_completed(&s->common, ret);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void coroutine_fn commit_run(void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = opaque;
|
||||
CommitCompleteData *data;
|
||||
BlockDriverState *top = s->top;
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t sector_num, end;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
void *buf = NULL;
|
||||
void *buf;
|
||||
int bytes_written = 0;
|
||||
int64_t base_len;
|
||||
|
||||
@@ -111,18 +77,18 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
|
||||
|
||||
if (s->common.len < 0) {
|
||||
goto out;
|
||||
goto exit_restore_reopen;
|
||||
}
|
||||
|
||||
ret = base_len = bdrv_getlength(base);
|
||||
if (base_len < 0) {
|
||||
goto out;
|
||||
goto exit_restore_reopen;
|
||||
}
|
||||
|
||||
if (base_len < s->common.len) {
|
||||
ret = bdrv_truncate(base, s->common.len);
|
||||
if (ret) {
|
||||
goto out;
|
||||
goto exit_restore_reopen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +127,7 @@ wait:
|
||||
if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
s->on_error == BLOCKDEV_ON_ERROR_REPORT||
|
||||
(s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
|
||||
goto out;
|
||||
goto exit_free_buf;
|
||||
} else {
|
||||
n = 0;
|
||||
continue;
|
||||
@@ -173,12 +139,27 @@ wait:
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(active, top, base);
|
||||
}
|
||||
|
||||
exit_free_buf:
|
||||
qemu_vfree(buf);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&s->common, commit_complete, data);
|
||||
exit_restore_reopen:
|
||||
/* restore base open flags here if appropriate (e.g., change the base back
|
||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||
* even on failure here */
|
||||
if (s->base_flags != bdrv_get_flags(base)) {
|
||||
bdrv_reopen(base, s->base_flags, NULL);
|
||||
}
|
||||
overlay_bs = bdrv_find_overlay(active, top);
|
||||
if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
||||
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
||||
}
|
||||
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
|
||||
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -200,8 +181,8 @@ static const BlockJobDriver commit_job_driver = {
|
||||
|
||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockCompletionFunc *cb,
|
||||
void *opaque, const char *backing_file_str, Error **errp)
|
||||
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
@@ -213,7 +194,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_setg(errp, "Invalid parameter combination");
|
||||
error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,8 +244,6 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
s->base_flags = orig_base_flags;
|
||||
s->orig_overlay_flags = orig_overlay_flags;
|
||||
|
||||
s->backing_file_str = g_strdup(backing_file_str);
|
||||
|
||||
s->on_error = on_error;
|
||||
s->common.co = qemu_coroutine_create(commit_run);
|
||||
|
||||
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Block driver for the COW format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* 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-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
/**************************************************************/
|
||||
/* COW block driver using file system holes */
|
||||
|
||||
/* user mode linux compatible COW file */
|
||||
#define COW_MAGIC 0x4f4f4f4d /* MOOO */
|
||||
#define COW_VERSION 2
|
||||
|
||||
struct cow_header_v2 {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
char backing_file[1024];
|
||||
int32_t mtime;
|
||||
uint64_t size;
|
||||
uint32_t sectorsize;
|
||||
};
|
||||
|
||||
typedef struct BDRVCowState {
|
||||
CoMutex lock;
|
||||
int64_t cow_sectors_offset;
|
||||
} BDRVCowState;
|
||||
|
||||
static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct cow_header_v2 *cow_header = (const void *)buf;
|
||||
|
||||
if (buf_size >= sizeof(struct cow_header_v2) &&
|
||||
be32_to_cpu(cow_header->magic) == COW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == COW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
struct cow_header_v2 cow_header;
|
||||
int bitmap_size;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
/* see if it is a cow image */
|
||||
ret = bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
||||
error_setg(errp, "Image not in COW format");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version),
|
||||
"COW version %d", cow_header.version);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "cow", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* cow image found */
|
||||
size = be64_to_cpu(cow_header.size);
|
||||
bs->total_sectors = size / 512;
|
||||
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
cow_header.backing_file);
|
||||
|
||||
bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
|
||||
s->cow_sectors_offset = (bitmap_size + 511) & ~511;
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void cow_set_bits(uint8_t *bitmap, int start, int64_t nb_sectors)
|
||||
{
|
||||
int64_t bitnum = start, last = start + nb_sectors;
|
||||
while (bitnum < last) {
|
||||
if ((bitnum & 7) == 0 && bitnum + 8 <= last) {
|
||||
bitmap[bitnum / 8] = 0xFF;
|
||||
bitnum += 8;
|
||||
continue;
|
||||
}
|
||||
bitmap[bitnum/8] |= (1 << (bitnum % 8));
|
||||
bitnum++;
|
||||
}
|
||||
}
|
||||
|
||||
#define BITS_PER_BITMAP_SECTOR (512 * 8)
|
||||
|
||||
/* Cannot use bitmap.c on big-endian machines. */
|
||||
static int cow_test_bit(int64_t bitnum, const uint8_t *bitmap)
|
||||
{
|
||||
return (bitmap[bitnum / 8] & (1 << (bitnum & 7))) != 0;
|
||||
}
|
||||
|
||||
static int cow_find_streak(const uint8_t *bitmap, int value, int start, int nb_sectors)
|
||||
{
|
||||
int streak_value = value ? 0xFF : 0;
|
||||
int last = MIN(start + nb_sectors, BITS_PER_BITMAP_SECTOR);
|
||||
int bitnum = start;
|
||||
while (bitnum < last) {
|
||||
if ((bitnum & 7) == 0 && bitmap[bitnum / 8] == streak_value) {
|
||||
bitnum += 8;
|
||||
continue;
|
||||
}
|
||||
if (cow_test_bit(bitnum, bitmap) == value) {
|
||||
bitnum++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return MIN(bitnum, last) - start;
|
||||
}
|
||||
|
||||
/* Return true if first block has been changed (ie. current version is
|
||||
* in COW file). Set the number of continuous blocks for which that
|
||||
* is true. */
|
||||
static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *num_same)
|
||||
{
|
||||
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
|
||||
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
|
||||
bool first = true;
|
||||
int changed = 0, same = 0;
|
||||
|
||||
do {
|
||||
int ret;
|
||||
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||
|
||||
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
|
||||
int sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
changed = cow_test_bit(bitnum, bitmap);
|
||||
first = false;
|
||||
}
|
||||
|
||||
same += cow_find_streak(bitmap, changed, bitnum, nb_sectors);
|
||||
|
||||
bitnum += sector_bits;
|
||||
nb_sectors -= sector_bits;
|
||||
offset += BDRV_SECTOR_SIZE;
|
||||
} while (nb_sectors);
|
||||
|
||||
*num_same = same;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn cow_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *num_same)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret = cow_co_is_allocated(bs, sector_num, nb_sectors, num_same);
|
||||
int64_t offset = s->cow_sectors_offset + (sector_num << BDRV_SECTOR_BITS);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return (ret ? BDRV_BLOCK_DATA : 0) | offset | BDRV_BLOCK_OFFSET_VALID;
|
||||
}
|
||||
|
||||
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
|
||||
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
|
||||
bool first = true;
|
||||
int sector_bits;
|
||||
|
||||
for ( ; nb_sectors;
|
||||
bitnum += sector_bits,
|
||||
nb_sectors -= sector_bits,
|
||||
offset += BDRV_SECTOR_SIZE) {
|
||||
int ret, set;
|
||||
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||
|
||||
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
|
||||
sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Skip over any already set bits */
|
||||
set = cow_find_streak(bitmap, 1, bitnum, sector_bits);
|
||||
bitnum += set;
|
||||
sector_bits -= set;
|
||||
nb_sectors -= set;
|
||||
if (!sector_bits) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
ret = bdrv_flush(bs->file);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
cow_set_bits(bitmap, bitnum, sector_bits);
|
||||
|
||||
ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, n;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
ret = cow_co_is_allocated(bs, sector_num, nb_sectors, &n);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret) {
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->cow_sectors_offset + sector_num * 512,
|
||||
buf, n * 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
memset(buf, 0, n * 512);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int cow_co_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVCowState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = cow_read(bs, sector_num, buf, nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512,
|
||||
buf, nb_sectors * 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return cow_update_bitmap(bs, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
static coroutine_fn int cow_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVCowState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = cow_write(bs, sector_num, buf, nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
struct cow_header_v2 cow_header;
|
||||
struct stat st;
|
||||
int64_t image_sectors = 0;
|
||||
const char *image_filename = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *cow_bs;
|
||||
|
||||
/* Read out options */
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
image_sectors = options->value.n / 512;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
||||
image_filename = options->value.s;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cow_bs = NULL;
|
||||
ret = bdrv_open(&cow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&cow_header, 0, sizeof(cow_header));
|
||||
cow_header.magic = cpu_to_be32(COW_MAGIC);
|
||||
cow_header.version = cpu_to_be32(COW_VERSION);
|
||||
if (image_filename) {
|
||||
/* Note: if no file, we put a dummy mtime */
|
||||
cow_header.mtime = cpu_to_be32(0);
|
||||
|
||||
if (stat(image_filename, &st) != 0) {
|
||||
goto mtime_fail;
|
||||
}
|
||||
cow_header.mtime = cpu_to_be32(st.st_mtime);
|
||||
mtime_fail:
|
||||
pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
|
||||
image_filename);
|
||||
}
|
||||
cow_header.sectorsize = cpu_to_be32(512);
|
||||
cow_header.size = cpu_to_be64(image_sectors * 512);
|
||||
ret = bdrv_pwrite(cow_bs, 0, &cow_header, sizeof(cow_header));
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* resize to include at least all the bitmap */
|
||||
ret = bdrv_truncate(cow_bs,
|
||||
sizeof(cow_header) + ((image_sectors + 7) >> 3));
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
bdrv_unref(cow_bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QEMUOptionParameter cow_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_cow = {
|
||||
.format_name = "cow",
|
||||
.instance_size = sizeof(BDRVCowState),
|
||||
|
||||
.bdrv_probe = cow_probe,
|
||||
.bdrv_open = cow_open,
|
||||
.bdrv_close = cow_close,
|
||||
.bdrv_create = cow_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
.bdrv_read = cow_co_read,
|
||||
.bdrv_write = cow_co_write,
|
||||
.bdrv_co_get_block_status = cow_co_get_block_status,
|
||||
|
||||
.create_options = cow_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_cow_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_cow);
|
||||
}
|
||||
|
||||
block_init(bdrv_cow_init);
|
||||
+205
-272
@@ -23,10 +23,9 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
// #define DEBUG_CURL
|
||||
// #define DEBUG
|
||||
// #define DEBUG_VERBOSE
|
||||
|
||||
#ifdef DEBUG_CURL
|
||||
@@ -38,21 +37,6 @@
|
||||
#if LIBCURL_VERSION_NUM >= 0x071000
|
||||
/* The multi interface timer callback was introduced in 7.16.0 */
|
||||
#define NEED_CURL_TIMER_CALLBACK
|
||||
#define HAVE_SOCKET_ACTION
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SOCKET_ACTION
|
||||
/* If curl_multi_socket_action isn't available, define it statically here in
|
||||
* terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is
|
||||
* less efficient but still safe. */
|
||||
static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||
curl_socket_t sockfd,
|
||||
int ev_bitmask,
|
||||
int *running_handles)
|
||||
{
|
||||
return curl_multi_socket(multi_handle, sockfd, running_handles);
|
||||
}
|
||||
#define curl_multi_socket_action __curl_multi_socket_action
|
||||
#endif
|
||||
|
||||
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
|
||||
@@ -62,24 +46,16 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||
#define CURL_NUM_STATES 8
|
||||
#define CURL_NUM_ACB 8
|
||||
#define SECTOR_SIZE 512
|
||||
#define READ_AHEAD_DEFAULT (256 * 1024)
|
||||
#define CURL_TIMEOUT_DEFAULT 5
|
||||
#define CURL_TIMEOUT_MAX 10000
|
||||
#define READ_AHEAD_SIZE (256 * 1024)
|
||||
|
||||
#define FIND_RET_NONE 0
|
||||
#define FIND_RET_OK 1
|
||||
#define FIND_RET_WAIT 2
|
||||
|
||||
#define CURL_BLOCK_OPT_URL "url"
|
||||
#define CURL_BLOCK_OPT_READAHEAD "readahead"
|
||||
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
||||
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
||||
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
||||
|
||||
struct BDRVCURLState;
|
||||
|
||||
typedef struct CURLAIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
QEMUIOVector *qiov;
|
||||
|
||||
@@ -95,7 +71,6 @@ typedef struct CURLState
|
||||
struct BDRVCURLState *s;
|
||||
CURLAIOCB *acb[CURL_NUM_ACB];
|
||||
CURL *curl;
|
||||
curl_socket_t sock_fd;
|
||||
char *orig_buf;
|
||||
size_t buf_start;
|
||||
size_t buf_off;
|
||||
@@ -112,16 +87,11 @@ typedef struct BDRVCURLState {
|
||||
CURLState states[CURL_NUM_STATES];
|
||||
char *url;
|
||||
size_t readahead_size;
|
||||
bool sslverify;
|
||||
uint64_t timeout;
|
||||
char *cookie;
|
||||
bool accept_range;
|
||||
AioContext *aio_context;
|
||||
} BDRVCURLState;
|
||||
|
||||
static void curl_clean_state(CURLState *s);
|
||||
static void curl_multi_do(void *arg);
|
||||
static void curl_multi_read(void *arg);
|
||||
|
||||
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||
static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
||||
@@ -141,29 +111,21 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
||||
#endif
|
||||
|
||||
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
||||
void *userp, void *sp)
|
||||
void *s, void *sp)
|
||||
{
|
||||
BDRVCURLState *s;
|
||||
CURLState *state = NULL;
|
||||
curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
|
||||
state->sock_fd = fd;
|
||||
s = state->s;
|
||||
|
||||
DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
|
||||
switch (action) {
|
||||
case CURL_POLL_IN:
|
||||
aio_set_fd_handler(s->aio_context, fd, curl_multi_read,
|
||||
NULL, state);
|
||||
qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, s);
|
||||
break;
|
||||
case CURL_POLL_OUT:
|
||||
aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, state);
|
||||
qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, s);
|
||||
break;
|
||||
case CURL_POLL_INOUT:
|
||||
aio_set_fd_handler(s->aio_context, fd, curl_multi_read,
|
||||
curl_multi_do, state);
|
||||
qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do, s);
|
||||
break;
|
||||
case CURL_POLL_REMOVE:
|
||||
aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(fd, NULL, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -193,7 +155,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||
DPRINTF("CURL: Just reading %zd bytes\n", realsize);
|
||||
|
||||
if (!s || !s->orig_buf)
|
||||
return 0;
|
||||
goto read_end;
|
||||
|
||||
if (s->buf_off >= s->buf_len) {
|
||||
/* buffer full, read nothing */
|
||||
@@ -213,11 +175,12 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||
qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
|
||||
acb->end - acb->start);
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
s->acb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
read_end:
|
||||
return realsize;
|
||||
}
|
||||
|
||||
@@ -252,8 +215,7 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
||||
}
|
||||
|
||||
// Wait for unfinished chunks
|
||||
if (state->in_use &&
|
||||
(start >= state->buf_start) &&
|
||||
if ((start >= state->buf_start) &&
|
||||
(start <= buf_fend) &&
|
||||
(end >= state->buf_start) &&
|
||||
(end <= buf_fend))
|
||||
@@ -275,69 +237,68 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
||||
return FIND_RET_NONE;
|
||||
}
|
||||
|
||||
static void curl_multi_check_completion(BDRVCURLState *s)
|
||||
static void curl_multi_read(BDRVCURLState *s)
|
||||
{
|
||||
int msgs_in_queue;
|
||||
|
||||
/* Try to find done transfers, so we can free the easy
|
||||
* handle again. */
|
||||
for (;;) {
|
||||
do {
|
||||
CURLMsg *msg;
|
||||
msg = curl_multi_info_read(s->multi, &msgs_in_queue);
|
||||
|
||||
/* Quit when there are no more completions */
|
||||
if (!msg)
|
||||
break;
|
||||
|
||||
if (msg->msg == CURLMSG_DONE) {
|
||||
CURLState *state = NULL;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE,
|
||||
(char **)&state);
|
||||
|
||||
/* ACBs for successful messages get completed in curl_read_cb */
|
||||
if (msg->data.result != CURLE_OK) {
|
||||
int i;
|
||||
for (i = 0; i < CURL_NUM_ACB; i++) {
|
||||
CURLAIOCB *acb = state->acb[i];
|
||||
|
||||
if (acb == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
qemu_aio_unref(acb);
|
||||
state->acb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
curl_clean_state(state);
|
||||
if (msg->msg == CURLMSG_NONE)
|
||||
break;
|
||||
|
||||
switch (msg->msg) {
|
||||
case CURLMSG_DONE:
|
||||
{
|
||||
CURLState *state = NULL;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
|
||||
|
||||
/* ACBs for successful messages get completed in curl_read_cb */
|
||||
if (msg->data.result != CURLE_OK) {
|
||||
int i;
|
||||
for (i = 0; i < CURL_NUM_ACB; i++) {
|
||||
CURLAIOCB *acb = state->acb[i];
|
||||
|
||||
if (acb == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
qemu_aio_release(acb);
|
||||
state->acb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
curl_clean_state(state);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
msgs_in_queue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(msgs_in_queue);
|
||||
}
|
||||
|
||||
static void curl_multi_do(void *arg)
|
||||
{
|
||||
CURLState *s = (CURLState *)arg;
|
||||
BDRVCURLState *s = (BDRVCURLState *)arg;
|
||||
int running;
|
||||
int r;
|
||||
|
||||
if (!s->s->multi) {
|
||||
if (!s->multi) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
r = curl_multi_socket_action(s->s->multi, s->sock_fd, 0, &running);
|
||||
r = curl_multi_socket_all(s->multi, &running);
|
||||
} while(r == CURLM_CALL_MULTI_PERFORM);
|
||||
|
||||
}
|
||||
|
||||
static void curl_multi_read(void *arg)
|
||||
{
|
||||
CURLState *s = (CURLState *)arg;
|
||||
|
||||
curl_multi_do(arg);
|
||||
curl_multi_check_completion(s->s);
|
||||
curl_multi_read(s);
|
||||
}
|
||||
|
||||
static void curl_multi_timeout_do(void *arg)
|
||||
@@ -352,13 +313,13 @@ static void curl_multi_timeout_do(void *arg)
|
||||
|
||||
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
|
||||
|
||||
curl_multi_check_completion(s);
|
||||
curl_multi_read(s);
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
||||
static CURLState *curl_init_state(BDRVCURLState *s)
|
||||
{
|
||||
CURLState *state = NULL;
|
||||
int i, j;
|
||||
@@ -376,47 +337,44 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
||||
break;
|
||||
}
|
||||
if (!state) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
g_usleep(100);
|
||||
curl_multi_do(s);
|
||||
}
|
||||
} while(!state);
|
||||
|
||||
if (!state->curl) {
|
||||
state->curl = curl_easy_init();
|
||||
if (!state->curl) {
|
||||
return NULL;
|
||||
}
|
||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||
(long) s->sslverify);
|
||||
if (s->cookie) {
|
||||
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
||||
}
|
||||
curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout);
|
||||
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
|
||||
(void *)curl_read_cb);
|
||||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
|
||||
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
|
||||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
||||
if (state->curl)
|
||||
goto has_curl;
|
||||
|
||||
/* Restrict supported protocols to avoid security issues in the more
|
||||
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
||||
* CVE-2013-0249.
|
||||
*
|
||||
* Restricting protocols is only supported from 7.19.4 upwards.
|
||||
*/
|
||||
state->curl = curl_easy_init();
|
||||
if (!state->curl)
|
||||
return NULL;
|
||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||
curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
|
||||
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
|
||||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
|
||||
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
|
||||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
||||
|
||||
/* Restrict supported protocols to avoid security issues in the more
|
||||
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
||||
* CVE-2013-0249.
|
||||
*
|
||||
* Restricting protocols is only supported from 7.19.4 upwards.
|
||||
*/
|
||||
#if LIBCURL_VERSION_NUM >= 0x071304
|
||||
curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS);
|
||||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS);
|
||||
curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS);
|
||||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
has_curl:
|
||||
|
||||
state->s = s;
|
||||
|
||||
@@ -433,50 +391,43 @@ static void curl_clean_state(CURLState *s)
|
||||
static void curl_parse_filename(const char *filename, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename));
|
||||
}
|
||||
|
||||
static void curl_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCURLState *s = bs->opaque;
|
||||
int i;
|
||||
#define RA_OPTSTR ":readahead="
|
||||
char *file;
|
||||
char *ra;
|
||||
const char *ra_val;
|
||||
int parse_state = 0;
|
||||
|
||||
for (i = 0; i < CURL_NUM_STATES; i++) {
|
||||
if (s->states[i].in_use) {
|
||||
curl_clean_state(&s->states[i]);
|
||||
file = g_strdup(filename);
|
||||
|
||||
/* Parse a trailing ":readahead=#:" param, if present. */
|
||||
ra = file + strlen(file) - 1;
|
||||
while (ra >= file) {
|
||||
if (parse_state == 0) {
|
||||
if (*ra == ':') {
|
||||
parse_state++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (parse_state == 1) {
|
||||
if (*ra > '9' || *ra < '0') {
|
||||
char *opt_start = ra - strlen(RA_OPTSTR) + 1;
|
||||
if (opt_start > file &&
|
||||
strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
|
||||
ra_val = ra + 1;
|
||||
ra -= strlen(RA_OPTSTR) - 1;
|
||||
*ra = '\0';
|
||||
qdict_put(options, "readahead", qstring_from_str(ra_val));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s->states[i].curl) {
|
||||
curl_easy_cleanup(s->states[i].curl);
|
||||
s->states[i].curl = NULL;
|
||||
}
|
||||
g_free(s->states[i].orig_buf);
|
||||
s->states[i].orig_buf = NULL;
|
||||
}
|
||||
if (s->multi) {
|
||||
curl_multi_cleanup(s->multi);
|
||||
s->multi = NULL;
|
||||
ra--;
|
||||
}
|
||||
|
||||
timer_del(&s->timer);
|
||||
}
|
||||
qdict_put(options, "url", qstring_from_str(file));
|
||||
|
||||
static void curl_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVCURLState *s = bs->opaque;
|
||||
|
||||
aio_timer_init(new_context, &s->timer,
|
||||
QEMU_CLOCK_REALTIME, SCALE_NS,
|
||||
curl_multi_timeout_do, s);
|
||||
|
||||
assert(!s->multi);
|
||||
s->multi = curl_multi_init();
|
||||
s->aio_context = new_context;
|
||||
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
|
||||
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
|
||||
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
|
||||
#endif
|
||||
g_free(file);
|
||||
}
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
@@ -484,30 +435,15 @@ static QemuOptsList runtime_opts = {
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_URL,
|
||||
.name = "url",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "URL to open",
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_READAHEAD,
|
||||
.name = "readahead",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Readahead size",
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_SSLVERIFY,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Verify SSL certificate"
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_TIMEOUT,
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Curl timeout"
|
||||
},
|
||||
{
|
||||
.name = CURL_BLOCK_OPT_COOKIE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Pass the cookie or list of cookies with each request"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -520,7 +456,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *file;
|
||||
const char *cookie;
|
||||
double d;
|
||||
|
||||
static int inited = 0;
|
||||
@@ -537,27 +472,14 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
|
||||
READ_AHEAD_DEFAULT);
|
||||
s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
|
||||
if ((s->readahead_size & 0x1ff) != 0) {
|
||||
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
|
||||
s->readahead_size);
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
|
||||
CURL_TIMEOUT_DEFAULT);
|
||||
if (s->timeout > CURL_TIMEOUT_MAX) {
|
||||
error_setg(errp, "timeout parameter is too large or negative");
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
|
||||
|
||||
cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
|
||||
s->cookie = g_strdup(cookie);
|
||||
|
||||
file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
|
||||
file = qemu_opt_get(opts, "url");
|
||||
if (file == NULL) {
|
||||
error_setg(errp, "curl block driver requires an 'url' option");
|
||||
goto out_noclean;
|
||||
@@ -569,9 +491,8 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
DPRINTF("CURL: Opening %s\n", file);
|
||||
s->aio_context = bdrv_get_aio_context(bs);
|
||||
s->url = g_strdup(file);
|
||||
state = curl_init_state(bs, s);
|
||||
state = curl_init_state(s);
|
||||
if (!state)
|
||||
goto out_noclean;
|
||||
|
||||
@@ -602,31 +523,49 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
curl_easy_cleanup(state->curl);
|
||||
state->curl = NULL;
|
||||
|
||||
curl_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
||||
aio_timer_init(bdrv_get_aio_context(bs), &s->timer,
|
||||
QEMU_CLOCK_REALTIME, SCALE_NS,
|
||||
curl_multi_timeout_do, s);
|
||||
|
||||
// Now we know the file exists and its size, so let's
|
||||
// initialize the multi interface!
|
||||
|
||||
s->multi = curl_multi_init();
|
||||
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
|
||||
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
|
||||
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
|
||||
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
|
||||
#endif
|
||||
curl_multi_do(s);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
error_setg(errp, "CURL: Error opening file: %s", state->errmsg);
|
||||
fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
|
||||
curl_easy_cleanup(state->curl);
|
||||
state->curl = NULL;
|
||||
out_noclean:
|
||||
g_free(s->cookie);
|
||||
g_free(s->url);
|
||||
qemu_opts_del(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
// Do we have to implement canceling? Seems to work without...
|
||||
}
|
||||
|
||||
static const AIOCBInfo curl_aiocb_info = {
|
||||
.aiocb_size = sizeof(CURLAIOCB),
|
||||
.cancel = curl_aio_cancel,
|
||||
};
|
||||
|
||||
|
||||
static void curl_readv_bh_cb(void *p)
|
||||
{
|
||||
CURLState *state;
|
||||
int running;
|
||||
|
||||
CURLAIOCB *acb = p;
|
||||
BDRVCURLState *s = acb->common.bs->opaque;
|
||||
@@ -641,7 +580,7 @@ static void curl_readv_bh_cb(void *p)
|
||||
// we can just call the callback and be done.
|
||||
switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
|
||||
case FIND_RET_OK:
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
// fall through
|
||||
case FIND_RET_WAIT:
|
||||
return;
|
||||
@@ -650,10 +589,10 @@ static void curl_readv_bh_cb(void *p)
|
||||
}
|
||||
|
||||
// No cache found, so let's start a new request
|
||||
state = curl_init_state(acb->common.bs, s);
|
||||
state = curl_init_state(s);
|
||||
if (!state) {
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -661,17 +600,12 @@ static void curl_readv_bh_cb(void *p)
|
||||
acb->end = (acb->nb_sectors * SECTOR_SIZE);
|
||||
|
||||
state->buf_off = 0;
|
||||
g_free(state->orig_buf);
|
||||
if (state->orig_buf)
|
||||
g_free(state->orig_buf);
|
||||
state->buf_start = start;
|
||||
state->buf_len = acb->end + s->readahead_size;
|
||||
end = MIN(start + state->buf_len, s->len) - 1;
|
||||
state->orig_buf = g_try_malloc(state->buf_len);
|
||||
if (state->buf_len && state->orig_buf == NULL) {
|
||||
curl_clean_state(state);
|
||||
acb->common.cb(acb->common.opaque, -ENOMEM);
|
||||
qemu_aio_unref(acb);
|
||||
return;
|
||||
}
|
||||
state->orig_buf = g_malloc(state->buf_len);
|
||||
state->acb[0] = acb;
|
||||
|
||||
snprintf(state->range, 127, "%zd-%zd", start, end);
|
||||
@@ -680,14 +614,13 @@ static void curl_readv_bh_cb(void *p)
|
||||
curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
|
||||
|
||||
curl_multi_add_handle(s->multi, state->curl);
|
||||
curl_multi_do(s);
|
||||
|
||||
/* Tell curl it needs to kick things off */
|
||||
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
|
||||
}
|
||||
|
||||
static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
CURLAIOCB *acb;
|
||||
|
||||
@@ -697,7 +630,7 @@ static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
acb->sector_num = sector_num;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(bs), curl_readv_bh_cb, acb);
|
||||
acb->bh = qemu_bh_new(curl_readv_bh_cb, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
return &acb->common;
|
||||
}
|
||||
@@ -705,11 +638,26 @@ static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
static void curl_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCURLState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
DPRINTF("CURL: Close\n");
|
||||
curl_detach_aio_context(bs);
|
||||
for (i=0; i<CURL_NUM_STATES; i++) {
|
||||
if (s->states[i].in_use)
|
||||
curl_clean_state(&s->states[i]);
|
||||
if (s->states[i].curl) {
|
||||
curl_easy_cleanup(s->states[i].curl);
|
||||
s->states[i].curl = NULL;
|
||||
}
|
||||
if (s->states[i].orig_buf) {
|
||||
g_free(s->states[i].orig_buf);
|
||||
s->states[i].orig_buf = NULL;
|
||||
}
|
||||
}
|
||||
if (s->multi)
|
||||
curl_multi_cleanup(s->multi);
|
||||
|
||||
timer_del(&s->timer);
|
||||
|
||||
g_free(s->cookie);
|
||||
g_free(s->url);
|
||||
}
|
||||
|
||||
@@ -720,83 +668,68 @@ static int64_t curl_getlength(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_http = {
|
||||
.format_name = "http",
|
||||
.protocol_name = "http",
|
||||
.format_name = "http",
|
||||
.protocol_name = "http",
|
||||
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
|
||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_https = {
|
||||
.format_name = "https",
|
||||
.protocol_name = "https",
|
||||
.format_name = "https",
|
||||
.protocol_name = "https",
|
||||
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
|
||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_ftp = {
|
||||
.format_name = "ftp",
|
||||
.protocol_name = "ftp",
|
||||
.format_name = "ftp",
|
||||
.protocol_name = "ftp",
|
||||
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
|
||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_ftps = {
|
||||
.format_name = "ftps",
|
||||
.protocol_name = "ftps",
|
||||
.format_name = "ftps",
|
||||
.protocol_name = "ftps",
|
||||
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
|
||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_tftp = {
|
||||
.format_name = "tftp",
|
||||
.protocol_name = "tftp",
|
||||
.format_name = "tftp",
|
||||
.protocol_name = "tftp",
|
||||
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
.instance_size = sizeof(BDRVCURLState),
|
||||
.bdrv_parse_filename = curl_parse_filename,
|
||||
.bdrv_file_open = curl_open,
|
||||
.bdrv_close = curl_close,
|
||||
.bdrv_getlength = curl_getlength,
|
||||
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
|
||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||
.bdrv_aio_readv = curl_aio_readv,
|
||||
};
|
||||
|
||||
static void curl_block_init(void)
|
||||
|
||||
+10
-17
@@ -248,8 +248,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
offset += 8;
|
||||
|
||||
if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
|
||||
error_report("sector count %" PRIu64 " for chunk %" PRIu32
|
||||
" is larger than max (%u)",
|
||||
error_report("sector count %" PRIu64 " for chunk %u is "
|
||||
"larger than max (%u)",
|
||||
s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@@ -269,8 +269,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
offset += 8;
|
||||
|
||||
if (s->lengths[i] > DMG_LENGTHS_MAX) {
|
||||
error_report("length %" PRIu64 " for chunk %" PRIu32
|
||||
" is larger than max (%u)",
|
||||
error_report("length %" PRIu64 " for chunk %u is larger "
|
||||
"than max (%u)",
|
||||
s->lengths[i], i, DMG_LENGTHS_MAX);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@@ -284,15 +284,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
s->compressed_chunk = qemu_try_blockalign(bs->file,
|
||||
max_compressed_size + 1);
|
||||
s->uncompressed_chunk = qemu_try_blockalign(bs->file,
|
||||
512 * max_sectors_per_chunk);
|
||||
if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->compressed_chunk = g_malloc(max_compressed_size + 1);
|
||||
s->uncompressed_chunk = g_malloc(512 * max_sectors_per_chunk);
|
||||
if (inflateInit(&s->zstream) != Z_OK) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@@ -309,8 +302,8 @@ fail:
|
||||
g_free(s->lengths);
|
||||
g_free(s->sectors);
|
||||
g_free(s->sectorcounts);
|
||||
qemu_vfree(s->compressed_chunk);
|
||||
qemu_vfree(s->uncompressed_chunk);
|
||||
g_free(s->compressed_chunk);
|
||||
g_free(s->uncompressed_chunk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -433,8 +426,8 @@ static void dmg_close(BlockDriverState *bs)
|
||||
g_free(s->lengths);
|
||||
g_free(s->sectors);
|
||||
g_free(s->sectorcounts);
|
||||
qemu_vfree(s->compressed_chunk);
|
||||
qemu_vfree(s->uncompressed_chunk);
|
||||
g_free(s->compressed_chunk);
|
||||
g_free(s->uncompressed_chunk);
|
||||
|
||||
inflateEnd(&s->zstream);
|
||||
}
|
||||
|
||||
+44
-56
@@ -16,7 +16,6 @@ typedef struct GlusterAIOCB {
|
||||
int ret;
|
||||
QEMUBH *bh;
|
||||
Coroutine *coroutine;
|
||||
AioContext *aio_context;
|
||||
} GlusterAIOCB;
|
||||
|
||||
typedef struct BDRVGlusterState {
|
||||
@@ -208,11 +207,6 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
|
||||
"volume=%s image=%s transport=%s", gconf->server,
|
||||
gconf->port, gconf->volname, gconf->image,
|
||||
gconf->transport);
|
||||
|
||||
/* glfs_init sometimes doesn't set errno although docs suggest that */
|
||||
if (errno == 0)
|
||||
errno = EINVAL;
|
||||
|
||||
goto out;
|
||||
}
|
||||
return glfs;
|
||||
@@ -250,7 +244,7 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||
acb->ret = -EIO; /* Partial read/write - fail it */
|
||||
}
|
||||
|
||||
acb->bh = aio_bh_new(acb->aio_context, qemu_gluster_complete_aio, acb);
|
||||
acb->bh = qemu_bh_new(qemu_gluster_complete_aio, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
}
|
||||
|
||||
@@ -291,7 +285,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int open_flags = 0;
|
||||
int ret = 0;
|
||||
GlusterConf *gconf = g_new0(GlusterConf, 1);
|
||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
@@ -351,12 +345,12 @@ static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
|
||||
assert(state != NULL);
|
||||
assert(state->bs != NULL);
|
||||
|
||||
state->opaque = g_new0(BDRVGlusterReopenState, 1);
|
||||
state->opaque = g_malloc0(sizeof(BDRVGlusterReopenState));
|
||||
reop_s = state->opaque;
|
||||
|
||||
qemu_gluster_parse_flags(state->flags, &open_flags);
|
||||
|
||||
gconf = g_new0(GlusterConf, 1);
|
||||
gconf = g_malloc0(sizeof(GlusterConf));
|
||||
|
||||
reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, errp);
|
||||
if (reop_s->glfs == NULL) {
|
||||
@@ -437,7 +431,6 @@ static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
|
||||
acb->size = size;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
@@ -478,37 +471,39 @@ static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
||||
#endif
|
||||
|
||||
static int qemu_gluster_create(const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
QEMUOptionParameter *options, Error **errp)
|
||||
{
|
||||
struct glfs *glfs;
|
||||
struct glfs_fd *fd;
|
||||
int ret = 0;
|
||||
int prealloc = 0;
|
||||
int64_t total_size = 0;
|
||||
char *tmp = NULL;
|
||||
GlusterConf *gconf = g_new0(GlusterConf, 1);
|
||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||
|
||||
glfs = qemu_gluster_init(gconf, filename, errp);
|
||||
if (!glfs) {
|
||||
ret = -errno;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
if (!tmp || !strcmp(tmp, "off")) {
|
||||
prealloc = 0;
|
||||
} else if (!strcmp(tmp, "full") &&
|
||||
gluster_supports_zerofill()) {
|
||||
prealloc = 1;
|
||||
} else {
|
||||
error_setg(errp, "Invalid preallocation mode: '%s'"
|
||||
" or GlusterFS doesn't support zerofill API",
|
||||
tmp);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n / BDRV_SECTOR_SIZE;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
|
||||
if (!options->value.s || !strcmp(options->value.s, "off")) {
|
||||
prealloc = 0;
|
||||
} else if (!strcmp(options->value.s, "full") &&
|
||||
gluster_supports_zerofill()) {
|
||||
prealloc = 1;
|
||||
} else {
|
||||
error_setg(errp, "Invalid preallocation mode: '%s'"
|
||||
" or GlusterFS doesn't support zerofill API",
|
||||
options->value.s);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = glfs_creat(glfs, gconf->image,
|
||||
@@ -516,8 +511,9 @@ static int qemu_gluster_create(const char *filename,
|
||||
if (!fd) {
|
||||
ret = -errno;
|
||||
} else {
|
||||
if (!glfs_ftruncate(fd, total_size)) {
|
||||
if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) {
|
||||
if (!glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE)) {
|
||||
if (prealloc && qemu_gluster_zerofill(fd, 0,
|
||||
total_size * BDRV_SECTOR_SIZE)) {
|
||||
ret = -errno;
|
||||
}
|
||||
} else {
|
||||
@@ -529,7 +525,6 @@ static int qemu_gluster_create(const char *filename,
|
||||
}
|
||||
}
|
||||
out:
|
||||
g_free(tmp);
|
||||
qemu_gluster_gconf_free(gconf);
|
||||
if (glfs) {
|
||||
glfs_fini(glfs);
|
||||
@@ -549,7 +544,6 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
||||
acb->size = size;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
if (write) {
|
||||
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||
@@ -606,7 +600,6 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
@@ -635,7 +628,6 @@ static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
@@ -696,22 +688,18 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList qemu_gluster_create_opts = {
|
||||
.name = "qemu-gluster-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_PREALLOC,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Preallocation mode (allowed values: off, full)"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter qemu_gluster_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_PREALLOC,
|
||||
.type = OPT_STRING,
|
||||
.help = "Preallocation mode (allowed values: off, full)"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster = {
|
||||
@@ -738,7 +726,7 @@ static BlockDriver bdrv_gluster = {
|
||||
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||
#endif
|
||||
.create_opts = &qemu_gluster_create_opts,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster_tcp = {
|
||||
@@ -765,7 +753,7 @@ static BlockDriver bdrv_gluster_tcp = {
|
||||
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||
#endif
|
||||
.create_opts = &qemu_gluster_create_opts,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster_unix = {
|
||||
@@ -792,7 +780,7 @@ static BlockDriver bdrv_gluster_unix = {
|
||||
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||
#endif
|
||||
.create_opts = &qemu_gluster_create_opts,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster_rdma = {
|
||||
@@ -819,7 +807,7 @@ static BlockDriver bdrv_gluster_rdma = {
|
||||
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||
#endif
|
||||
.create_opts = &qemu_gluster_create_opts,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_gluster_init(void)
|
||||
|
||||
+439
-613
File diff suppressed because it is too large
Load Diff
+47
-189
@@ -25,10 +25,8 @@
|
||||
*/
|
||||
#define MAX_EVENTS 128
|
||||
|
||||
#define MAX_QUEUED_IO 128
|
||||
|
||||
struct qemu_laiocb {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
struct qemu_laio_state *ctx;
|
||||
struct iocb iocb;
|
||||
ssize_t ret;
|
||||
@@ -38,25 +36,9 @@ struct qemu_laiocb {
|
||||
QLIST_ENTRY(qemu_laiocb) node;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct iocb *iocbs[MAX_QUEUED_IO];
|
||||
int plugged;
|
||||
unsigned int size;
|
||||
unsigned int idx;
|
||||
} LaioQueue;
|
||||
|
||||
struct qemu_laio_state {
|
||||
io_context_t ctx;
|
||||
EventNotifier e;
|
||||
|
||||
/* io queue for submit at batch */
|
||||
LaioQueue io_q;
|
||||
|
||||
/* I/O completion processing */
|
||||
QEMUBH *completion_bh;
|
||||
struct io_event events[MAX_EVENTS];
|
||||
int event_idx;
|
||||
int event_max;
|
||||
};
|
||||
|
||||
static inline ssize_t io_event_ret(struct io_event *ev)
|
||||
@@ -85,167 +67,77 @@ static void qemu_laio_process_completion(struct qemu_laio_state *s,
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
laiocb->common.cb(laiocb->common.opaque, ret);
|
||||
|
||||
qemu_aio_unref(laiocb);
|
||||
}
|
||||
|
||||
/* The completion BH fetches completed I/O requests and invokes their
|
||||
* callbacks.
|
||||
*
|
||||
* The function is somewhat tricky because it supports nested event loops, for
|
||||
* example when a request callback invokes aio_poll(). In order to do this,
|
||||
* the completion events array and index are kept in qemu_laio_state. The BH
|
||||
* reschedules itself as long as there are completions pending so it will
|
||||
* either be called again in a nested event loop or will be called after all
|
||||
* events have been completed. When there are no events left to complete, the
|
||||
* BH returns without rescheduling.
|
||||
*/
|
||||
static void qemu_laio_completion_bh(void *opaque)
|
||||
{
|
||||
struct qemu_laio_state *s = opaque;
|
||||
|
||||
/* Fetch more completion events when empty */
|
||||
if (s->event_idx == s->event_max) {
|
||||
do {
|
||||
struct timespec ts = { 0 };
|
||||
s->event_max = io_getevents(s->ctx, MAX_EVENTS, MAX_EVENTS,
|
||||
s->events, &ts);
|
||||
} while (s->event_max == -EINTR);
|
||||
|
||||
s->event_idx = 0;
|
||||
if (s->event_max <= 0) {
|
||||
s->event_max = 0;
|
||||
return; /* no more events */
|
||||
}
|
||||
laiocb->common.cb(laiocb->common.opaque, ret);
|
||||
}
|
||||
|
||||
/* Reschedule so nested event loops see currently pending completions */
|
||||
qemu_bh_schedule(s->completion_bh);
|
||||
|
||||
/* Process completion events */
|
||||
while (s->event_idx < s->event_max) {
|
||||
struct iocb *iocb = s->events[s->event_idx].obj;
|
||||
struct qemu_laiocb *laiocb =
|
||||
container_of(iocb, struct qemu_laiocb, iocb);
|
||||
|
||||
laiocb->ret = io_event_ret(&s->events[s->event_idx]);
|
||||
s->event_idx++;
|
||||
|
||||
qemu_laio_process_completion(s, laiocb);
|
||||
}
|
||||
qemu_aio_release(laiocb);
|
||||
}
|
||||
|
||||
static void qemu_laio_completion_cb(EventNotifier *e)
|
||||
{
|
||||
struct qemu_laio_state *s = container_of(e, struct qemu_laio_state, e);
|
||||
|
||||
if (event_notifier_test_and_clear(&s->e)) {
|
||||
qemu_bh_schedule(s->completion_bh);
|
||||
while (event_notifier_test_and_clear(&s->e)) {
|
||||
struct io_event events[MAX_EVENTS];
|
||||
struct timespec ts = { 0 };
|
||||
int nevents, i;
|
||||
|
||||
do {
|
||||
nevents = io_getevents(s->ctx, MAX_EVENTS, MAX_EVENTS, events, &ts);
|
||||
} while (nevents == -EINTR);
|
||||
|
||||
for (i = 0; i < nevents; i++) {
|
||||
struct iocb *iocb = events[i].obj;
|
||||
struct qemu_laiocb *laiocb =
|
||||
container_of(iocb, struct qemu_laiocb, iocb);
|
||||
|
||||
laiocb->ret = io_event_ret(&events[i]);
|
||||
qemu_laio_process_completion(s, laiocb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void laio_cancel(BlockAIOCB *blockacb)
|
||||
static void laio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
struct qemu_laiocb *laiocb = (struct qemu_laiocb *)blockacb;
|
||||
struct io_event event;
|
||||
int ret;
|
||||
|
||||
if (laiocb->ret != -EINPROGRESS) {
|
||||
if (laiocb->ret != -EINPROGRESS)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that as of Linux 2.6.31 neither the block device code nor any
|
||||
* filesystem implements cancellation of AIO request.
|
||||
* Thus the polling loop below is the normal code path.
|
||||
*/
|
||||
ret = io_cancel(laiocb->ctx->ctx, &laiocb->iocb, &event);
|
||||
laiocb->ret = -ECANCELED;
|
||||
if (ret != 0) {
|
||||
/* iocb is not cancelled, cb will be called by the event loop later */
|
||||
if (ret == 0) {
|
||||
laiocb->ret = -ECANCELED;
|
||||
return;
|
||||
}
|
||||
|
||||
laiocb->common.cb(laiocb->common.opaque, laiocb->ret);
|
||||
/*
|
||||
* We have to wait for the iocb to finish.
|
||||
*
|
||||
* The only way to get the iocb status update is by polling the io context.
|
||||
* We might be able to do this slightly more optimal by removing the
|
||||
* O_NONBLOCK flag.
|
||||
*/
|
||||
while (laiocb->ret == -EINPROGRESS) {
|
||||
qemu_laio_completion_cb(&laiocb->ctx->e);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo laio_aiocb_info = {
|
||||
.aiocb_size = sizeof(struct qemu_laiocb),
|
||||
.cancel_async = laio_cancel,
|
||||
.cancel = laio_cancel,
|
||||
};
|
||||
|
||||
static void ioq_init(LaioQueue *io_q)
|
||||
{
|
||||
io_q->size = MAX_QUEUED_IO;
|
||||
io_q->idx = 0;
|
||||
io_q->plugged = 0;
|
||||
}
|
||||
|
||||
static int ioq_submit(struct qemu_laio_state *s)
|
||||
{
|
||||
int ret, i = 0;
|
||||
int len = s->io_q.idx;
|
||||
|
||||
do {
|
||||
ret = io_submit(s->ctx, len, s->io_q.iocbs);
|
||||
} while (i++ < 3 && ret == -EAGAIN);
|
||||
|
||||
/* empty io queue */
|
||||
s->io_q.idx = 0;
|
||||
|
||||
if (ret < 0) {
|
||||
i = 0;
|
||||
} else {
|
||||
i = ret;
|
||||
}
|
||||
|
||||
for (; i < len; i++) {
|
||||
struct qemu_laiocb *laiocb =
|
||||
container_of(s->io_q.iocbs[i], struct qemu_laiocb, iocb);
|
||||
|
||||
laiocb->ret = (ret < 0) ? ret : -EIO;
|
||||
qemu_laio_process_completion(s, laiocb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ioq_enqueue(struct qemu_laio_state *s, struct iocb *iocb)
|
||||
{
|
||||
unsigned int idx = s->io_q.idx;
|
||||
|
||||
s->io_q.iocbs[idx++] = iocb;
|
||||
s->io_q.idx = idx;
|
||||
|
||||
/* submit immediately if queue is full */
|
||||
if (idx == s->io_q.size) {
|
||||
ioq_submit(s);
|
||||
}
|
||||
}
|
||||
|
||||
void laio_io_plug(BlockDriverState *bs, void *aio_ctx)
|
||||
{
|
||||
struct qemu_laio_state *s = aio_ctx;
|
||||
|
||||
s->io_q.plugged++;
|
||||
}
|
||||
|
||||
int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug)
|
||||
{
|
||||
struct qemu_laio_state *s = aio_ctx;
|
||||
int ret = 0;
|
||||
|
||||
assert(s->io_q.plugged > 0 || !unplug);
|
||||
|
||||
if (unplug && --s->io_q.plugged > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->io_q.idx > 0) {
|
||||
ret = ioq_submit(s);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
struct qemu_laio_state *s = aio_ctx;
|
||||
struct qemu_laiocb *laiocb;
|
||||
@@ -276,36 +168,15 @@ BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
}
|
||||
io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e));
|
||||
|
||||
if (!s->io_q.plugged) {
|
||||
if (io_submit(s->ctx, 1, &iocbs) < 0) {
|
||||
goto out_free_aiocb;
|
||||
}
|
||||
} else {
|
||||
ioq_enqueue(s, iocbs);
|
||||
}
|
||||
if (io_submit(s->ctx, 1, &iocbs) < 0)
|
||||
goto out_free_aiocb;
|
||||
return &laiocb->common;
|
||||
|
||||
out_free_aiocb:
|
||||
qemu_aio_unref(laiocb);
|
||||
qemu_aio_release(laiocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void laio_detach_aio_context(void *s_, AioContext *old_context)
|
||||
{
|
||||
struct qemu_laio_state *s = s_;
|
||||
|
||||
aio_set_event_notifier(old_context, &s->e, NULL);
|
||||
qemu_bh_delete(s->completion_bh);
|
||||
}
|
||||
|
||||
void laio_attach_aio_context(void *s_, AioContext *new_context)
|
||||
{
|
||||
struct qemu_laio_state *s = s_;
|
||||
|
||||
s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s);
|
||||
aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb);
|
||||
}
|
||||
|
||||
void *laio_init(void)
|
||||
{
|
||||
struct qemu_laio_state *s;
|
||||
@@ -319,7 +190,7 @@ void *laio_init(void)
|
||||
goto out_close_efd;
|
||||
}
|
||||
|
||||
ioq_init(&s->io_q);
|
||||
qemu_aio_set_event_notifier(&s->e, qemu_laio_completion_cb);
|
||||
|
||||
return s;
|
||||
|
||||
@@ -329,16 +200,3 @@ out_free_state:
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void laio_cleanup(void *s_)
|
||||
{
|
||||
struct qemu_laio_state *s = s_;
|
||||
|
||||
event_notifier_cleanup(&s->e);
|
||||
|
||||
if (io_destroy(s->ctx) != 0) {
|
||||
fprintf(stderr, "%s: destroy AIO context %p failed\n",
|
||||
__func__, &s->ctx);
|
||||
}
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
+53
-141
@@ -32,12 +32,6 @@ typedef struct MirrorBlockJob {
|
||||
RateLimit limit;
|
||||
BlockDriverState *target;
|
||||
BlockDriverState *base;
|
||||
/* The name of the graph node to replace */
|
||||
char *replaces;
|
||||
/* The BDS to replace */
|
||||
BlockDriverState *to_replace;
|
||||
/* Used to block operations on the drive-mirror-replace target */
|
||||
Error *replace_blocker;
|
||||
bool is_none_mode;
|
||||
BlockdevOnError on_source_error, on_target_error;
|
||||
bool synced;
|
||||
@@ -45,7 +39,6 @@ typedef struct MirrorBlockJob {
|
||||
int64_t sector_num;
|
||||
int64_t granularity;
|
||||
size_t buf_size;
|
||||
int64_t bdev_length;
|
||||
unsigned long *cow_bitmap;
|
||||
BdrvDirtyBitmap *dirty_bitmap;
|
||||
HBitmapIter hbi;
|
||||
@@ -55,7 +48,6 @@ typedef struct MirrorBlockJob {
|
||||
|
||||
unsigned long *in_flight_bitmap;
|
||||
int in_flight;
|
||||
int sectors_in_flight;
|
||||
int ret;
|
||||
} MirrorBlockJob;
|
||||
|
||||
@@ -89,7 +81,6 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret);
|
||||
|
||||
s->in_flight--;
|
||||
s->sectors_in_flight -= op->nb_sectors;
|
||||
iov = op->qiov.iov;
|
||||
for (i = 0; i < op->qiov.niov; i++) {
|
||||
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
|
||||
@@ -101,11 +92,8 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
chunk_num = op->sector_num / sectors_per_chunk;
|
||||
nb_chunks = op->nb_sectors / sectors_per_chunk;
|
||||
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
|
||||
if (ret >= 0) {
|
||||
if (s->cow_bitmap) {
|
||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||
}
|
||||
s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE;
|
||||
if (s->cow_bitmap && ret >= 0) {
|
||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||
}
|
||||
|
||||
qemu_iovec_destroy(&op->qiov);
|
||||
@@ -130,7 +118,7 @@ static void mirror_write_complete(void *opaque, int ret)
|
||||
|
||||
bdrv_set_dirty(source, op->sector_num, op->nb_sectors);
|
||||
action = mirror_error_action(s, false, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
||||
if (action == BDRV_ACTION_REPORT && s->ret >= 0) {
|
||||
s->ret = ret;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +135,7 @@ static void mirror_read_complete(void *opaque, int ret)
|
||||
|
||||
bdrv_set_dirty(source, op->sector_num, op->nb_sectors);
|
||||
action = mirror_error_action(s, true, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
||||
if (action == BDRV_ACTION_REPORT && s->ret >= 0) {
|
||||
s->ret = ret;
|
||||
}
|
||||
|
||||
@@ -163,7 +151,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
BlockDriverState *source = s->common.bs;
|
||||
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;
|
||||
uint64_t delay_ns;
|
||||
MirrorOp *op;
|
||||
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
@@ -178,7 +166,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
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;
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
|
||||
/* Extend the QEMUIOVector to include all adjacent blocks that will
|
||||
* be copied in this operation.
|
||||
@@ -253,6 +241,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
next_chunk += added_chunks;
|
||||
if (!s->synced && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
}
|
||||
} while (delay_ns == 0 && next_sector < end);
|
||||
|
||||
@@ -290,7 +280,6 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
|
||||
/* 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);
|
||||
@@ -321,56 +310,9 @@ static void mirror_drain(MirrorBlockJob *s)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
} MirrorExitData;
|
||||
|
||||
static void mirror_exit(BlockJob *job, void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
MirrorExitData *data = opaque;
|
||||
AioContext *replace_aio_context = NULL;
|
||||
|
||||
if (s->to_replace) {
|
||||
replace_aio_context = bdrv_get_aio_context(s->to_replace);
|
||||
aio_context_acquire(replace_aio_context);
|
||||
}
|
||||
|
||||
if (s->should_complete && data->ret == 0) {
|
||||
BlockDriverState *to_replace = s->common.bs;
|
||||
if (s->to_replace) {
|
||||
to_replace = s->to_replace;
|
||||
}
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
|
||||
bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
|
||||
}
|
||||
bdrv_swap(s->target, to_replace);
|
||||
if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
|
||||
/* drop the bs loop chain formed by the swap: break the loop then
|
||||
* trigger the unref from the top one */
|
||||
BlockDriverState *p = s->base->backing_hd;
|
||||
bdrv_set_backing_hd(s->base, NULL);
|
||||
bdrv_unref(p);
|
||||
}
|
||||
}
|
||||
if (s->to_replace) {
|
||||
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
|
||||
error_free(s->replace_blocker);
|
||||
bdrv_unref(s->to_replace);
|
||||
}
|
||||
if (replace_aio_context) {
|
||||
aio_context_release(replace_aio_context);
|
||||
}
|
||||
g_free(s->replaces);
|
||||
bdrv_unref(s->target);
|
||||
block_job_completed(&s->common, data->ret);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void coroutine_fn mirror_run(void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = opaque;
|
||||
MirrorExitData *data;
|
||||
BlockDriverState *bs = s->common.bs;
|
||||
int64_t sector_num, end, sectors_per_chunk, length;
|
||||
uint64_t last_pause_ns;
|
||||
@@ -383,13 +325,13 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
s->bdev_length = bdrv_getlength(bs);
|
||||
if (s->bdev_length < 0) {
|
||||
ret = s->bdev_length;
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
ret = s->common.len;
|
||||
goto immediate_exit;
|
||||
} else if (s->bdev_length == 0) {
|
||||
} else if (s->common.len == 0) {
|
||||
/* Report BLOCK_JOB_READY and wait for complete. */
|
||||
block_job_event_ready(&s->common);
|
||||
block_job_ready(&s->common);
|
||||
s->synced = true;
|
||||
while (!block_job_is_cancelled(&s->common) && !s->should_complete) {
|
||||
block_job_yield(&s->common);
|
||||
@@ -398,7 +340,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
length = DIV_ROUND_UP(s->bdev_length, s->granularity);
|
||||
length = (bdrv_getlength(bs) + s->granularity - 1) / s->granularity;
|
||||
s->in_flight_bitmap = bitmap_new(length);
|
||||
|
||||
/* If we have no backing file yet in the destination, we cannot let
|
||||
@@ -408,23 +350,15 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
bdrv_get_backing_filename(s->target, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
if (backing_filename[0] && !s->target->backing_hd) {
|
||||
ret = bdrv_get_info(s->target, &bdi);
|
||||
if (ret < 0) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
bdrv_get_info(s->target, &bdi);
|
||||
if (s->granularity < bdi.cluster_size) {
|
||||
s->buf_size = MAX(s->buf_size, bdi.cluster_size);
|
||||
s->cow_bitmap = bitmap_new(length);
|
||||
}
|
||||
}
|
||||
|
||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
||||
if (s->buf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
s->buf = qemu_blockalign(bs, s->buf_size);
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
mirror_free_init(s);
|
||||
|
||||
@@ -463,12 +397,6 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
|
||||
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
|
||||
/* s->common.offset contains the number of bytes already processed so
|
||||
* far, cnt is the number of dirty sectors remaining and
|
||||
* s->sectors_in_flight is the number of sectors currently being
|
||||
* processed; together those are the current total operation length */
|
||||
s->common.len = s->common.offset +
|
||||
(cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* periodically with no pending I/O so that qemu_aio_flush() returns.
|
||||
@@ -495,8 +423,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
trace_mirror_before_flush(s);
|
||||
ret = bdrv_flush(s->target);
|
||||
if (ret < 0) {
|
||||
if (mirror_error_action(s, false, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT) {
|
||||
if (mirror_error_action(s, false, -ret) == BDRV_ACTION_REPORT) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
} else {
|
||||
@@ -505,8 +432,9 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
* report completion. This way, block-job-cancel will leave
|
||||
* the target in a consistent state.
|
||||
*/
|
||||
s->common.offset = end * BDRV_SECTOR_SIZE;
|
||||
if (!s->synced) {
|
||||
block_job_event_ready(&s->common);
|
||||
block_job_ready(&s->common);
|
||||
s->synced = true;
|
||||
}
|
||||
|
||||
@@ -526,13 +454,15 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
* mirror_populate runs.
|
||||
*/
|
||||
trace_mirror_before_drain(s, cnt);
|
||||
bdrv_drain(bs);
|
||||
bdrv_drain_all();
|
||||
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
|
||||
if (!s->synced) {
|
||||
/* Publish progress */
|
||||
s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE;
|
||||
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
@@ -567,10 +497,21 @@ immediate_exit:
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||
bdrv_iostatus_disable(s->target);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&s->common, mirror_exit, data);
|
||||
if (s->should_complete && ret == 0) {
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) {
|
||||
bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL);
|
||||
}
|
||||
bdrv_swap(s->target, s->common.bs);
|
||||
if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
|
||||
/* drop the bs loop chain formed by the swap: break the loop then
|
||||
* trigger the unref from the top one */
|
||||
BlockDriverState *p = s->base->backing_hd;
|
||||
s->base->backing_hd = NULL;
|
||||
bdrv_unref(p);
|
||||
}
|
||||
}
|
||||
bdrv_unref(s->target);
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
|
||||
static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -603,32 +544,10 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
return;
|
||||
}
|
||||
if (!s->synced) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY,
|
||||
bdrv_get_device_name(job->bs));
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check the target bs is not blocked and block all operations on it */
|
||||
if (s->replaces) {
|
||||
AioContext *replace_aio_context;
|
||||
|
||||
s->to_replace = check_to_replace_node(s->replaces, &local_err);
|
||||
if (!s->to_replace) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
replace_aio_context = bdrv_get_aio_context(s->to_replace);
|
||||
aio_context_acquire(replace_aio_context);
|
||||
|
||||
error_setg(&s->replace_blocker,
|
||||
"block device is in use by block-job-complete");
|
||||
bdrv_op_block_all(s->to_replace, s->replace_blocker);
|
||||
bdrv_ref(s->to_replace);
|
||||
|
||||
aio_context_release(replace_aio_context);
|
||||
}
|
||||
|
||||
s->should_complete = true;
|
||||
block_job_resume(job);
|
||||
}
|
||||
@@ -651,15 +570,14 @@ static const BlockJobDriver commit_active_job_driver = {
|
||||
};
|
||||
|
||||
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
const char *replaces,
|
||||
int64_t speed, int64_t granularity,
|
||||
int64_t buf_size,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp,
|
||||
const BlockJobDriver *driver,
|
||||
bool is_none_mode, BlockDriverState *base)
|
||||
int64_t speed, int64_t granularity,
|
||||
int64_t buf_size,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp,
|
||||
const BlockJobDriver *driver,
|
||||
bool is_none_mode, BlockDriverState *base)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
|
||||
@@ -690,7 +608,6 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
return;
|
||||
}
|
||||
|
||||
s->replaces = g_strdup(replaces);
|
||||
s->on_source_error = on_source_error;
|
||||
s->on_target_error = on_target_error;
|
||||
s->target = target;
|
||||
@@ -699,10 +616,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
s->granularity = granularity;
|
||||
s->buf_size = MAX(buf_size, granularity);
|
||||
|
||||
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, errp);
|
||||
if (!s->dirty_bitmap) {
|
||||
return;
|
||||
}
|
||||
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity);
|
||||
bdrv_set_enable_write_cache(s->target, true);
|
||||
bdrv_set_on_error(s->target, on_target_error, on_target_error);
|
||||
bdrv_iostatus_enable(s->target);
|
||||
@@ -712,11 +626,10 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
}
|
||||
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
const char *replaces,
|
||||
int64_t speed, int64_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool is_none_mode;
|
||||
@@ -724,8 +637,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||
base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
|
||||
mirror_start_job(bs, target, replaces,
|
||||
speed, granularity, buf_size,
|
||||
mirror_start_job(bs, target, speed, granularity, buf_size,
|
||||
on_source_error, on_target_error, cb, opaque, errp,
|
||||
&mirror_job_driver, is_none_mode, base);
|
||||
}
|
||||
@@ -733,7 +645,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
int64_t length, base_length;
|
||||
@@ -773,10 +685,10 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
}
|
||||
|
||||
bdrv_ref(base);
|
||||
mirror_start_job(bs, base, NULL, speed, 0, 0,
|
||||
mirror_start_job(bs, base, speed, 0, 0,
|
||||
on_error, on_error, cb, opaque, &local_err,
|
||||
&commit_active_job_driver, false, base);
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto error_restore_flags;
|
||||
}
|
||||
|
||||
+4
-20
@@ -49,7 +49,7 @@ static void nbd_teardown_connection(NbdClientSession *client)
|
||||
shutdown(client->sock, 2);
|
||||
nbd_recv_coroutines_enter_all(client);
|
||||
|
||||
nbd_client_session_detach_aio_context(client);
|
||||
qemu_aio_set_fd_handler(client->sock, NULL, NULL, NULL);
|
||||
closesocket(client->sock);
|
||||
client->sock = -1;
|
||||
}
|
||||
@@ -103,14 +103,11 @@ static int nbd_co_send_request(NbdClientSession *s,
|
||||
struct nbd_request *request,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
int rc, ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->send_mutex);
|
||||
s->send_coroutine = qemu_coroutine_self();
|
||||
aio_context = bdrv_get_aio_context(s->bs);
|
||||
aio_set_fd_handler(aio_context, s->sock,
|
||||
nbd_reply_ready, nbd_restart_write, s);
|
||||
qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, s);
|
||||
if (qiov) {
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 1);
|
||||
@@ -129,7 +126,7 @@ static int nbd_co_send_request(NbdClientSession *s,
|
||||
} else {
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
}
|
||||
aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, s);
|
||||
qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, s);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return rc;
|
||||
@@ -338,19 +335,6 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
||||
|
||||
}
|
||||
|
||||
void nbd_client_session_detach_aio_context(NbdClientSession *client)
|
||||
{
|
||||
aio_set_fd_handler(bdrv_get_aio_context(client->bs), client->sock,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void nbd_client_session_attach_aio_context(NbdClientSession *client,
|
||||
AioContext *new_context)
|
||||
{
|
||||
aio_set_fd_handler(new_context, client->sock,
|
||||
nbd_reply_ready, NULL, client);
|
||||
}
|
||||
|
||||
void nbd_client_session_close(NbdClientSession *client)
|
||||
{
|
||||
struct nbd_request request = {
|
||||
@@ -397,7 +381,7 @@ int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
|
||||
/* Now that we're connected, set the socket to be non-blocking and
|
||||
* kick the reply mechanism. */
|
||||
qemu_set_nonblock(sock);
|
||||
nbd_client_session_attach_aio_context(client, bdrv_get_aio_context(bs));
|
||||
qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, client);
|
||||
|
||||
logout("Established connection with NBD server\n");
|
||||
return 0;
|
||||
|
||||
@@ -47,8 +47,4 @@ int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
|
||||
int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
|
||||
void nbd_client_session_detach_aio_context(NbdClientSession *client);
|
||||
void nbd_client_session_attach_aio_context(NbdClientSession *client,
|
||||
AioContext *new_context);
|
||||
|
||||
#endif /* NBD_CLIENT_H */
|
||||
|
||||
+34
-105
@@ -31,10 +31,8 @@
|
||||
#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>
|
||||
@@ -177,7 +175,7 @@ static void nbd_parse_filename(const char *filename, QDict *options,
|
||||
InetSocketAddress *addr = NULL;
|
||||
|
||||
addr = inet_parse(host_spec, errp);
|
||||
if (!addr) {
|
||||
if (error_is_set(errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -325,115 +323,46 @@ static int64_t nbd_getlength(BlockDriverState *bs)
|
||||
return s->client.size;
|
||||
}
|
||||
|
||||
static void nbd_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
nbd_client_session_detach_aio_context(&s->client);
|
||||
}
|
||||
|
||||
static void nbd_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
nbd_client_session_attach_aio_context(&s->client, new_context);
|
||||
}
|
||||
|
||||
static void nbd_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
QDict *opts = qdict_new();
|
||||
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")));
|
||||
|
||||
if (path && export) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"nbd+unix:///%s?socket=%s", export, path);
|
||||
} else if (path && !export) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"nbd+unix://?socket=%s", path);
|
||||
} else if (!path && export && port) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"nbd://%s:%s/%s", host, port, export);
|
||||
} else if (!path && export && !port) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"nbd://%s/%s", host, export);
|
||||
} else if (!path && !export && port) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"nbd://%s:%s", host, port);
|
||||
} else if (!path && !export && !port) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"nbd://%s", host);
|
||||
}
|
||||
|
||||
if (path) {
|
||||
qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path)));
|
||||
} else if (port) {
|
||||
qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host)));
|
||||
qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(port)));
|
||||
} else {
|
||||
qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host)));
|
||||
}
|
||||
if (export) {
|
||||
qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
|
||||
}
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_nbd = {
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_tcp = {
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd+tcp",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd+tcp",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_unix = {
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd+unix",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd+unix",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
|
||||
+42
-109
@@ -40,7 +40,6 @@ typedef struct NFSClient {
|
||||
struct nfsfh *fh;
|
||||
int events;
|
||||
bool has_zero_init;
|
||||
AioContext *aio_context;
|
||||
} NFSClient;
|
||||
|
||||
typedef struct NFSRPC {
|
||||
@@ -50,7 +49,6 @@ typedef struct NFSRPC {
|
||||
struct stat *st;
|
||||
Coroutine *co;
|
||||
QEMUBH *bh;
|
||||
NFSClient *client;
|
||||
} NFSRPC;
|
||||
|
||||
static void nfs_process_read(void *arg);
|
||||
@@ -60,11 +58,10 @@ static void nfs_set_events(NFSClient *client)
|
||||
{
|
||||
int ev = nfs_which_events(client->context);
|
||||
if (ev != client->events) {
|
||||
aio_set_fd_handler(client->aio_context,
|
||||
nfs_get_fd(client->context),
|
||||
(ev & POLLIN) ? nfs_process_read : NULL,
|
||||
(ev & POLLOUT) ? nfs_process_write : NULL,
|
||||
client);
|
||||
qemu_aio_set_fd_handler(nfs_get_fd(client->context),
|
||||
(ev & POLLIN) ? nfs_process_read : NULL,
|
||||
(ev & POLLOUT) ? nfs_process_write : NULL,
|
||||
client);
|
||||
|
||||
}
|
||||
client->events = ev;
|
||||
@@ -87,15 +84,13 @@ static void nfs_process_write(void *arg)
|
||||
static void nfs_co_init_task(NFSClient *client, NFSRPC *task)
|
||||
{
|
||||
*task = (NFSRPC) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.client = client,
|
||||
.co = qemu_coroutine_self(),
|
||||
};
|
||||
}
|
||||
|
||||
static void nfs_co_generic_bh_cb(void *opaque)
|
||||
{
|
||||
NFSRPC *task = opaque;
|
||||
task->complete = 1;
|
||||
qemu_bh_delete(task->bh);
|
||||
qemu_coroutine_enter(task->co, NULL);
|
||||
}
|
||||
@@ -105,6 +100,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
|
||||
void *private_data)
|
||||
{
|
||||
NFSRPC *task = private_data;
|
||||
task->complete = 1;
|
||||
task->ret = ret;
|
||||
if (task->ret > 0 && task->iov) {
|
||||
if (task->ret <= task->iov->size) {
|
||||
@@ -120,11 +116,8 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
|
||||
error_report("NFS Error: %s", nfs_get_error(nfs));
|
||||
}
|
||||
if (task->co) {
|
||||
task->bh = aio_bh_new(task->client->aio_context,
|
||||
nfs_co_generic_bh_cb, task);
|
||||
task->bh = qemu_bh_new(nfs_co_generic_bh_cb, task);
|
||||
qemu_bh_schedule(task->bh);
|
||||
} else {
|
||||
task->complete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,11 +165,7 @@ static int coroutine_fn nfs_co_writev(BlockDriverState *bs,
|
||||
|
||||
nfs_co_init_task(client, &task);
|
||||
|
||||
buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE);
|
||||
if (nb_sectors && buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf = g_malloc(nb_sectors * BDRV_SECTOR_SIZE);
|
||||
qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE);
|
||||
|
||||
if (nfs_pwrite_async(client->context, client->fh,
|
||||
@@ -235,34 +224,13 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static void nfs_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(client->aio_context,
|
||||
nfs_get_fd(client->context),
|
||||
NULL, NULL, NULL);
|
||||
client->events = 0;
|
||||
}
|
||||
|
||||
static void nfs_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
|
||||
client->aio_context = new_context;
|
||||
nfs_set_events(client);
|
||||
}
|
||||
|
||||
static void nfs_client_close(NFSClient *client)
|
||||
{
|
||||
if (client->context) {
|
||||
if (client->fh) {
|
||||
nfs_close(client->context, client->fh);
|
||||
}
|
||||
aio_set_fd_handler(client->aio_context,
|
||||
nfs_get_fd(client->context),
|
||||
NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(nfs_get_fd(client->context), NULL, NULL, NULL);
|
||||
nfs_destroy_context(client->context);
|
||||
}
|
||||
memset(client, 0, sizeof(NFSClient));
|
||||
@@ -288,10 +256,6 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||
error_setg(errp, "Invalid URL specified");
|
||||
goto fail;
|
||||
}
|
||||
if (!uri->server) {
|
||||
error_setg(errp, "Invalid URL specified");
|
||||
goto fail;
|
||||
}
|
||||
strp = strrchr(uri->path, '/');
|
||||
if (strp == NULL) {
|
||||
error_setg(errp, "Invalid URL specified");
|
||||
@@ -308,27 +272,17 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||
|
||||
qp = query_params_parse(uri->query);
|
||||
for (i = 0; i < qp->n; i++) {
|
||||
unsigned long long val;
|
||||
if (!qp->p[i].value) {
|
||||
error_setg(errp, "Value for NFS parameter expected: %s",
|
||||
qp->p[i].name);
|
||||
goto fail;
|
||||
}
|
||||
if (parse_uint_full(qp->p[i].value, &val, 0)) {
|
||||
error_setg(errp, "Illegal value for NFS parameter: %s",
|
||||
qp->p[i].name);
|
||||
goto fail;
|
||||
}
|
||||
if (!strcmp(qp->p[i].name, "uid")) {
|
||||
nfs_set_uid(client->context, val);
|
||||
} else if (!strcmp(qp->p[i].name, "gid")) {
|
||||
nfs_set_gid(client->context, val);
|
||||
} else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
|
||||
nfs_set_tcp_syncnt(client->context, val);
|
||||
#ifdef LIBNFS_FEATURE_READAHEAD
|
||||
} else if (!strcmp(qp->p[i].name, "readahead")) {
|
||||
nfs_set_readahead(client->context, val);
|
||||
#endif
|
||||
if (!strncmp(qp->p[i].name, "uid", 3)) {
|
||||
nfs_set_uid(client->context, atoi(qp->p[i].value));
|
||||
} else if (!strncmp(qp->p[i].name, "gid", 3)) {
|
||||
nfs_set_gid(client->context, atoi(qp->p[i].value));
|
||||
} else if (!strncmp(qp->p[i].name, "tcp-syncnt", 10)) {
|
||||
nfs_set_tcp_syncnt(client->context, atoi(qp->p[i].value));
|
||||
} else {
|
||||
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||
qp->p[i].name);
|
||||
@@ -387,52 +341,36 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
|
||||
client->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = nfs_client_open(client, qemu_opt_get(opts, "filename"),
|
||||
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
bs->total_sectors = ret;
|
||||
ret = 0;
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList nfs_create_opts = {
|
||||
.name = "nfs-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp)
|
||||
static int nfs_file_create(const char *url, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t total_size = 0;
|
||||
NFSClient *client = g_new0(NFSClient, 1);
|
||||
|
||||
client->aio_context = qemu_get_aio_context();
|
||||
NFSClient *client = g_malloc0(sizeof(NFSClient));
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, "size")) {
|
||||
total_size = options->value.n;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = nfs_client_open(client, url, O_CREAT, errp);
|
||||
if (ret < 0) {
|
||||
@@ -465,7 +403,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
|
||||
|
||||
while (!task.complete) {
|
||||
nfs_set_events(client);
|
||||
aio_poll(client->aio_context, true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize);
|
||||
@@ -478,27 +416,22 @@ static int nfs_file_truncate(BlockDriverState *bs, int64_t offset)
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_nfs = {
|
||||
.format_name = "nfs",
|
||||
.protocol_name = "nfs",
|
||||
.format_name = "nfs",
|
||||
.protocol_name = "nfs",
|
||||
|
||||
.instance_size = sizeof(NFSClient),
|
||||
.bdrv_needs_filename = true,
|
||||
.create_opts = &nfs_create_opts,
|
||||
.instance_size = sizeof(NFSClient),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_has_zero_init = nfs_has_zero_init,
|
||||
.bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
|
||||
.bdrv_truncate = nfs_file_truncate,
|
||||
|
||||
.bdrv_has_zero_init = nfs_has_zero_init,
|
||||
.bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
|
||||
.bdrv_truncate = nfs_file_truncate,
|
||||
.bdrv_file_open = nfs_file_open,
|
||||
.bdrv_close = nfs_file_close,
|
||||
.bdrv_create = nfs_file_create,
|
||||
|
||||
.bdrv_file_open = nfs_file_open,
|
||||
.bdrv_close = nfs_file_close,
|
||||
.bdrv_create = nfs_file_create,
|
||||
|
||||
.bdrv_co_readv = nfs_co_readv,
|
||||
.bdrv_co_writev = nfs_co_writev,
|
||||
.bdrv_co_flush_to_disk = nfs_co_flush,
|
||||
|
||||
.bdrv_detach_aio_context = nfs_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nfs_attach_aio_context,
|
||||
.bdrv_co_readv = nfs_co_readv,
|
||||
.bdrv_co_writev = nfs_co_writev,
|
||||
.bdrv_co_flush_to_disk = nfs_co_flush,
|
||||
};
|
||||
|
||||
static void nfs_block_init(void)
|
||||
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Null block driver
|
||||
*
|
||||
* Authors:
|
||||
* Fam Zheng <famz@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
*
|
||||
* 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 "block/block_int.h"
|
||||
|
||||
typedef struct {
|
||||
int64_t length;
|
||||
} BDRVNullState;
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "null",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "",
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "size of the null block",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &error_abort);
|
||||
s->length =
|
||||
qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void null_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int64_t null_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
return s->length;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BlockAIOCB common;
|
||||
QEMUBH *bh;
|
||||
} NullAIOCB;
|
||||
|
||||
static const AIOCBInfo null_aiocb_info = {
|
||||
.aiocb_size = sizeof(NullAIOCB),
|
||||
};
|
||||
|
||||
static void null_bh_cb(void *opaque)
|
||||
{
|
||||
NullAIOCB *acb = opaque;
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
NullAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_null_co = {
|
||||
.format_name = "null-co",
|
||||
.protocol_name = "null-co",
|
||||
.instance_size = sizeof(BDRVNullState),
|
||||
|
||||
.bdrv_file_open = null_file_open,
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_co_readv = null_co_readv,
|
||||
.bdrv_co_writev = null_co_writev,
|
||||
.bdrv_co_flush_to_disk = null_co_flush,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_null_aio = {
|
||||
.format_name = "null-aio",
|
||||
.protocol_name = "null-aio",
|
||||
.instance_size = sizeof(BDRVNullState),
|
||||
|
||||
.bdrv_file_open = null_file_open,
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_aio_readv = null_aio_readv,
|
||||
.bdrv_aio_writev = null_aio_writev,
|
||||
.bdrv_aio_flush = null_aio_flush,
|
||||
};
|
||||
|
||||
static void bdrv_null_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_null_co);
|
||||
bdrv_register(&bdrv_null_aio);
|
||||
}
|
||||
|
||||
block_init(bdrv_null_init);
|
||||
+18
-42
@@ -30,7 +30,6 @@
|
||||
/**************************************************************/
|
||||
|
||||
#define HEADER_MAGIC "WithoutFreeSpace"
|
||||
#define HEADER_MAGIC2 "WithouFreSpacExt"
|
||||
#define HEADER_VERSION 2
|
||||
#define HEADER_SIZE 64
|
||||
|
||||
@@ -42,10 +41,8 @@ struct parallels_header {
|
||||
uint32_t cylinders;
|
||||
uint32_t tracks;
|
||||
uint32_t catalog_entries;
|
||||
uint64_t nb_sectors;
|
||||
uint32_t inuse;
|
||||
uint32_t data_off;
|
||||
char padding[12];
|
||||
uint32_t nb_sectors;
|
||||
char padding[24];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct BDRVParallelsState {
|
||||
@@ -55,8 +52,6 @@ typedef struct BDRVParallelsState {
|
||||
unsigned int catalog_size;
|
||||
|
||||
unsigned int tracks;
|
||||
|
||||
unsigned int off_multiplier;
|
||||
} BDRVParallelsState;
|
||||
|
||||
static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
@@ -64,12 +59,11 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
|
||||
const struct parallels_header *ph = (const void *)buf;
|
||||
|
||||
if (buf_size < HEADER_SIZE)
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
|
||||
!memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
|
||||
(le32_to_cpu(ph->version) == HEADER_VERSION))
|
||||
return 100;
|
||||
if (!memcmp(ph->magic, HEADER_MAGIC, 16) &&
|
||||
(le32_to_cpu(ph->version) == HEADER_VERSION))
|
||||
return 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -89,19 +83,14 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->total_sectors = le64_to_cpu(ph.nb_sectors);
|
||||
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
|
||||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
|
||||
error_setg(errp, "Image not in Parallels format");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(ph.version) != HEADER_VERSION) {
|
||||
goto fail_format;
|
||||
}
|
||||
if (!memcmp(ph.magic, HEADER_MAGIC, 16)) {
|
||||
s->off_multiplier = 1;
|
||||
bs->total_sectors = 0xffffffff & bs->total_sectors;
|
||||
} else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) {
|
||||
s->off_multiplier = le32_to_cpu(ph.tracks);
|
||||
} else {
|
||||
goto fail_format;
|
||||
}
|
||||
bs->total_sectors = le32_to_cpu(ph.nb_sectors);
|
||||
|
||||
s->tracks = le32_to_cpu(ph.tracks);
|
||||
if (s->tracks == 0) {
|
||||
@@ -109,11 +98,6 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (s->tracks > INT32_MAX/513) {
|
||||
error_setg(errp, "Invalid image: Too big cluster");
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->catalog_size = le32_to_cpu(ph.catalog_entries);
|
||||
if (s->catalog_size > INT_MAX / 4) {
|
||||
@@ -121,11 +105,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
s->catalog_bitmap = g_try_new(uint32_t, s->catalog_size);
|
||||
if (s->catalog_size && s->catalog_bitmap == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||
|
||||
ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4);
|
||||
if (ret < 0) {
|
||||
@@ -133,14 +113,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
for (i = 0; i < s->catalog_size; i++)
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
|
||||
fail_format:
|
||||
error_setg(errp, "Image not in Parallels format");
|
||||
ret = -EINVAL;
|
||||
fail:
|
||||
g_free(s->catalog_bitmap);
|
||||
return ret;
|
||||
@@ -155,10 +132,9 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
offset = sector_num % s->tracks;
|
||||
|
||||
/* not allocated */
|
||||
if ((index >= s->catalog_size) || (s->catalog_bitmap[index] == 0))
|
||||
return -1;
|
||||
return
|
||||
((uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset) * 512;
|
||||
if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
|
||||
return -1;
|
||||
return (uint64_t)(s->catalog_bitmap[index] + offset) * 512;
|
||||
}
|
||||
|
||||
static int parallels_read(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
+29
-41
@@ -28,7 +28,6 @@
|
||||
#include "qapi-visit.h"
|
||||
#include "qapi/qmp-output-visitor.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
|
||||
{
|
||||
@@ -51,7 +50,6 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||
info->detect_zeroes = bs->detect_zeroes;
|
||||
|
||||
if (bs->io_limits_enabled) {
|
||||
ThrottleConfig cfg;
|
||||
@@ -166,25 +164,19 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
ImageInfo **p_info,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t size;
|
||||
uint64_t total_sectors;
|
||||
const char *backing_filename;
|
||||
char backing_filename2[1024];
|
||||
BlockDriverInfo bdi;
|
||||
int ret;
|
||||
Error *err = NULL;
|
||||
ImageInfo *info;
|
||||
ImageInfo *info = g_new0(ImageInfo, 1);
|
||||
|
||||
size = bdrv_getlength(bs);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "Can't get size of device '%s'",
|
||||
bdrv_get_device_name(bs));
|
||||
return;
|
||||
}
|
||||
bdrv_get_geometry(bs, &total_sectors);
|
||||
|
||||
info = g_new0(ImageInfo, 1);
|
||||
info->filename = g_strdup(bs->filename);
|
||||
info->format = g_strdup(bdrv_get_format_name(bs));
|
||||
info->virtual_size = size;
|
||||
info->virtual_size = total_sectors * 512;
|
||||
info->actual_size = bdrv_get_allocated_file_size(bs);
|
||||
info->has_actual_size = info->actual_size >= 0;
|
||||
if (bdrv_is_encrypted(bs)) {
|
||||
@@ -243,22 +235,22 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* @p_info will be set only on success. */
|
||||
static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
Error **errp)
|
||||
void bdrv_query_info(BlockDriverState *bs,
|
||||
BlockInfo **p_info,
|
||||
Error **errp)
|
||||
{
|
||||
BlockInfo *info = g_malloc0(sizeof(*info));
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverState *bs0;
|
||||
ImageInfo **p_image_info;
|
||||
Error *local_err = NULL;
|
||||
info->device = g_strdup(blk_name(blk));
|
||||
info->device = g_strdup(bs->device_name);
|
||||
info->type = g_strdup("unknown");
|
||||
info->locked = blk_dev_is_medium_locked(blk);
|
||||
info->removable = blk_dev_has_removable_media(blk);
|
||||
info->locked = bdrv_dev_is_medium_locked(bs);
|
||||
info->removable = bdrv_dev_has_removable_media(bs);
|
||||
|
||||
if (blk_dev_has_removable_media(blk)) {
|
||||
if (bdrv_dev_has_removable_media(bs)) {
|
||||
info->has_tray_open = true;
|
||||
info->tray_open = blk_dev_is_tray_open(blk);
|
||||
info->tray_open = bdrv_dev_is_tray_open(bs);
|
||||
}
|
||||
|
||||
if (bdrv_iostatus_is_enabled(bs)) {
|
||||
@@ -300,28 +292,27 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||
{
|
||||
BlockStats *s;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
|
||||
if (bdrv_get_device_name(bs)[0]) {
|
||||
if (bs->device_name[0]) {
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(bdrv_get_device_name(bs));
|
||||
s->device = g_strdup(bs->device_name);
|
||||
}
|
||||
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
s->stats->rd_bytes = bs->stats.nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = bs->stats.nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = bs->stats.nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = bs->stats.nr_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->wr_highest_offset =
|
||||
bs->stats.wr_highest_sector * BDRV_SECTOR_SIZE;
|
||||
s->stats->flush_operations = bs->stats.nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ];
|
||||
s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE];
|
||||
s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ];
|
||||
s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE];
|
||||
s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE;
|
||||
s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH];
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
@@ -339,12 +330,12 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||
BlockInfoList *qmp_query_block(Error **errp)
|
||||
{
|
||||
BlockInfoList *head = NULL, **p_next = &head;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
BlockInfoList *info = g_malloc0(sizeof(*info));
|
||||
bdrv_query_info(blk, &info->value, &local_err);
|
||||
bdrv_query_info(bs, &info->value, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto err;
|
||||
@@ -368,11 +359,7 @@ BlockStatsList *qmp_query_blockstats(Error **errp)
|
||||
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
info->value = bdrv_query_stats(bs);
|
||||
aio_context_release(ctx);
|
||||
|
||||
*p_next = info;
|
||||
p_next = &info->next;
|
||||
@@ -546,11 +533,12 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfoSpecific *info_spec)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||
QObject *obj, *data;
|
||||
|
||||
visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL,
|
||||
&error_abort);
|
||||
&local_err);
|
||||
obj = qmp_output_get_qobject(ov);
|
||||
assert(qobject_type(obj) == QTYPE_QDICT);
|
||||
data = qdict_get(qobject_to_qdict(obj), "data");
|
||||
|
||||
+48
-70
@@ -121,10 +121,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
if (header.version != QCOW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version), "QCOW version %" PRIu32,
|
||||
header.version);
|
||||
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bdrv_get_device_name(bs), "qcow", version);
|
||||
bs->device_name, "qcow", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -182,12 +181,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
s->l1_table_offset = header.l1_table_offset;
|
||||
s->l1_table = g_try_new(uint64_t, s->l1_size);
|
||||
if (s->l1_table == NULL) {
|
||||
error_setg(errp, "Could not allocate memory for L1 table");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
||||
|
||||
ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
@@ -198,16 +192,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
|
||||
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
||||
s->l2_cache =
|
||||
qemu_try_blockalign(bs->file,
|
||||
s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
if (s->l2_cache == NULL) {
|
||||
error_setg(errp, "Could not allocate L2 table cache");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
/* alloc L2 cache */
|
||||
s->l2_cache = g_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
s->cluster_cache = g_malloc(s->cluster_size);
|
||||
s->cluster_data = g_malloc(s->cluster_size);
|
||||
s->cluster_cache_offset = -1;
|
||||
@@ -231,7 +217,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Disable migration when qcow images are used */
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"qcow", bdrv_get_device_name(bs), "live migration");
|
||||
"qcow", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
@@ -239,7 +225,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
fail:
|
||||
g_free(s->l1_table);
|
||||
qemu_vfree(s->l2_cache);
|
||||
g_free(s->l2_cache);
|
||||
g_free(s->cluster_cache);
|
||||
g_free(s->cluster_data);
|
||||
return ret;
|
||||
@@ -530,10 +516,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
void *orig_buf;
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
|
||||
if (buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = orig_buf = qemu_blockalign(bs, qiov->size);
|
||||
} else {
|
||||
orig_buf = NULL;
|
||||
buf = (uint8_t *)qiov->iov->iov_base;
|
||||
@@ -635,10 +618,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
|
||||
if (buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = orig_buf = qemu_blockalign(bs, qiov->size);
|
||||
qemu_iovec_to_buf(qiov, 0, buf, qiov->size);
|
||||
} else {
|
||||
orig_buf = NULL;
|
||||
@@ -704,7 +684,7 @@ static void qcow_close(BlockDriverState *bs)
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
g_free(s->l1_table);
|
||||
qemu_vfree(s->l2_cache);
|
||||
g_free(s->l2_cache);
|
||||
g_free(s->cluster_cache);
|
||||
g_free(s->cluster_data);
|
||||
|
||||
@@ -712,30 +692,35 @@ static void qcow_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int qcow_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int header_size, backing_filename_len, l1_size, shift, i;
|
||||
QCowHeader header;
|
||||
uint8_t *tmp;
|
||||
int64_t total_size = 0;
|
||||
char *backing_file = NULL;
|
||||
const char *backing_file = NULL;
|
||||
int flags = 0;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *qcow_bs;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
|
||||
flags |= BLOCK_FLAG_ENCRYPT;
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n / 512;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
||||
backing_file = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
|
||||
flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcow_bs = NULL;
|
||||
@@ -743,7 +728,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(qcow_bs, 0);
|
||||
@@ -754,7 +739,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(QCOW_VERSION);
|
||||
header.size = cpu_to_be64(total_size);
|
||||
header.size = cpu_to_be64(total_size * 512);
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (backing_file) {
|
||||
@@ -776,7 +761,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
header_size = (header_size + 7) & ~7;
|
||||
shift = header.cluster_bits + header.l2_bits;
|
||||
l1_size = (total_size + (1LL << shift) - 1) >> shift;
|
||||
l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
|
||||
|
||||
header.l1_table_offset = cpu_to_be64(header_size);
|
||||
if (flags & BLOCK_FLAG_ENCRYPT) {
|
||||
@@ -814,8 +799,6 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ret = 0;
|
||||
exit:
|
||||
bdrv_unref(qcow_bs);
|
||||
cleanup:
|
||||
g_free(backing_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -928,28 +911,24 @@ static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList qcow_create_opts = {
|
||||
.name = "qcow-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qcow_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ENCRYPT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Encrypt the image",
|
||||
.def_value_str = "off"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
|
||||
static QEMUOptionParameter qcow_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ENCRYPT,
|
||||
.type = OPT_FLAG,
|
||||
.help = "Encrypt the image"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_qcow = {
|
||||
@@ -958,10 +937,9 @@ static BlockDriver bdrv_qcow = {
|
||||
.bdrv_probe = qcow_probe,
|
||||
.bdrv_open = qcow_open,
|
||||
.bdrv_close = qcow_close,
|
||||
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
||||
.bdrv_create = qcow_create,
|
||||
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
||||
.bdrv_create = qcow_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.supports_backing = true,
|
||||
|
||||
.bdrv_co_readv = qcow_co_readv,
|
||||
.bdrv_co_writev = qcow_co_writev,
|
||||
@@ -972,7 +950,7 @@ static BlockDriver bdrv_qcow = {
|
||||
.bdrv_write_compressed = qcow_write_compressed,
|
||||
.bdrv_get_info = qcow_get_info,
|
||||
|
||||
.create_opts = &qcow_create_opts,
|
||||
.create_options = qcow_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_qcow_init(void)
|
||||
|
||||
+3
-19
@@ -48,31 +48,15 @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
|
||||
Qcow2Cache *c;
|
||||
int i;
|
||||
|
||||
c = g_new0(Qcow2Cache, 1);
|
||||
c = g_malloc0(sizeof(*c));
|
||||
c->size = num_tables;
|
||||
c->entries = g_try_new0(Qcow2CachedTable, num_tables);
|
||||
if (!c->entries) {
|
||||
goto fail;
|
||||
}
|
||||
c->entries = g_malloc0(sizeof(*c->entries) * num_tables);
|
||||
|
||||
for (i = 0; i < c->size; i++) {
|
||||
c->entries[i].table = qemu_try_blockalign(bs->file, s->cluster_size);
|
||||
if (c->entries[i].table == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
fail:
|
||||
if (c->entries) {
|
||||
for (i = 0; i < c->size; i++) {
|
||||
qemu_vfree(c->entries[i].table);
|
||||
}
|
||||
}
|
||||
g_free(c->entries);
|
||||
g_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
|
||||
|
||||
+90
-176
@@ -42,13 +42,6 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
if (min_size <= s->l1_size)
|
||||
return 0;
|
||||
|
||||
/* Do a sanity check on min_size before trying to calculate new_l1_size
|
||||
* (this prevents overflows during the while loop for the calculation of
|
||||
* new_l1_size) */
|
||||
if (min_size > INT_MAX / sizeof(uint64_t)) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (exact_size) {
|
||||
new_l1_size = min_size;
|
||||
} else {
|
||||
@@ -72,20 +65,14 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
#endif
|
||||
|
||||
new_l1_size2 = sizeof(uint64_t) * new_l1_size;
|
||||
new_l1_table = qemu_try_blockalign(bs->file,
|
||||
align_offset(new_l1_size2, 512));
|
||||
if (new_l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(new_l1_table, 0, align_offset(new_l1_size2, 512));
|
||||
|
||||
new_l1_table = g_malloc0(align_offset(new_l1_size2, 512));
|
||||
memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
|
||||
|
||||
/* write new table (align to cluster) */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE);
|
||||
new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2);
|
||||
if (new_l1_table_offset < 0) {
|
||||
qemu_vfree(new_l1_table);
|
||||
g_free(new_l1_table);
|
||||
return new_l1_table_offset;
|
||||
}
|
||||
|
||||
@@ -119,7 +106,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
qemu_vfree(s->l1_table);
|
||||
g_free(s->l1_table);
|
||||
old_l1_table_offset = s->l1_table_offset;
|
||||
s->l1_table_offset = new_l1_table_offset;
|
||||
s->l1_table = new_l1_table;
|
||||
@@ -129,7 +116,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
QCOW2_DISCARD_OTHER);
|
||||
return 0;
|
||||
fail:
|
||||
qemu_vfree(new_l1_table);
|
||||
g_free(new_l1_table);
|
||||
qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2,
|
||||
QCOW2_DISCARD_OTHER);
|
||||
return ret;
|
||||
@@ -164,14 +151,12 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
|
||||
uint64_t buf[L1_ENTRIES_PER_SECTOR];
|
||||
int l1_start_index;
|
||||
int i, ret;
|
||||
|
||||
l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1);
|
||||
for (i = 0; i < L1_ENTRIES_PER_SECTOR && l1_start_index + i < s->l1_size;
|
||||
i++)
|
||||
{
|
||||
for (i = 0; i < L1_ENTRIES_PER_SECTOR; i++) {
|
||||
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
|
||||
}
|
||||
|
||||
@@ -380,18 +365,14 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||
iov.iov_base = qemu_try_blockalign(bs, iov.iov_len);
|
||||
if (iov.iov_base == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
iov.iov_base = qemu_blockalign(bs, iov.iov_len);
|
||||
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
|
||||
|
||||
if (!bs->drv) {
|
||||
ret = -ENOMEDIUM;
|
||||
goto out;
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
/* Call .bdrv_co_readv() directly instead of using the public block-layer
|
||||
@@ -488,13 +469,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
|
||||
" unaligned (L1 index: %#" PRIx64 ")",
|
||||
l2_offset, l1_index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* load the l2 table in memory */
|
||||
|
||||
ret = l2_load(bs, l2_offset, &l2_table);
|
||||
@@ -517,11 +491,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
||||
" in pre-v3 image (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", l2_offset, l2_index);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
return -EIO;
|
||||
}
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
@@ -537,14 +508,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
*cluster_offset &= L2E_OFFSET_MASK;
|
||||
if (offset_into_cluster(s, *cluster_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
|
||||
PRIx64 " unaligned (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", *cluster_offset,
|
||||
l2_offset, l2_index);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
@@ -561,10 +524,6 @@ out:
|
||||
*num = nb_available - index_in_cluster;
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -600,12 +559,6 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
assert(l1_index < s->l1_size);
|
||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
|
||||
" unaligned (L1 index: %#" PRIx64 ")",
|
||||
l2_offset, l1_index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* seek the l2 table of the given l2 offset */
|
||||
|
||||
@@ -741,11 +694,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
|
||||
assert(m->nb_clusters > 0);
|
||||
|
||||
old_cluster = g_try_new(uint64_t, m->nb_clusters);
|
||||
if (old_cluster == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
old_cluster = g_malloc(m->nb_clusters * sizeof(uint64_t));
|
||||
|
||||
/* copy content of unmodified sectors */
|
||||
ret = perform_cow(bs, m, &m->cow_start);
|
||||
@@ -978,15 +927,6 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
|
||||
bool offset_matches =
|
||||
(cluster_offset & L2E_OFFSET_MASK) == *host_offset;
|
||||
|
||||
if (offset_into_cluster(s, cluster_offset & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset "
|
||||
"%#llx unaligned (guest offset: %#" PRIx64
|
||||
")", cluster_offset & L2E_OFFSET_MASK,
|
||||
guest_offset);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*host_offset != 0 && !offset_matches) {
|
||||
*bytes = 0;
|
||||
ret = 0;
|
||||
@@ -1018,7 +958,7 @@ out:
|
||||
|
||||
/* Only return a host offset if we actually made progress. Otherwise we
|
||||
* would make requirements for handle_alloc() that it can't fulfill */
|
||||
if (ret > 0) {
|
||||
if (ret) {
|
||||
*host_offset = (cluster_offset & L2E_OFFSET_MASK)
|
||||
+ offset_into_cluster(s, guest_offset);
|
||||
}
|
||||
@@ -1158,17 +1098,6 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* !*host_offset would overwrite the image header and is reserved for "no
|
||||
* host offset preferred". If 0 was a valid host offset, it'd trigger the
|
||||
* following overlap check; do that now to avoid having an invalid value in
|
||||
* *host_offset. */
|
||||
if (!alloc_cluster_offset) {
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
||||
nb_clusters * s->cluster_size);
|
||||
assert(ret < 0);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save info needed for meta data update.
|
||||
*
|
||||
@@ -1263,7 +1192,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
again:
|
||||
start = offset;
|
||||
remaining = (uint64_t)*num << BDRV_SECTOR_BITS;
|
||||
remaining = *num << BDRV_SECTOR_BITS;
|
||||
cluster_offset = 0;
|
||||
*host_offset = 0;
|
||||
cur_bytes = 0;
|
||||
@@ -1414,7 +1343,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
* clusters.
|
||||
*/
|
||||
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard)
|
||||
unsigned int nb_clusters, enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
@@ -1431,54 +1360,37 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t old_l2_entry;
|
||||
uint64_t old_offset;
|
||||
|
||||
old_l2_entry = be64_to_cpu(l2_table[l2_index + i]);
|
||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
/*
|
||||
* If full_discard is false, make sure that a discarded area reads back
|
||||
* as zeroes for v3 images (we cannot do it for v2 without actually
|
||||
* writing a zero-filled buffer). We can skip the operation if the
|
||||
* cluster is already marked as zero, or if it's unallocated and we
|
||||
* don't have a backing file.
|
||||
* Make sure that a discarded area reads back as zeroes for v3 images
|
||||
* (we cannot do it for v2 without actually writing a zero-filled
|
||||
* buffer). We can skip the operation if the cluster is already marked
|
||||
* as zero, or if it's unallocated and we don't have a backing file.
|
||||
*
|
||||
* TODO We might want to use bdrv_get_block_status(bs) here, but we're
|
||||
* holding s->lock, so that doesn't work today.
|
||||
*
|
||||
* If full_discard is true, the sector should not read back as zeroes,
|
||||
* but rather fall through to the backing file.
|
||||
*/
|
||||
switch (qcow2_get_cluster_type(old_l2_entry)) {
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (full_discard || !bs->backing_hd) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
if (old_offset & QCOW_OFLAG_ZERO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (!full_discard) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
if ((old_offset & L2E_OFFSET_MASK) == 0 && !bs->backing_hd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* First remove L2 entries */
|
||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||
if (!full_discard && s->qcow_version >= 3) {
|
||||
if (s->qcow_version >= 3) {
|
||||
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||
} else {
|
||||
l2_table[l2_index + i] = cpu_to_be64(0);
|
||||
}
|
||||
|
||||
/* Then decrease the refcount */
|
||||
qcow2_free_any_clusters(bs, old_l2_entry, 1, type);
|
||||
qcow2_free_any_clusters(bs, old_offset, 1, type);
|
||||
}
|
||||
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
@@ -1490,7 +1402,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard)
|
||||
int nb_sectors, enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t end_offset;
|
||||
@@ -1513,7 +1425,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
/* Each L2 table is handled by its own loop iteration */
|
||||
while (nb_clusters > 0) {
|
||||
ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard);
|
||||
ret = discard_single_l2(bs, offset, nb_clusters, type);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1613,14 +1525,15 @@ fail:
|
||||
* Expands all zero clusters in a specific L1 table (or deallocates them, for
|
||||
* non-backed non-pre-allocated zero clusters).
|
||||
*
|
||||
* l1_entries and *visited_l1_entries are used to keep track of progress for
|
||||
* status_cb(). l1_entries contains the total number of L1 entries and
|
||||
* *visited_l1_entries counts all visited L1 entries.
|
||||
* expanded_clusters is a bitmap where every bit corresponds to one cluster in
|
||||
* the image file; a bit gets set if the corresponding cluster has been used for
|
||||
* zero expansion (i.e., has been filled with zeroes and is referenced from an
|
||||
* L2 table). nb_clusters contains the total cluster count of the image file,
|
||||
* i.e., the number of bits in expanded_clusters.
|
||||
*/
|
||||
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)
|
||||
int l1_size, uint8_t **expanded_clusters,
|
||||
uint64_t *nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bool is_active_l1 = (l1_table == s->l1_table);
|
||||
@@ -1631,23 +1544,15 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
if (!is_active_l1) {
|
||||
/* inactive L2 tables require a buffer to be stored in when loading
|
||||
* them from disk */
|
||||
l2_table = qemu_try_blockalign(bs->file, s->cluster_size);
|
||||
if (l2_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
l2_table = qemu_blockalign(bs, s->cluster_size);
|
||||
}
|
||||
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK;
|
||||
bool l2_dirty = false;
|
||||
int l2_refcount;
|
||||
|
||||
if (!l2_offset) {
|
||||
/* unallocated */
|
||||
(*visited_l1_entries)++;
|
||||
if (status_cb) {
|
||||
status_cb(bs, *visited_l1_entries, l1_entries);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1664,19 +1569,33 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
|
||||
if (l2_refcount < 0) {
|
||||
ret = l2_refcount;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||
int64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index;
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
bool preallocated = offset != 0;
|
||||
|
||||
if (cluster_type != QCOW2_CLUSTER_ZERO) {
|
||||
if (cluster_type == QCOW2_CLUSTER_NORMAL) {
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
assert((cluster_index >= 0) && (cluster_index < *nb_clusters));
|
||||
if ((*expanded_clusters)[cluster_index / 8] &
|
||||
(1 << (cluster_index % 8))) {
|
||||
/* Probably a shared L2 table; this cluster was a zero
|
||||
* cluster which has been expanded, its refcount
|
||||
* therefore most likely requires an update. */
|
||||
ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
/* Since we just increased the refcount, the COPIED flag may
|
||||
* no longer be set. */
|
||||
l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED);
|
||||
l2_dirty = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1694,19 +1613,6 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
ret = offset;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (l2_refcount > 1) {
|
||||
/* For shared L2 tables, set the refcount accordingly (it is
|
||||
* already 1 and needs to be l2_refcount) */
|
||||
ret = qcow2_update_cluster_refcount(bs,
|
||||
offset >> s->cluster_bits, l2_refcount - 1,
|
||||
QCOW2_DISCARD_OTHER);
|
||||
if (ret < 0) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_OTHER);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
||||
@@ -1728,12 +1634,29 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (l2_refcount == 1) {
|
||||
l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
|
||||
} else {
|
||||
l2_table[j] = cpu_to_be64(offset);
|
||||
}
|
||||
l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
|
||||
l2_dirty = true;
|
||||
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
|
||||
if (cluster_index >= *nb_clusters) {
|
||||
uint64_t old_bitmap_size = (*nb_clusters + 7) / 8;
|
||||
uint64_t new_bitmap_size;
|
||||
/* The offset may lie beyond the old end of the underlying image
|
||||
* file for growable files only */
|
||||
assert(bs->file->growable);
|
||||
*nb_clusters = size_to_clusters(s, bs->file->total_sectors *
|
||||
BDRV_SECTOR_SIZE);
|
||||
new_bitmap_size = (*nb_clusters + 7) / 8;
|
||||
*expanded_clusters = g_realloc(*expanded_clusters,
|
||||
new_bitmap_size);
|
||||
/* clear the newly allocated space */
|
||||
memset(&(*expanded_clusters)[old_bitmap_size], 0,
|
||||
new_bitmap_size - old_bitmap_size);
|
||||
}
|
||||
|
||||
assert((cluster_index >= 0) && (cluster_index < *nb_clusters));
|
||||
(*expanded_clusters)[cluster_index / 8] |= 1 << (cluster_index % 8);
|
||||
}
|
||||
|
||||
if (is_active_l1) {
|
||||
@@ -1762,11 +1685,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@@ -1793,25 +1711,21 @@ fail:
|
||||
* allocation for pre-allocated ones). This is important for downgrading to a
|
||||
* qcow2 version which doesn't yet support metadata zero clusters.
|
||||
*/
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL;
|
||||
int64_t l1_entries = 0, visited_l1_entries = 0;
|
||||
uint64_t nb_clusters;
|
||||
uint8_t *expanded_clusters;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
if (status_cb) {
|
||||
l1_entries = s->l1_size;
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
l1_entries += s->snapshots[i].l1_size;
|
||||
}
|
||||
}
|
||||
nb_clusters = size_to_clusters(s, bs->file->total_sectors *
|
||||
BDRV_SECTOR_SIZE);
|
||||
expanded_clusters = g_malloc0((nb_clusters + 7) / 8);
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
|
||||
&visited_l1_entries, l1_entries,
|
||||
status_cb);
|
||||
&expanded_clusters, &nb_clusters);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1845,8 +1759,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);
|
||||
&expanded_clusters, &nb_clusters);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1855,6 +1768,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
g_free(expanded_clusters);
|
||||
g_free(l1_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+313
-745
File diff suppressed because it is too large
Load Diff
+9
-22
@@ -58,7 +58,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
offset = s->snapshots_offset;
|
||||
s->snapshots = g_new0(QCowSnapshot, s->nb_snapshots);
|
||||
s->snapshots = g_malloc0(s->nb_snapshots * sizeof(QCowSnapshot));
|
||||
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
/* Read statically sized part of the snapshot header */
|
||||
@@ -381,12 +381,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
sn->l1_table_offset = l1_table_offset;
|
||||
sn->l1_size = s->l1_size;
|
||||
|
||||
l1_table = g_try_new(uint64_t, s->l1_size);
|
||||
if (s->l1_size && l1_table == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
l1_table[i] = cpu_to_be64(s->l1_table[i]);
|
||||
}
|
||||
@@ -417,7 +412,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
}
|
||||
|
||||
/* Append the new snapshot to the snapshot list */
|
||||
new_snapshot_list = g_new(QCowSnapshot, s->nb_snapshots + 1);
|
||||
new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
|
||||
if (s->snapshots) {
|
||||
memcpy(new_snapshot_list, s->snapshots,
|
||||
s->nb_snapshots * sizeof(QCowSnapshot));
|
||||
@@ -441,7 +436,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
|
||||
align_offset(sn->vm_state_size, s->cluster_size)
|
||||
>> BDRV_SECTOR_BITS,
|
||||
QCOW2_DISCARD_NEVER, false);
|
||||
QCOW2_DISCARD_NEVER);
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
@@ -504,11 +499,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
* Decrease the refcount referenced by the old one only when the L1
|
||||
* table is overwritten.
|
||||
*/
|
||||
sn_l1_table = g_try_malloc0(cur_l1_bytes);
|
||||
if (cur_l1_bytes && sn_l1_table == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
sn_l1_table = g_malloc0(cur_l1_bytes);
|
||||
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes);
|
||||
if (ret < 0) {
|
||||
@@ -661,7 +652,7 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
return s->nb_snapshots;
|
||||
}
|
||||
|
||||
sn_tab = g_new0(QEMUSnapshotInfo, s->nb_snapshots);
|
||||
sn_tab = g_malloc0(s->nb_snapshots * sizeof(QEMUSnapshotInfo));
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
sn_info = sn_tab + i;
|
||||
sn = s->snapshots + i;
|
||||
@@ -707,21 +698,17 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
return -EFBIG;
|
||||
}
|
||||
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||
new_l1_table = qemu_try_blockalign(bs->file,
|
||||
align_offset(new_l1_bytes, 512));
|
||||
if (new_l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
|
||||
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to read l1 table for snapshot");
|
||||
qemu_vfree(new_l1_table);
|
||||
g_free(new_l1_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Switch the L1 table */
|
||||
qemu_vfree(s->l1_table);
|
||||
g_free(s->l1_table);
|
||||
|
||||
s->l1_size = sn->l1_size;
|
||||
s->l1_table_offset = sn->l1_table_offset;
|
||||
|
||||
+184
-677
File diff suppressed because it is too large
Load Diff
+6
-24
@@ -59,19 +59,15 @@
|
||||
/* The cluster reads as all zeros */
|
||||
#define QCOW_OFLAG_ZERO (1ULL << 0)
|
||||
|
||||
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
||||
|
||||
#define MIN_CLUSTER_BITS 9
|
||||
#define MAX_CLUSTER_BITS 21
|
||||
|
||||
#define MIN_L2_CACHE_SIZE 1 /* cluster */
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
/* Must be at least 4 to cover all cases of refcount table growth */
|
||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
||||
|
||||
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
|
||||
|
||||
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
|
||||
* clusters */
|
||||
#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4
|
||||
#define REFCOUNT_CACHE_SIZE 4
|
||||
|
||||
#define DEFAULT_CLUSTER_SIZE 65536
|
||||
|
||||
@@ -81,7 +77,6 @@
|
||||
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
|
||||
#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other"
|
||||
#define QCOW2_OPT_OVERLAP "overlap-check"
|
||||
#define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template"
|
||||
#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header"
|
||||
#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1"
|
||||
#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2"
|
||||
@@ -90,9 +85,6 @@
|
||||
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
|
||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
|
||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
|
||||
#define QCOW2_OPT_CACHE_SIZE "cache-size"
|
||||
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
|
||||
#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size"
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
@@ -221,8 +213,6 @@ typedef struct BDRVQcowState {
|
||||
int l2_size;
|
||||
int l1_size;
|
||||
int l1_vm_state_index;
|
||||
int refcount_block_bits;
|
||||
int refcount_block_size;
|
||||
int csize_shift;
|
||||
int csize_mask;
|
||||
uint64_t cluster_offset_mask;
|
||||
@@ -262,7 +252,6 @@ typedef struct BDRVQcowState {
|
||||
bool discard_passthrough[QCOW2_DISCARD_MAX];
|
||||
|
||||
int overlap_check; /* bitmask of Qcow2MetadataOverlap values */
|
||||
bool signaled_corruption;
|
||||
|
||||
uint64_t incompatible_features;
|
||||
uint64_t compatible_features;
|
||||
@@ -479,16 +468,10 @@ int qcow2_mark_corrupt(BlockDriverState *bs);
|
||||
int qcow2_mark_consistent(BlockDriverState *bs);
|
||||
int qcow2_update_header(BlockDriverState *bs);
|
||||
|
||||
void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||
int64_t size, const char *message_format, ...)
|
||||
GCC_FMT_ATTR(5, 6);
|
||||
|
||||
/* qcow2-refcount.c functions */
|
||||
int qcow2_refcount_init(BlockDriverState *bs);
|
||||
void qcow2_refcount_close(BlockDriverState *bs);
|
||||
|
||||
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index);
|
||||
|
||||
int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||
int addend, enum qcow2_discard_type type);
|
||||
|
||||
@@ -536,11 +519,10 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard);
|
||||
int nb_sectors, enum qcow2_discard_type type);
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
|
||||
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb);
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs);
|
||||
|
||||
/* qcow2-snapshot.c functions */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
||||
|
||||
+2
-4
@@ -227,10 +227,8 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
};
|
||||
int ret;
|
||||
|
||||
check.used_clusters = g_try_new0(uint32_t, (check.nclusters + 31) / 32);
|
||||
if (check.nclusters && check.used_clusters == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
check.used_clusters = g_malloc0(((check.nclusters + 31) / 32) *
|
||||
sizeof(check.used_clusters[0]));
|
||||
|
||||
check.result->bfi.total_clusters =
|
||||
(s->header.image_size + s->header.cluster_size - 1) /
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
|
||||
#include "qed.h"
|
||||
|
||||
void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque)
|
||||
void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
GenericCB *gencb = g_malloc(len);
|
||||
gencb->cb = cb;
|
||||
@@ -24,7 +24,7 @@ void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque)
|
||||
void gencb_complete(void *opaque, int ret)
|
||||
{
|
||||
GenericCB *gencb = opaque;
|
||||
BlockCompletionFunc *cb = gencb->cb;
|
||||
BlockDriverCompletionFunc *cb = gencb->cb;
|
||||
void *user_opaque = gencb->opaque;
|
||||
|
||||
g_free(gencb);
|
||||
|
||||
+9
-9
@@ -49,7 +49,7 @@ out:
|
||||
}
|
||||
|
||||
static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb),
|
||||
cb, opaque);
|
||||
@@ -119,7 +119,7 @@ out:
|
||||
*/
|
||||
static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
unsigned int index, unsigned int n, bool flush,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QEDWriteTableCB *write_table_cb;
|
||||
unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
|
||||
@@ -173,14 +173,14 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
|
||||
qed_read_table(s, s->header.l1_table_offset,
|
||||
s->l1_table, qed_sync_cb, &ret);
|
||||
while (ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(s->bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
|
||||
qed_write_table(s, s->header.l1_table_offset,
|
||||
@@ -194,7 +194,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
|
||||
qed_write_l1_table(s, index, n, qed_sync_cb, &ret);
|
||||
while (ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(s->bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -235,7 +235,7 @@ static void qed_read_l2_table_cb(void *opaque, int ret)
|
||||
}
|
||||
|
||||
void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QEDReadL2TableCB *read_l2_table_cb;
|
||||
|
||||
@@ -267,7 +267,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
|
||||
|
||||
qed_read_l2_table(s, request, offset, qed_sync_cb, &ret);
|
||||
while (ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(s->bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -275,7 +275,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
|
||||
|
||||
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
|
||||
qed_write_table(s, request->l2_table->offset,
|
||||
@@ -289,7 +289,7 @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
|
||||
qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret);
|
||||
while (ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(s->bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
+122
-158
@@ -18,8 +18,21 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "migration/migration.h"
|
||||
|
||||
static void qed_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QEDAIOCB *acb = (QEDAIOCB *)blockacb;
|
||||
bool finished = false;
|
||||
|
||||
/* Wait for the request to finish */
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo qed_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEDAIOCB),
|
||||
.cancel = qed_aio_cancel,
|
||||
};
|
||||
|
||||
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
|
||||
@@ -130,7 +143,7 @@ static void qed_write_header_read_cb(void *opaque, int ret)
|
||||
* This function only updates known header fields in-place and does not affect
|
||||
* extra data after the QED header.
|
||||
*/
|
||||
static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb,
|
||||
static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
|
||||
void *opaque)
|
||||
{
|
||||
/* We must write full sectors for O_DIRECT but cannot necessarily generate
|
||||
@@ -360,27 +373,6 @@ static void bdrv_qed_rebind(BlockDriverState *bs)
|
||||
s->bs = bs;
|
||||
}
|
||||
|
||||
static void bdrv_qed_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
qed_cancel_need_check_timer(s);
|
||||
timer_free(s->need_check_timer);
|
||||
}
|
||||
|
||||
static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
s->need_check_timer = aio_timer_new(new_context,
|
||||
QEMU_CLOCK_VIRTUAL, SCALE_NS,
|
||||
qed_need_check_timer_cb, s);
|
||||
if (s->header.features & QED_F_NEED_CHECK) {
|
||||
qed_start_need_check_timer(s);
|
||||
}
|
||||
}
|
||||
|
||||
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -408,7 +400,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
snprintf(buf, sizeof(buf), "%" PRIx64,
|
||||
s->header.features & ~QED_FEATURE_MASK);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bdrv_get_device_name(bs), "QED", buf);
|
||||
bs->device_name, "QED", buf);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!qed_is_cluster_size_valid(s->header.cluster_size)) {
|
||||
@@ -504,7 +496,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_qed_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
||||
s->need_check_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
qed_need_check_timer_cb, s);
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
@@ -514,11 +507,13 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
static int bdrv_qed_refresh_limits(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We have nothing to do for QED reopen, stubs just return
|
||||
@@ -533,7 +528,8 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
bdrv_qed_detach_aio_context(bs);
|
||||
qed_cancel_need_check_timer(s);
|
||||
timer_free(s->need_check_timer);
|
||||
|
||||
/* Ensure writes reach stable storage */
|
||||
bdrv_flush(bs->file);
|
||||
@@ -551,7 +547,7 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
||||
static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
uint64_t image_size, uint32_t table_size,
|
||||
const char *backing_file, const char *backing_fmt,
|
||||
QemuOpts *opts, Error **errp)
|
||||
Error **errp)
|
||||
{
|
||||
QEDHeader header = {
|
||||
.magic = QED_MAGIC,
|
||||
@@ -570,7 +566,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
int ret = 0;
|
||||
BlockDriverState *bs;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
@@ -625,54 +621,53 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bdrv_qed_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
uint64_t image_size = 0;
|
||||
uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
|
||||
uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
|
||||
char *backing_file = NULL;
|
||||
char *backing_fmt = NULL;
|
||||
int ret;
|
||||
const char *backing_file = NULL;
|
||||
const char *backing_fmt = NULL;
|
||||
|
||||
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
|
||||
cluster_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
QED_DEFAULT_CLUSTER_SIZE);
|
||||
table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE,
|
||||
QED_DEFAULT_TABLE_SIZE);
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
image_size = options->value.n;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
||||
backing_file = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FMT)) {
|
||||
backing_fmt = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
if (options->value.n) {
|
||||
cluster_size = options->value.n;
|
||||
}
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_TABLE_SIZE)) {
|
||||
if (options->value.n) {
|
||||
table_size = options->value.n;
|
||||
}
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
if (!qed_is_cluster_size_valid(cluster_size)) {
|
||||
error_setg(errp, "QED cluster size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
fprintf(stderr, "QED cluster size must be within range [%u, %u] and power of 2\n",
|
||||
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_table_size_valid(table_size)) {
|
||||
error_setg(errp, "QED table size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
fprintf(stderr, "QED table size must be within range [%u, %u] and power of 2\n",
|
||||
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
|
||||
error_setg(errp, "QED image size must be a non-zero multiple of "
|
||||
"cluster size and less than %" PRIu64 " bytes",
|
||||
qed_max_image_size(cluster_size, table_size));
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
fprintf(stderr, "QED image size must be a non-zero multiple of "
|
||||
"cluster size and less than %" PRIu64 " bytes\n",
|
||||
qed_max_image_size(cluster_size, table_size));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = qed_create(filename, cluster_size, image_size, table_size,
|
||||
backing_file, backing_fmt, opts, errp);
|
||||
|
||||
finish:
|
||||
g_free(backing_file);
|
||||
g_free(backing_fmt);
|
||||
return ret;
|
||||
return qed_create(filename, cluster_size, image_size, table_size,
|
||||
backing_file, backing_fmt, errp);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -746,20 +741,18 @@ static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
|
||||
/**
|
||||
* Read from the backing file or zero-fill if no backing file
|
||||
*
|
||||
* @s: QED state
|
||||
* @pos: Byte position in device
|
||||
* @qiov: Destination I/O vector
|
||||
* @backing_qiov: Possibly shortened copy of qiov, to be allocated here
|
||||
* @cb: Completion function
|
||||
* @opaque: User data for completion function
|
||||
* @s: QED state
|
||||
* @pos: Byte position in device
|
||||
* @qiov: Destination I/O vector
|
||||
* @cb: Completion function
|
||||
* @opaque: User data for completion function
|
||||
*
|
||||
* This function reads qiov->size bytes starting at pos from the backing file.
|
||||
* If there is no backing file then zeroes are read.
|
||||
*/
|
||||
static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
QEMUIOVector *qiov,
|
||||
QEMUIOVector **backing_qiov,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
uint64_t backing_length = 0;
|
||||
size_t size;
|
||||
@@ -791,21 +784,15 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
/* If the read straddles the end of the backing file, shorten it */
|
||||
size = MIN((uint64_t)backing_length - pos, qiov->size);
|
||||
|
||||
assert(*backing_qiov == NULL);
|
||||
*backing_qiov = g_new(QEMUIOVector, 1);
|
||||
qemu_iovec_init(*backing_qiov, qiov->niov);
|
||||
qemu_iovec_concat(*backing_qiov, qiov, 0, size);
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
|
||||
*backing_qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
|
||||
qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GenericCB gencb;
|
||||
BDRVQEDState *s;
|
||||
QEMUIOVector qiov;
|
||||
QEMUIOVector *backing_qiov;
|
||||
struct iovec iov;
|
||||
uint64_t offset;
|
||||
} CopyFromBackingFileCB;
|
||||
@@ -822,12 +809,6 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
|
||||
CopyFromBackingFileCB *copy_cb = opaque;
|
||||
BDRVQEDState *s = copy_cb->s;
|
||||
|
||||
if (copy_cb->backing_qiov) {
|
||||
qemu_iovec_destroy(copy_cb->backing_qiov);
|
||||
g_free(copy_cb->backing_qiov);
|
||||
copy_cb->backing_qiov = NULL;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
qed_copy_from_backing_file_cb(copy_cb, ret);
|
||||
return;
|
||||
@@ -851,7 +832,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
|
||||
*/
|
||||
static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
uint64_t len, uint64_t offset,
|
||||
BlockCompletionFunc *cb,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
CopyFromBackingFileCB *copy_cb;
|
||||
@@ -865,12 +846,11 @@ static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
copy_cb = gencb_alloc(sizeof(*copy_cb), cb, opaque);
|
||||
copy_cb->s = s;
|
||||
copy_cb->offset = offset;
|
||||
copy_cb->backing_qiov = NULL;
|
||||
copy_cb->iov.iov_base = qemu_blockalign(s->bs, len);
|
||||
copy_cb->iov.iov_len = len;
|
||||
qemu_iovec_init_external(©_cb->qiov, ©_cb->iov, 1);
|
||||
|
||||
qed_read_backing_file(s, pos, ©_cb->qiov, ©_cb->backing_qiov,
|
||||
qed_read_backing_file(s, pos, ©_cb->qiov,
|
||||
qed_copy_from_backing_file_write, copy_cb);
|
||||
}
|
||||
|
||||
@@ -902,15 +882,21 @@ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
|
||||
static void qed_aio_complete_bh(void *opaque)
|
||||
{
|
||||
QEDAIOCB *acb = opaque;
|
||||
BlockCompletionFunc *cb = acb->common.cb;
|
||||
BlockDriverCompletionFunc *cb = acb->common.cb;
|
||||
void *user_opaque = acb->common.opaque;
|
||||
int ret = acb->bh_ret;
|
||||
bool *finished = acb->finished;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
|
||||
/* Invoke callback */
|
||||
cb(user_opaque, ret);
|
||||
|
||||
/* Signal cancel completion */
|
||||
if (finished) {
|
||||
*finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void qed_aio_complete(QEDAIOCB *acb, int ret)
|
||||
@@ -931,8 +917,7 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
|
||||
|
||||
/* Arrange for a bh to invoke the completion function */
|
||||
acb->bh_ret = ret;
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs),
|
||||
qed_aio_complete_bh, acb);
|
||||
acb->bh = qemu_bh_new(qed_aio_complete_bh, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
|
||||
/* Start next allocating write request waiting behind this one. Note that
|
||||
@@ -1064,7 +1049,7 @@ static void qed_aio_write_main(void *opaque, int ret)
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
uint64_t offset = acb->cur_cluster +
|
||||
qed_offset_into_cluster(s, acb->cur_pos);
|
||||
BlockCompletionFunc *next_fn;
|
||||
BlockDriverCompletionFunc *next_fn;
|
||||
|
||||
trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size);
|
||||
|
||||
@@ -1164,7 +1149,7 @@ static void qed_aio_write_zero_cluster(void *opaque, int ret)
|
||||
static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
|
||||
{
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
BlockCompletionFunc *cb;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
|
||||
/* Cancel timer when the first allocating request comes in */
|
||||
if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
|
||||
@@ -1221,11 +1206,7 @@ static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
|
||||
struct iovec *iov = acb->qiov->iov;
|
||||
|
||||
if (!iov->iov_base) {
|
||||
iov->iov_base = qemu_try_blockalign(acb->common.bs, iov->iov_len);
|
||||
if (iov->iov_base == NULL) {
|
||||
qed_aio_complete(acb, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
iov->iov_base = qemu_blockalign(acb->common.bs, iov->iov_len);
|
||||
memset(iov->iov_base, 0, iov->iov_len);
|
||||
}
|
||||
}
|
||||
@@ -1311,7 +1292,7 @@ static void qed_aio_read_data(void *opaque, int ret,
|
||||
return;
|
||||
} else if (ret != QED_CLUSTER_FOUND) {
|
||||
qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov,
|
||||
&acb->backing_qiov, qed_aio_next_io, acb);
|
||||
qed_aio_next_io, acb);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1337,12 +1318,6 @@ static void qed_aio_next_io(void *opaque, int ret)
|
||||
|
||||
trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size);
|
||||
|
||||
if (acb->backing_qiov) {
|
||||
qemu_iovec_destroy(acb->backing_qiov);
|
||||
g_free(acb->backing_qiov);
|
||||
acb->backing_qiov = NULL;
|
||||
}
|
||||
|
||||
/* Handle I/O error */
|
||||
if (ret) {
|
||||
qed_aio_complete(acb, ret);
|
||||
@@ -1365,11 +1340,11 @@ static void qed_aio_next_io(void *opaque, int ret)
|
||||
io_fn, acb);
|
||||
}
|
||||
|
||||
static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, int flags)
|
||||
static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, int flags)
|
||||
{
|
||||
QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, cb, opaque);
|
||||
|
||||
@@ -1377,11 +1352,11 @@ static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
opaque, flags);
|
||||
|
||||
acb->flags = flags;
|
||||
acb->finished = NULL;
|
||||
acb->qiov = qiov;
|
||||
acb->qiov_offset = 0;
|
||||
acb->cur_pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
|
||||
acb->end_pos = acb->cur_pos + nb_sectors * BDRV_SECTOR_SIZE;
|
||||
acb->backing_qiov = NULL;
|
||||
acb->request.l2_table = NULL;
|
||||
qemu_iovec_init(&acb->cur_qiov, qiov->niov);
|
||||
|
||||
@@ -1390,20 +1365,20 @@ static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
}
|
||||
|
||||
static BlockAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb,
|
||||
opaque, QED_AIOCB_WRITE);
|
||||
@@ -1431,7 +1406,7 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
|
||||
int nb_sectors,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
BlockAIOCB *blockacb;
|
||||
BlockDriverAIOCB *blockacb;
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
QEDWriteZeroesCB cb = { .done = false };
|
||||
QEMUIOVector qiov;
|
||||
@@ -1618,45 +1593,36 @@ static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
return qed_check(s, result, !!fix);
|
||||
}
|
||||
|
||||
static QemuOptsList qed_create_opts = {
|
||||
.name = "qed-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qed_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FMT,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Image format of the base image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Cluster size (in bytes)",
|
||||
.def_value_str = stringify(QED_DEFAULT_CLUSTER_SIZE)
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_TABLE_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "L1/L2 table size (in clusters)"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter qed_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size (in bytes)"
|
||||
}, {
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
}, {
|
||||
.name = BLOCK_OPT_BACKING_FMT,
|
||||
.type = OPT_STRING,
|
||||
.help = "Image format of the base image"
|
||||
}, {
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Cluster size (in bytes)",
|
||||
.value = { .n = QED_DEFAULT_CLUSTER_SIZE },
|
||||
}, {
|
||||
.name = BLOCK_OPT_TABLE_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "L1/L2 table size (in clusters)"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_qed = {
|
||||
.format_name = "qed",
|
||||
.instance_size = sizeof(BDRVQEDState),
|
||||
.create_opts = &qed_create_opts,
|
||||
.supports_backing = true,
|
||||
.create_options = qed_create_options,
|
||||
|
||||
.bdrv_probe = bdrv_qed_probe,
|
||||
.bdrv_rebind = bdrv_qed_rebind,
|
||||
@@ -1676,8 +1642,6 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
|
||||
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
|
||||
.bdrv_check = bdrv_qed_check,
|
||||
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
|
||||
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
|
||||
};
|
||||
|
||||
static void bdrv_qed_init(void)
|
||||
|
||||
+8
-8
@@ -43,7 +43,7 @@
|
||||
*
|
||||
* All fields are little-endian on disk.
|
||||
*/
|
||||
#define QED_DEFAULT_CLUSTER_SIZE 65536
|
||||
|
||||
enum {
|
||||
QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24,
|
||||
|
||||
@@ -69,6 +69,7 @@ enum {
|
||||
*/
|
||||
QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */
|
||||
QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024,
|
||||
QED_DEFAULT_CLUSTER_SIZE = 64 * 1024,
|
||||
|
||||
/* Allocated clusters are tracked using a 2-level pagetable. Table size is
|
||||
* a multiple of clusters so large maximum image sizes can be supported
|
||||
@@ -128,7 +129,7 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct QEDAIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int bh_ret; /* final return status for completion bh */
|
||||
QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */
|
||||
@@ -142,7 +143,6 @@ typedef struct QEDAIOCB {
|
||||
|
||||
/* Current cluster scatter-gather list */
|
||||
QEMUIOVector cur_qiov;
|
||||
QEMUIOVector *backing_qiov;
|
||||
uint64_t cur_pos; /* position on block device, in bytes */
|
||||
uint64_t cur_cluster; /* cluster offset in image file */
|
||||
unsigned int cur_nclusters; /* number of clusters being accessed */
|
||||
@@ -203,11 +203,11 @@ typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t l
|
||||
* Generic callback for chaining async callbacks
|
||||
*/
|
||||
typedef struct {
|
||||
BlockCompletionFunc *cb;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
} GenericCB;
|
||||
|
||||
void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque);
|
||||
void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void gencb_complete(void *opaque, int ret);
|
||||
|
||||
/**
|
||||
@@ -230,16 +230,16 @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table);
|
||||
*/
|
||||
int qed_read_l1_table_sync(BDRVQEDState *s);
|
||||
void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
|
||||
BlockCompletionFunc *cb, void *opaque);
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
unsigned int n);
|
||||
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
uint64_t offset);
|
||||
void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
|
||||
BlockCompletionFunc *cb, void *opaque);
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush,
|
||||
BlockCompletionFunc *cb, void *opaque);
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush);
|
||||
|
||||
|
||||
+95
-315
@@ -16,20 +16,12 @@
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qint.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
#define HASH_LENGTH 32
|
||||
|
||||
#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold"
|
||||
#define QUORUM_OPT_BLKVERIFY "blkverify"
|
||||
#define QUORUM_OPT_REWRITE "rewrite-corrupted"
|
||||
#define QUORUM_OPT_READ_PATTERN "read-pattern"
|
||||
|
||||
/* This union holds a vote hash value */
|
||||
typedef union QuorumVoteValue {
|
||||
@@ -77,11 +69,6 @@ typedef struct BDRVQuorumState {
|
||||
* It is useful to debug other block drivers by
|
||||
* comparing them with a reference one.
|
||||
*/
|
||||
bool rewrite_corrupted;/* true if the driver must rewrite-on-read corrupted
|
||||
* block if Quorum is reached.
|
||||
*/
|
||||
|
||||
QuorumReadPattern read_pattern;
|
||||
} BDRVQuorumState;
|
||||
|
||||
typedef struct QuorumAIOCB QuorumAIOCB;
|
||||
@@ -92,7 +79,7 @@ typedef struct QuorumAIOCB QuorumAIOCB;
|
||||
* $children_count QuorumChildRequest.
|
||||
*/
|
||||
typedef struct QuorumChildRequest {
|
||||
BlockAIOCB *aiocb;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
QEMUIOVector qiov;
|
||||
uint8_t *buf;
|
||||
int ret;
|
||||
@@ -105,7 +92,7 @@ typedef struct QuorumChildRequest {
|
||||
* used to do operations on each children and track overall progress.
|
||||
*/
|
||||
struct QuorumAIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
|
||||
/* Request metadata */
|
||||
uint64_t sector_num;
|
||||
@@ -117,20 +104,15 @@ struct QuorumAIOCB {
|
||||
int count; /* number of completed AIOCB */
|
||||
int success_count; /* number of successfully completed AIOCB */
|
||||
|
||||
int rewrite_count; /* number of replica to rewrite: count down to
|
||||
* zero once writes are fired
|
||||
*/
|
||||
|
||||
QuorumVotes votes;
|
||||
|
||||
bool is_read;
|
||||
int vote_ret;
|
||||
int child_iter; /* which child to read in fifo pattern */
|
||||
};
|
||||
|
||||
static bool quorum_vote(QuorumAIOCB *acb);
|
||||
static void quorum_vote(QuorumAIOCB *acb);
|
||||
|
||||
static void quorum_aio_cancel(BlockAIOCB *blockacb)
|
||||
static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
@@ -138,19 +120,21 @@ static void quorum_aio_cancel(BlockAIOCB *blockacb)
|
||||
|
||||
/* cancel all callbacks */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
if (acb->qcrs[i].aiocb) {
|
||||
bdrv_aio_cancel_async(acb->qcrs[i].aiocb);
|
||||
}
|
||||
bdrv_aio_cancel(acb->qcrs[i].aiocb);
|
||||
}
|
||||
|
||||
g_free(acb->qcrs);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static AIOCBInfo quorum_aiocb_info = {
|
||||
.aiocb_size = sizeof(QuorumAIOCB),
|
||||
.cancel_async = quorum_aio_cancel,
|
||||
.cancel = quorum_aio_cancel,
|
||||
};
|
||||
|
||||
static void quorum_aio_finalize(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int i, ret = 0;
|
||||
|
||||
if (acb->vote_ret) {
|
||||
@@ -160,15 +144,14 @@ static void quorum_aio_finalize(QuorumAIOCB *acb)
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
|
||||
if (acb->is_read) {
|
||||
/* on the quorum case acb->child_iter == s->num_children - 1 */
|
||||
for (i = 0; i <= acb->child_iter; i++) {
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
qemu_vfree(acb->qcrs[i].buf);
|
||||
qemu_iovec_destroy(&acb->qcrs[i].qiov);
|
||||
}
|
||||
}
|
||||
|
||||
g_free(acb->qcrs);
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
@@ -186,7 +169,7 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
QEMUIOVector *qiov,
|
||||
uint64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
|
||||
@@ -199,7 +182,6 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
acb->qcrs = g_new0(QuorumChildRequest, s->num_children);
|
||||
acb->count = 0;
|
||||
acb->success_count = 0;
|
||||
acb->rewrite_count = 0;
|
||||
acb->votes.compare = quorum_sha256_compare;
|
||||
QLIST_INIT(&acb->votes.vote_list);
|
||||
acb->is_read = false;
|
||||
@@ -216,22 +198,32 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
|
||||
static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
|
||||
{
|
||||
const char *msg = NULL;
|
||||
QObject *data;
|
||||
assert(node_name);
|
||||
data = qobject_from_jsonf("{ 'node-name': %s"
|
||||
", 'sector-num': %" PRId64
|
||||
", 'sectors-count': %d }",
|
||||
node_name, acb->sector_num, acb->nb_sectors);
|
||||
if (ret < 0) {
|
||||
msg = strerror(-ret);
|
||||
QDict *dict = qobject_to_qdict(data);
|
||||
qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
|
||||
}
|
||||
qapi_event_send_quorum_report_bad(!!msg, msg, node_name,
|
||||
acb->sector_num, acb->nb_sectors, &error_abort);
|
||||
monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data);
|
||||
qobject_decref(data);
|
||||
}
|
||||
|
||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
{
|
||||
const char *reference = bdrv_get_device_name(acb->common.bs)[0] ?
|
||||
bdrv_get_device_name(acb->common.bs) :
|
||||
QObject *data;
|
||||
const char *reference = acb->common.bs->device_name[0] ?
|
||||
acb->common.bs->device_name :
|
||||
acb->common.bs->node_name;
|
||||
|
||||
qapi_event_send_quorum_failure(reference, acb->sector_num,
|
||||
acb->nb_sectors, &error_abort);
|
||||
data = qobject_from_jsonf("{ 'reference': %s"
|
||||
", 'sector-num': %" PRId64
|
||||
", 'sectors-count': %d }",
|
||||
reference, acb->sector_num, acb->nb_sectors);
|
||||
monitor_protocol_event(QEVENT_QUORUM_FAILURE, data);
|
||||
qobject_decref(data);
|
||||
}
|
||||
|
||||
static int quorum_vote_error(QuorumAIOCB *acb);
|
||||
@@ -249,57 +241,11 @@ static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void quorum_rewrite_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
QuorumAIOCB *acb = opaque;
|
||||
|
||||
/* one less rewrite to do */
|
||||
acb->rewrite_count--;
|
||||
|
||||
/* wait until all rewrite callbacks have completed */
|
||||
if (acb->rewrite_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
quorum_aio_finalize(acb);
|
||||
}
|
||||
|
||||
static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb);
|
||||
|
||||
static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
|
||||
{
|
||||
int i;
|
||||
assert(dest->niov == source->niov);
|
||||
assert(dest->size == source->size);
|
||||
for (i = 0; i < source->niov; i++) {
|
||||
assert(dest->iov[i].iov_len == source->iov[i].iov_len);
|
||||
memcpy(dest->iov[i].iov_base,
|
||||
source->iov[i].iov_base,
|
||||
source->iov[i].iov_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
QuorumChildRequest *sacb = opaque;
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
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 < s->num_children) {
|
||||
read_fifo_child(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
quorum_copy_qiov(acb->qiov, &acb->qcrs[acb->child_iter].qiov);
|
||||
}
|
||||
acb->vote_ret = ret;
|
||||
quorum_aio_finalize(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
sacb->ret = ret;
|
||||
acb->count++;
|
||||
@@ -316,15 +262,12 @@ static void quorum_aio_cb(void *opaque, int ret)
|
||||
|
||||
/* Do the vote on read */
|
||||
if (acb->is_read) {
|
||||
rewrite = quorum_vote(acb);
|
||||
quorum_vote(acb);
|
||||
} else {
|
||||
quorum_has_too_much_io_failed(acb);
|
||||
}
|
||||
|
||||
/* if no rewrite is done the code will finish right away */
|
||||
if (!rewrite) {
|
||||
quorum_aio_finalize(acb);
|
||||
}
|
||||
quorum_aio_finalize(acb);
|
||||
}
|
||||
|
||||
static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
@@ -344,41 +287,17 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
}
|
||||
}
|
||||
|
||||
static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb,
|
||||
QuorumVoteValue *value)
|
||||
static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
|
||||
{
|
||||
QuorumVoteVersion *version;
|
||||
QuorumVoteItem *item;
|
||||
int count = 0;
|
||||
|
||||
/* first count the number of bad versions: done first to avoid concurrency
|
||||
* issues.
|
||||
*/
|
||||
QLIST_FOREACH(version, &acb->votes.vote_list, next) {
|
||||
if (acb->votes.compare(&version->value, value)) {
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
count++;
|
||||
}
|
||||
int i;
|
||||
assert(dest->niov == source->niov);
|
||||
assert(dest->size == source->size);
|
||||
for (i = 0; i < source->niov; i++) {
|
||||
assert(dest->iov[i].iov_len == source->iov[i].iov_len);
|
||||
memcpy(dest->iov[i].iov_base,
|
||||
source->iov[i].iov_base,
|
||||
source->iov[i].iov_len);
|
||||
}
|
||||
|
||||
/* quorum_rewrite_aio_cb will count down this to zero */
|
||||
acb->rewrite_count = count;
|
||||
|
||||
/* now fire the correcting rewrites */
|
||||
QLIST_FOREACH(version, &acb->votes.vote_list, next) {
|
||||
if (acb->votes.compare(&version->value, value)) {
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
bdrv_aio_writev(s->bs[item->index], acb->sector_num, acb->qiov,
|
||||
acb->nb_sectors, quorum_rewrite_aio_cb, acb);
|
||||
}
|
||||
}
|
||||
|
||||
/* return true if any rewrite is done else false */
|
||||
return count;
|
||||
}
|
||||
|
||||
static void quorum_count_vote(QuorumVotes *votes,
|
||||
@@ -558,17 +477,16 @@ static int quorum_vote_error(QuorumAIOCB *acb)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool quorum_vote(QuorumAIOCB *acb)
|
||||
static void quorum_vote(QuorumAIOCB *acb)
|
||||
{
|
||||
bool quorum = true;
|
||||
bool rewrite = false;
|
||||
int i, j, ret;
|
||||
QuorumVoteValue hash;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
QuorumVoteVersion *winner;
|
||||
|
||||
if (quorum_has_too_much_io_failed(acb)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the index of the first successful read */
|
||||
@@ -596,7 +514,7 @@ static bool quorum_vote(QuorumAIOCB *acb)
|
||||
/* Every successful read agrees */
|
||||
if (quorum) {
|
||||
quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* compute hashes for each successful read, also store indexes */
|
||||
@@ -629,79 +547,45 @@ static bool quorum_vote(QuorumAIOCB *acb)
|
||||
/* some versions are bad print them */
|
||||
quorum_report_bad_versions(s, acb, &winner->value);
|
||||
|
||||
/* corruption correction is enabled */
|
||||
if (s->rewrite_corrupted) {
|
||||
rewrite = quorum_rewrite_bad_versions(s, acb, &winner->value);
|
||||
}
|
||||
|
||||
free_exit:
|
||||
/* free lists */
|
||||
quorum_free_vote_list(&acb->votes);
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].buf = qemu_blockalign(s->bs[i], acb->qiov->size);
|
||||
qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->qcrs[i].qiov, acb->qiov, acb->qcrs[i].buf);
|
||||
}
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_aio_readv(s->bs[i], acb->sector_num, &acb->qcrs[i].qiov,
|
||||
acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]);
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
|
||||
acb->qcrs[acb->child_iter].buf = qemu_blockalign(s->bs[acb->child_iter],
|
||||
acb->qiov->size);
|
||||
qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov,
|
||||
acb->qcrs[acb->child_iter].buf);
|
||||
bdrv_aio_readv(s->bs[acb->child_iter], acb->sector_num,
|
||||
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
|
||||
nb_sectors, cb, opaque);
|
||||
int i;
|
||||
|
||||
acb->is_read = true;
|
||||
|
||||
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
||||
acb->child_iter = s->num_children - 1;
|
||||
return read_quorum_children(acb);
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].buf = qemu_blockalign(s->bs[i], qiov->size);
|
||||
qemu_iovec_init(&acb->qcrs[i].qiov, qiov->niov);
|
||||
qemu_iovec_clone(&acb->qcrs[i].qiov, qiov, acb->qcrs[i].buf);
|
||||
}
|
||||
|
||||
acb->child_iter = 0;
|
||||
return read_fifo_child(acb);
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_aio_readv(s->bs[i], sector_num, &acb->qcrs[i].qiov, nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[i]);
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *quorum_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors,
|
||||
@@ -830,44 +714,16 @@ static QemuOptsList quorum_runtime_opts = {
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Trigger block verify mode if set",
|
||||
},
|
||||
{
|
||||
.name = QUORUM_OPT_REWRITE,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Rewrite corrupted block on read quorum",
|
||||
},
|
||||
{
|
||||
.name = QUORUM_OPT_READ_PATTERN,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Allowed pattern: quorum, fifo. Quorum is default",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int parse_read_pattern(const char *opt)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!opt) {
|
||||
/* Set quorum as default */
|
||||
return QUORUM_READ_PATTERN_QUORUM;
|
||||
}
|
||||
|
||||
for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) {
|
||||
if (!strcmp(opt, QuorumReadPattern_lookup[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
QemuOpts *opts;
|
||||
bool *opened;
|
||||
QDict *sub = NULL;
|
||||
QList *list = NULL;
|
||||
@@ -897,43 +753,26 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0);
|
||||
ret = parse_read_pattern(qemu_opt_get(opts, QUORUM_OPT_READ_PATTERN));
|
||||
|
||||
/* and validate it against s->num_children */
|
||||
ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
|
||||
if (ret < 0) {
|
||||
error_setg(&local_err, "Please set read-pattern as fifo or quorum");
|
||||
goto exit;
|
||||
}
|
||||
s->read_pattern = ret;
|
||||
|
||||
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
||||
/* and validate it against s->num_children */
|
||||
ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* is the driver in blkverify mode */
|
||||
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||
s->num_children == 2 && s->threshold == 2) {
|
||||
s->is_blkverify = true;
|
||||
} else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
|
||||
fprintf(stderr, "blkverify mode is set by setting blkverify=on "
|
||||
"and using two files with vote_threshold=2\n");
|
||||
}
|
||||
|
||||
s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE,
|
||||
false);
|
||||
if (s->rewrite_corrupted && s->is_blkverify) {
|
||||
error_setg(&local_err,
|
||||
"rewrite-corrupted=on cannot be used with blkverify=on");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
/* is the driver in blkverify mode */
|
||||
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||
s->num_children == 2 && s->threshold == 2) {
|
||||
s->is_blkverify = true;
|
||||
} else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
|
||||
fprintf(stderr, "blkverify mode is set by setting blkverify=on "
|
||||
"and using two files with vote_threshold=2\n");
|
||||
}
|
||||
|
||||
/* allocate the children BlockDriverState array */
|
||||
@@ -988,9 +827,8 @@ close_exit:
|
||||
g_free(s->bs);
|
||||
g_free(opened);
|
||||
exit:
|
||||
qemu_opts_del(opts);
|
||||
/* propagate error */
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
QDECREF(list);
|
||||
@@ -1010,83 +848,25 @@ static void quorum_close(BlockDriverState *bs)
|
||||
g_free(s->bs);
|
||||
}
|
||||
|
||||
static void quorum_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_detach_aio_context(s->bs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_attach_aio_context(s->bs[i], new_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QDict *opts;
|
||||
QList *children;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_refresh_filename(s->bs[i]);
|
||||
if (!s->bs[i]->full_open_options) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
children = qlist_new();
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
QINCREF(s->bs[i]->full_open_options);
|
||||
qlist_append_obj(children, QOBJECT(s->bs[i]->full_open_options));
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("quorum")));
|
||||
qdict_put_obj(opts, QUORUM_OPT_VOTE_THRESHOLD,
|
||||
QOBJECT(qint_from_int(s->threshold)));
|
||||
qdict_put_obj(opts, QUORUM_OPT_BLKVERIFY,
|
||||
QOBJECT(qbool_from_int(s->is_blkverify)));
|
||||
qdict_put_obj(opts, QUORUM_OPT_REWRITE,
|
||||
QOBJECT(qbool_from_int(s->rewrite_corrupted)));
|
||||
qdict_put_obj(opts, "children", QOBJECT(children));
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_quorum = {
|
||||
.format_name = "quorum",
|
||||
.protocol_name = "quorum",
|
||||
.format_name = "quorum",
|
||||
.protocol_name = "quorum",
|
||||
|
||||
.instance_size = sizeof(BDRVQuorumState),
|
||||
.instance_size = sizeof(BDRVQuorumState),
|
||||
|
||||
.bdrv_file_open = quorum_open,
|
||||
.bdrv_close = quorum_close,
|
||||
.bdrv_refresh_filename = quorum_refresh_filename,
|
||||
.bdrv_file_open = quorum_open,
|
||||
.bdrv_close = quorum_close,
|
||||
|
||||
.bdrv_co_flush_to_disk = quorum_co_flush,
|
||||
.bdrv_co_flush_to_disk = quorum_co_flush,
|
||||
|
||||
.bdrv_getlength = quorum_getlength,
|
||||
.bdrv_getlength = quorum_getlength,
|
||||
|
||||
.bdrv_aio_readv = quorum_aio_readv,
|
||||
.bdrv_aio_writev = quorum_aio_writev,
|
||||
.bdrv_invalidate_cache = quorum_invalidate_cache,
|
||||
.bdrv_aio_readv = quorum_aio_readv,
|
||||
.bdrv_aio_writev = quorum_aio_writev,
|
||||
.bdrv_invalidate_cache = quorum_invalidate_cache,
|
||||
|
||||
.bdrv_detach_aio_context = quorum_detach_aio_context,
|
||||
.bdrv_attach_aio_context = quorum_attach_aio_context,
|
||||
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
|
||||
};
|
||||
|
||||
static void bdrv_quorum_init(void)
|
||||
|
||||
+4
-14
@@ -34,29 +34,19 @@
|
||||
/* linux-aio.c - Linux native implementation */
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
void *laio_init(void);
|
||||
void laio_cleanup(void *s);
|
||||
BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type);
|
||||
void laio_detach_aio_context(void *s, AioContext *old_context);
|
||||
void laio_attach_aio_context(void *s, AioContext *new_context);
|
||||
void laio_io_plug(BlockDriverState *bs, void *aio_ctx);
|
||||
int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug);
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef struct QEMUWin32AIOState QEMUWin32AIOState;
|
||||
QEMUWin32AIOState *win32_aio_init(void);
|
||||
void win32_aio_cleanup(QEMUWin32AIOState *aio);
|
||||
int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile);
|
||||
BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
QEMUWin32AIOState *aio, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type);
|
||||
void win32_aio_detach_aio_context(QEMUWin32AIOState *aio,
|
||||
AioContext *old_context);
|
||||
void win32_aio_attach_aio_context(QEMUWin32AIOState *aio,
|
||||
AioContext *new_context);
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
#endif
|
||||
|
||||
#endif /* QEMU_RAW_AIO_H */
|
||||
|
||||
+194
-452
File diff suppressed because it is too large
Load Diff
+42
-73
@@ -36,6 +36,8 @@
|
||||
#define FTYPE_CD 1
|
||||
#define FTYPE_HARDDISK 2
|
||||
|
||||
static QEMUWin32AIOState *aio;
|
||||
|
||||
typedef struct RawWin32AIOData {
|
||||
BlockDriverState *bs;
|
||||
HANDLE hfile;
|
||||
@@ -138,9 +140,9 @@ static int aio_worker(void *arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
||||
static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawWin32AIOData *acb = g_slice_new(RawWin32AIOData);
|
||||
ThreadPool *pool;
|
||||
@@ -200,25 +202,6 @@ static int set_sparse(int fd)
|
||||
NULL, 0, NULL, 0, &returned, NULL);
|
||||
}
|
||||
|
||||
static void raw_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (s->aio) {
|
||||
win32_aio_detach_aio_context(s->aio, bdrv_get_aio_context(bs));
|
||||
}
|
||||
}
|
||||
|
||||
static void raw_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (s->aio) {
|
||||
win32_aio_attach_aio_context(s->aio, new_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void raw_probe_alignment(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
@@ -317,6 +300,15 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
raw_parse_flags(flags, &access_flags, &overlapped);
|
||||
|
||||
if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) {
|
||||
aio = win32_aio_init();
|
||||
if (aio == NULL) {
|
||||
error_setg(errp, "Could not initialize AIO");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (filename[0] && filename[1] == ':') {
|
||||
snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", filename[0]);
|
||||
} else if (filename[0] == '\\' && filename[1] == '\\') {
|
||||
@@ -343,23 +335,13 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
if (flags & BDRV_O_NATIVE_AIO) {
|
||||
s->aio = win32_aio_init();
|
||||
if (s->aio == NULL) {
|
||||
CloseHandle(s->hfile);
|
||||
error_setg(errp, "Could not initialize AIO");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = win32_aio_attach(s->aio, s->hfile);
|
||||
ret = win32_aio_attach(aio, s->hfile);
|
||||
if (ret < 0) {
|
||||
win32_aio_cleanup(s->aio);
|
||||
CloseHandle(s->hfile);
|
||||
error_setg_errno(errp, -ret, "Could not enable AIO");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
win32_aio_attach_aio_context(s->aio, bdrv_get_aio_context(bs));
|
||||
s->aio = aio;
|
||||
}
|
||||
|
||||
raw_probe_alignment(bs);
|
||||
@@ -369,9 +351,9 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
@@ -383,9 +365,9 @@ static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||
static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
@@ -397,8 +379,8 @@ static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
return paio_submit(bs, s->hfile, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
|
||||
@@ -407,17 +389,7 @@ static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
|
||||
static void raw_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (s->aio) {
|
||||
win32_aio_detach_aio_context(s->aio, bdrv_get_aio_context(bs));
|
||||
win32_aio_cleanup(s->aio);
|
||||
s->aio = NULL;
|
||||
}
|
||||
|
||||
CloseHandle(s->hfile);
|
||||
if (bs->open_flags & BDRV_O_TEMPORARY) {
|
||||
unlink(bs->filename);
|
||||
}
|
||||
}
|
||||
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
@@ -503,7 +475,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int raw_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int fd;
|
||||
int64_t total_size = 0;
|
||||
@@ -511,8 +484,12 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
strstart(filename, "file:", &filename);
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n / 512;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
@@ -521,34 +498,29 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
return -EIO;
|
||||
}
|
||||
set_sparse(fd);
|
||||
ftruncate(fd, total_size);
|
||||
ftruncate(fd, total_size * 512);
|
||||
qemu_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static QemuOptsList raw_create_opts = {
|
||||
.name = "raw-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter raw_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
BlockDriver bdrv_file = {
|
||||
static BlockDriver bdrv_file = {
|
||||
.format_name = "file",
|
||||
.protocol_name = "file",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_parse_filename = raw_parse_filename,
|
||||
.bdrv_file_open = raw_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_create = raw_create,
|
||||
.bdrv_file_open = raw_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_create = raw_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
@@ -560,7 +532,7 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
|
||||
.create_opts = &raw_create_opts,
|
||||
.create_options = raw_create_options,
|
||||
};
|
||||
|
||||
/***********************************************/
|
||||
@@ -709,9 +681,6 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
|
||||
.bdrv_detach_aio_context = raw_detach_aio_context,
|
||||
.bdrv_attach_aio_context = raw_attach_aio_context,
|
||||
|
||||
.bdrv_getlength = raw_getlength,
|
||||
.has_variable_length = true,
|
||||
|
||||
|
||||
+18
-20
@@ -29,17 +29,13 @@
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
static QemuOptsList raw_create_opts = {
|
||||
.name = "raw-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter raw_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int raw_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
@@ -94,9 +90,10 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return bdrv_get_info(bs->file, bdi);
|
||||
}
|
||||
|
||||
static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
static int raw_refresh_limits(BlockDriverState *bs)
|
||||
{
|
||||
bs->bl = bs->file->bl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
@@ -129,10 +126,10 @@ static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
return bdrv_ioctl(bs->file, req, buf);
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
|
||||
}
|
||||
@@ -142,12 +139,13 @@ static int raw_has_zero_init(BlockDriverState *bs)
|
||||
return bdrv_has_zero_init(bs->file);
|
||||
}
|
||||
|
||||
static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int raw_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
@@ -173,7 +171,7 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 1;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_raw = {
|
||||
static BlockDriver bdrv_raw = {
|
||||
.format_name = "raw",
|
||||
.bdrv_probe = &raw_probe,
|
||||
.bdrv_reopen_prepare = &raw_reopen_prepare,
|
||||
@@ -196,7 +194,7 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_lock_medium = &raw_lock_medium,
|
||||
.bdrv_ioctl = &raw_ioctl,
|
||||
.bdrv_aio_ioctl = &raw_aio_ioctl,
|
||||
.create_opts = &raw_create_opts,
|
||||
.create_options = &raw_create_options[0],
|
||||
.bdrv_has_zero_init = &raw_has_zero_init
|
||||
};
|
||||
|
||||
|
||||
+120
-126
@@ -68,7 +68,7 @@ typedef enum {
|
||||
} RBDAIOCmd;
|
||||
|
||||
typedef struct RBDAIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int64_t ret;
|
||||
QEMUIOVector *qiov;
|
||||
@@ -77,6 +77,7 @@ typedef struct RBDAIOCB {
|
||||
int64_t sector_num;
|
||||
int error;
|
||||
struct BDRVRBDState *s;
|
||||
int cancelled;
|
||||
int status;
|
||||
} RBDAIOCB;
|
||||
|
||||
@@ -104,7 +105,7 @@ typedef struct BDRVRBDState {
|
||||
static int qemu_rbd_next_tok(char *dst, int dst_len,
|
||||
char *src, char delim,
|
||||
const char *name,
|
||||
char **p, Error **errp)
|
||||
char **p)
|
||||
{
|
||||
int l;
|
||||
char *end;
|
||||
@@ -127,10 +128,10 @@ static int qemu_rbd_next_tok(char *dst, int dst_len,
|
||||
}
|
||||
l = strlen(src);
|
||||
if (l >= dst_len) {
|
||||
error_setg(errp, "%s too long", name);
|
||||
error_report("%s too long", name);
|
||||
return -EINVAL;
|
||||
} else if (l == 0) {
|
||||
error_setg(errp, "%s too short", name);
|
||||
error_report("%s too short", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -156,15 +157,13 @@ static int qemu_rbd_parsename(const char *filename,
|
||||
char *pool, int pool_len,
|
||||
char *snap, int snap_len,
|
||||
char *name, int name_len,
|
||||
char *conf, int conf_len,
|
||||
Error **errp)
|
||||
char *conf, int conf_len)
|
||||
{
|
||||
const char *start;
|
||||
char *p, *buf;
|
||||
int ret;
|
||||
|
||||
if (!strstart(filename, "rbd:", &start)) {
|
||||
error_setg(errp, "File name must start with 'rbd:'");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -173,8 +172,7 @@ static int qemu_rbd_parsename(const char *filename,
|
||||
*snap = '\0';
|
||||
*conf = '\0';
|
||||
|
||||
ret = qemu_rbd_next_tok(pool, pool_len, p,
|
||||
'/', "pool name", &p, errp);
|
||||
ret = qemu_rbd_next_tok(pool, pool_len, p, '/', "pool name", &p);
|
||||
if (ret < 0 || !p) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
@@ -182,25 +180,21 @@ static int qemu_rbd_parsename(const char *filename,
|
||||
qemu_rbd_unescape(pool);
|
||||
|
||||
if (strchr(p, '@')) {
|
||||
ret = qemu_rbd_next_tok(name, name_len, p,
|
||||
'@', "object name", &p, errp);
|
||||
ret = qemu_rbd_next_tok(name, name_len, p, '@', "object name", &p);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
ret = qemu_rbd_next_tok(snap, snap_len, p,
|
||||
':', "snap name", &p, errp);
|
||||
ret = qemu_rbd_next_tok(snap, snap_len, p, ':', "snap name", &p);
|
||||
qemu_rbd_unescape(snap);
|
||||
} else {
|
||||
ret = qemu_rbd_next_tok(name, name_len, p,
|
||||
':', "object name", &p, errp);
|
||||
ret = qemu_rbd_next_tok(name, name_len, p, ':', "object name", &p);
|
||||
}
|
||||
qemu_rbd_unescape(name);
|
||||
if (ret < 0 || !p) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = qemu_rbd_next_tok(conf, conf_len, p,
|
||||
'\0', "configuration", &p, errp);
|
||||
ret = qemu_rbd_next_tok(conf, conf_len, p, '\0', "configuration", &p);
|
||||
|
||||
done:
|
||||
g_free(buf);
|
||||
@@ -235,7 +229,7 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp)
|
||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
|
||||
{
|
||||
char *p, *buf;
|
||||
char name[RBD_MAX_CONF_NAME_SIZE];
|
||||
@@ -247,20 +241,20 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp)
|
||||
|
||||
while (p) {
|
||||
ret = qemu_rbd_next_tok(name, sizeof(name), p,
|
||||
'=', "conf option name", &p, errp);
|
||||
'=', "conf option name", &p);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
qemu_rbd_unescape(name);
|
||||
|
||||
if (!p) {
|
||||
error_setg(errp, "conf option %s has no value", name);
|
||||
error_report("conf option %s has no value", name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = qemu_rbd_next_tok(value, sizeof(value), p,
|
||||
':', "conf option value", &p, errp);
|
||||
':', "conf option value", &p);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -269,7 +263,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp)
|
||||
if (strcmp(name, "conf") == 0) {
|
||||
ret = rados_conf_read_file(cluster, value);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "error reading conf file %s", value);
|
||||
error_report("error reading conf file %s", value);
|
||||
break;
|
||||
}
|
||||
} else if (strcmp(name, "id") == 0) {
|
||||
@@ -277,7 +271,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp)
|
||||
} else {
|
||||
ret = rados_conf_set(cluster, name, value);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "invalid conf option %s", name);
|
||||
error_report("invalid conf option %s", name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -288,9 +282,9 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int64_t bytes = 0;
|
||||
int64_t objsize;
|
||||
int obj_order = 0;
|
||||
@@ -307,30 +301,34 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||
snap_buf, sizeof(snap_buf),
|
||||
name, sizeof(name),
|
||||
conf, sizeof(conf), &local_err) < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
conf, sizeof(conf)) < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read out options */
|
||||
bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0);
|
||||
if (objsize) {
|
||||
if ((objsize - 1) & objsize) { /* not a power of 2? */
|
||||
error_setg(errp, "obj size needs to be power of 2");
|
||||
return -EINVAL;
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
bytes = options->value.n;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
if (options->value.n) {
|
||||
objsize = options->value.n;
|
||||
if ((objsize - 1) & objsize) { /* not a power of 2? */
|
||||
error_report("obj size needs to be power of 2");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (objsize < 4096) {
|
||||
error_report("obj size too small");
|
||||
return -EINVAL;
|
||||
}
|
||||
obj_order = ffs(objsize) - 1;
|
||||
}
|
||||
}
|
||||
if (objsize < 4096) {
|
||||
error_setg(errp, "obj size too small");
|
||||
return -EINVAL;
|
||||
}
|
||||
obj_order = ffs(objsize) - 1;
|
||||
options++;
|
||||
}
|
||||
|
||||
clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
|
||||
if (rados_create(&cluster, clientname) < 0) {
|
||||
error_setg(errp, "error initializing");
|
||||
error_report("error initializing");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -340,20 +338,20 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
if (conf[0] != '\0' &&
|
||||
qemu_rbd_set_conf(cluster, conf, &local_err) < 0) {
|
||||
qemu_rbd_set_conf(cluster, conf) < 0) {
|
||||
error_report("error setting config options");
|
||||
rados_shutdown(cluster);
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rados_connect(cluster) < 0) {
|
||||
error_setg(errp, "error connecting");
|
||||
error_report("error connecting");
|
||||
rados_shutdown(cluster);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) {
|
||||
error_setg(errp, "error opening pool %s", pool);
|
||||
error_report("error opening pool %s", pool);
|
||||
rados_shutdown(cluster);
|
||||
return -EIO;
|
||||
}
|
||||
@@ -407,7 +405,9 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
acb->status = 0;
|
||||
|
||||
qemu_aio_unref(acb);
|
||||
if (!acb->cancelled) {
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
@@ -441,7 +441,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
qemu_opts_del(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -451,7 +452,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||
snap_buf, sizeof(snap_buf),
|
||||
s->name, sizeof(s->name),
|
||||
conf, sizeof(conf), errp) < 0) {
|
||||
conf, sizeof(conf)) < 0) {
|
||||
r = -EINVAL;
|
||||
goto failed_opts;
|
||||
}
|
||||
@@ -459,7 +460,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
|
||||
r = rados_create(&s->cluster, clientname);
|
||||
if (r < 0) {
|
||||
error_setg(&local_err, "error initializing");
|
||||
error_report("error initializing");
|
||||
goto failed_opts;
|
||||
}
|
||||
|
||||
@@ -487,27 +488,28 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
if (conf[0] != '\0') {
|
||||
r = qemu_rbd_set_conf(s->cluster, conf, errp);
|
||||
r = qemu_rbd_set_conf(s->cluster, conf);
|
||||
if (r < 0) {
|
||||
error_report("error setting config options");
|
||||
goto failed_shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
r = rados_connect(s->cluster);
|
||||
if (r < 0) {
|
||||
error_setg(&local_err, "error connecting");
|
||||
error_report("error connecting");
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
|
||||
if (r < 0) {
|
||||
error_setg(&local_err, "error opening pool %s", pool);
|
||||
error_report("error opening pool %s", pool);
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
|
||||
if (r < 0) {
|
||||
error_setg(&local_err, "error reading header from %s", s->name);
|
||||
error_report("error reading header from %s", s->name);
|
||||
goto failed_open;
|
||||
}
|
||||
|
||||
@@ -536,8 +538,25 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
||||
rados_shutdown(s->cluster);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel aio. Since we don't reference acb in a non qemu threads,
|
||||
* it is safe to access it here.
|
||||
*/
|
||||
static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
RBDAIOCB *acb = (RBDAIOCB *) blockacb;
|
||||
acb->cancelled = 1;
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo rbd_aiocb_info = {
|
||||
.aiocb_size = sizeof(RBDAIOCB),
|
||||
.cancel = qemu_rbd_aio_cancel,
|
||||
};
|
||||
|
||||
static void rbd_finish_bh(void *opaque)
|
||||
@@ -562,8 +581,7 @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
|
||||
rcb->ret = rbd_aio_get_return_value(c);
|
||||
rbd_aio_release(c);
|
||||
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs),
|
||||
rbd_finish_bh, rcb);
|
||||
acb->bh = qemu_bh_new(rbd_finish_bh, rcb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
}
|
||||
|
||||
@@ -589,16 +607,16 @@ static int rbd_aio_flush_wrapper(rbd_image_t image,
|
||||
#endif
|
||||
}
|
||||
|
||||
static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque,
|
||||
RBDAIOCmd cmd)
|
||||
static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque,
|
||||
RBDAIOCmd cmd)
|
||||
{
|
||||
RBDAIOCB *acb;
|
||||
RADOSCB *rcb = NULL;
|
||||
RADOSCB *rcb;
|
||||
rbd_completion_t c;
|
||||
int64_t off, size;
|
||||
char *buf;
|
||||
@@ -612,14 +630,12 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) {
|
||||
acb->bounce = NULL;
|
||||
} else {
|
||||
acb->bounce = qemu_try_blockalign(bs, qiov->size);
|
||||
if (acb->bounce == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
acb->bounce = qemu_blockalign(bs, qiov->size);
|
||||
}
|
||||
acb->ret = 0;
|
||||
acb->error = 0;
|
||||
acb->s = s;
|
||||
acb->cancelled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
@@ -632,7 +648,7 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
off = sector_num * BDRV_SECTOR_SIZE;
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
|
||||
rcb = g_new(RADOSCB, 1);
|
||||
rcb = g_malloc(sizeof(RADOSCB));
|
||||
rcb->done = 0;
|
||||
rcb->acb = acb;
|
||||
rcb->buf = buf;
|
||||
@@ -661,46 +677,43 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
goto failed_completion;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
|
||||
failed_completion:
|
||||
rbd_aio_release(c);
|
||||
failed:
|
||||
g_free(rcb);
|
||||
qemu_vfree(acb->bounce);
|
||||
qemu_aio_unref(acb);
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
|
||||
RBD_AIO_READ);
|
||||
}
|
||||
|
||||
static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
|
||||
RBD_AIO_WRITE);
|
||||
}
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
|
||||
static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH);
|
||||
}
|
||||
@@ -842,7 +855,7 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
|
||||
int max_snaps = RBD_MAX_SNAPS;
|
||||
|
||||
do {
|
||||
snaps = g_new(rbd_snap_info_t, max_snaps);
|
||||
snaps = g_malloc(sizeof(*snaps) * max_snaps);
|
||||
snap_count = rbd_snap_list(s->image, snaps, &max_snaps);
|
||||
if (snap_count <= 0) {
|
||||
g_free(snaps);
|
||||
@@ -853,7 +866,7 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
|
||||
goto done;
|
||||
}
|
||||
|
||||
sn_tab = g_new0(QEMUSnapshotInfo, snap_count);
|
||||
sn_tab = g_malloc0(snap_count * sizeof(QEMUSnapshotInfo));
|
||||
|
||||
for (i = 0; i < snap_count; i++) {
|
||||
const char *snap_name = snaps[i].name;
|
||||
@@ -876,45 +889,29 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_DISCARD
|
||||
static BlockAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockDriverAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque,
|
||||
RBD_AIO_DISCARD);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_INVALIDATE
|
||||
static void qemu_rbd_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r = rbd_invalidate_cache(s->image);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "Failed to invalidate the cache");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static QemuOptsList qemu_rbd_create_opts = {
|
||||
.name = "rbd-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_rbd_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "RBD object size"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter qemu_rbd_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "RBD object size"
|
||||
},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_rbd = {
|
||||
@@ -926,7 +923,7 @@ static BlockDriver bdrv_rbd = {
|
||||
.bdrv_create = qemu_rbd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_get_info = qemu_rbd_getinfo,
|
||||
.create_opts = &qemu_rbd_create_opts,
|
||||
.create_options = qemu_rbd_create_options,
|
||||
.bdrv_getlength = qemu_rbd_getlength,
|
||||
.bdrv_truncate = qemu_rbd_truncate,
|
||||
.protocol_name = "rbd",
|
||||
@@ -948,9 +945,6 @@ static BlockDriver bdrv_rbd = {
|
||||
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
|
||||
.bdrv_snapshot_list = qemu_rbd_snap_list,
|
||||
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
|
||||
#ifdef LIBRBD_SUPPORTS_INVALIDATE
|
||||
.bdrv_invalidate_cache = qemu_rbd_invalidate_cache,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void bdrv_rbd_init(void)
|
||||
|
||||
+198
-291
File diff suppressed because it is too large
Load Diff
@@ -236,10 +236,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
error_setg(errp, "snapshot_id and name are both NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* drain all pending i/o before deleting snapshot */
|
||||
bdrv_drain_all();
|
||||
|
||||
if (drv->bdrv_snapshot_delete) {
|
||||
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
||||
}
|
||||
|
||||
+93
-135
@@ -106,59 +106,30 @@ static void ssh_state_free(BDRVSSHState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(3, 4)
|
||||
session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
|
||||
/* Wrappers around error_report which make sure to dump as much
|
||||
* information from libssh2 as possible.
|
||||
*/
|
||||
static void GCC_FMT_ATTR(2, 3)
|
||||
session_error_report(BDRVSSHState *s, const char *fs, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *msg;
|
||||
|
||||
va_start(args, fs);
|
||||
msg = g_strdup_vprintf(fs, args);
|
||||
va_end(args);
|
||||
error_vprintf(fs, args);
|
||||
|
||||
if (s->session) {
|
||||
if ((s)->session) {
|
||||
char *ssh_err;
|
||||
int ssh_err_code;
|
||||
|
||||
libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
error_setg(errp, "%s: %s (libssh2 error code: %d)",
|
||||
msg, ssh_err, ssh_err_code);
|
||||
} else {
|
||||
error_setg(errp, "%s", msg);
|
||||
ssh_err_code = libssh2_session_last_errno((s)->session);
|
||||
|
||||
error_printf(": %s (libssh2 error code: %d)", ssh_err, ssh_err_code);
|
||||
}
|
||||
g_free(msg);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(3, 4)
|
||||
sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *msg;
|
||||
|
||||
va_start(args, fs);
|
||||
msg = g_strdup_vprintf(fs, args);
|
||||
va_end(args);
|
||||
|
||||
if (s->sftp) {
|
||||
char *ssh_err;
|
||||
int ssh_err_code;
|
||||
unsigned long sftp_err_code;
|
||||
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
/* See <libssh2_sftp.h>. */
|
||||
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
|
||||
|
||||
error_setg(errp,
|
||||
"%s: %s (libssh2 error code: %d, sftp error code: %lu)",
|
||||
msg, ssh_err, ssh_err_code, sftp_err_code);
|
||||
} else {
|
||||
error_setg(errp, "%s", msg);
|
||||
}
|
||||
g_free(msg);
|
||||
error_printf("\n");
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3)
|
||||
@@ -174,9 +145,9 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...)
|
||||
int ssh_err_code;
|
||||
unsigned long sftp_err_code;
|
||||
|
||||
libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
|
||||
/* This is not an errno. See <libssh2.h>. */
|
||||
ssh_err_code = libssh2_session_last_error(s->session,
|
||||
&ssh_err, NULL, 0);
|
||||
ssh_err_code = libssh2_session_last_errno((s)->session);
|
||||
/* See <libssh2_sftp.h>. */
|
||||
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
|
||||
|
||||
@@ -272,7 +243,7 @@ static void ssh_parse_filename(const char *filename, QDict *options,
|
||||
}
|
||||
|
||||
static int check_host_key_knownhosts(BDRVSSHState *s,
|
||||
const char *host, int port, Error **errp)
|
||||
const char *host, int port)
|
||||
{
|
||||
const char *home;
|
||||
char *knh_file = NULL;
|
||||
@@ -286,15 +257,14 @@ static int check_host_key_knownhosts(BDRVSSHState *s,
|
||||
hostkey = libssh2_session_hostkey(s->session, &len, &type);
|
||||
if (!hostkey) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s, "failed to read remote host key");
|
||||
session_error_report(s, "failed to read remote host key");
|
||||
goto out;
|
||||
}
|
||||
|
||||
knh = libssh2_knownhost_init(s->session);
|
||||
if (!knh) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s,
|
||||
"failed to initialize known hosts support");
|
||||
session_error_report(s, "failed to initialize known hosts support");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -319,23 +289,21 @@ static int check_host_key_knownhosts(BDRVSSHState *s,
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s,
|
||||
"host key does not match the one in known_hosts"
|
||||
" (found key %s)", found->key);
|
||||
session_error_report(s, "host key does not match the one in known_hosts (found key %s)",
|
||||
found->key);
|
||||
goto out;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s, "no host key was found in known_hosts");
|
||||
session_error_report(s, "no host key was found in known_hosts");
|
||||
goto out;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s,
|
||||
"failure matching the host key with known_hosts");
|
||||
session_error_report(s, "failure matching the host key with known_hosts");
|
||||
goto out;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s, "unknown error matching the host key"
|
||||
" with known_hosts (%d)", r);
|
||||
session_error_report(s, "unknown error matching the host key with known_hosts (%d)",
|
||||
r);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -390,20 +358,20 @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
|
||||
|
||||
static int
|
||||
check_host_key_hash(BDRVSSHState *s, const char *hash,
|
||||
int hash_type, size_t fingerprint_len, Error **errp)
|
||||
int hash_type, size_t fingerprint_len)
|
||||
{
|
||||
const char *fingerprint;
|
||||
|
||||
fingerprint = libssh2_hostkey_hash(s->session, hash_type);
|
||||
if (!fingerprint) {
|
||||
session_error_setg(errp, s, "failed to read remote host key");
|
||||
session_error_report(s, "failed to read remote host key");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
|
||||
hash) != 0) {
|
||||
error_setg(errp, "remote host key does not match host_key_check '%s'",
|
||||
hash);
|
||||
error_report("remote host key does not match host_key_check '%s'",
|
||||
hash);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
@@ -411,7 +379,7 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
|
||||
}
|
||||
|
||||
static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
||||
const char *host_key_check, Error **errp)
|
||||
const char *host_key_check)
|
||||
{
|
||||
/* host_key_check=no */
|
||||
if (strcmp(host_key_check, "no") == 0) {
|
||||
@@ -421,25 +389,25 @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
||||
/* host_key_check=md5:xx:yy:zz:... */
|
||||
if (strncmp(host_key_check, "md5:", 4) == 0) {
|
||||
return check_host_key_hash(s, &host_key_check[4],
|
||||
LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
|
||||
LIBSSH2_HOSTKEY_HASH_MD5, 16);
|
||||
}
|
||||
|
||||
/* host_key_check=sha1:xx:yy:zz:... */
|
||||
if (strncmp(host_key_check, "sha1:", 5) == 0) {
|
||||
return check_host_key_hash(s, &host_key_check[5],
|
||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
|
||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20);
|
||||
}
|
||||
|
||||
/* host_key_check=yes */
|
||||
if (strcmp(host_key_check, "yes") == 0) {
|
||||
return check_host_key_knownhosts(s, host, port, errp);
|
||||
return check_host_key_knownhosts(s, host, port);
|
||||
}
|
||||
|
||||
error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
|
||||
error_report("unknown host_key_check setting (%s)", host_key_check);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
static int authenticate(BDRVSSHState *s, const char *user)
|
||||
{
|
||||
int r, ret;
|
||||
const char *userauthlist;
|
||||
@@ -450,8 +418,7 @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
|
||||
if (strstr(userauthlist, "publickey") == NULL) {
|
||||
ret = -EPERM;
|
||||
error_setg(errp,
|
||||
"remote server does not support \"publickey\" authentication");
|
||||
error_report("remote server does not support \"publickey\" authentication");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -459,18 +426,17 @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
agent = libssh2_agent_init(s->session);
|
||||
if (!agent) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s, "failed to initialize ssh-agent support");
|
||||
session_error_report(s, "failed to initialize ssh-agent support");
|
||||
goto out;
|
||||
}
|
||||
if (libssh2_agent_connect(agent)) {
|
||||
ret = -ECONNREFUSED;
|
||||
session_error_setg(errp, s, "failed to connect to ssh-agent");
|
||||
session_error_report(s, "failed to connect to ssh-agent");
|
||||
goto out;
|
||||
}
|
||||
if (libssh2_agent_list_identities(agent)) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s,
|
||||
"failed requesting identities from ssh-agent");
|
||||
session_error_report(s, "failed requesting identities from ssh-agent");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -481,8 +447,7 @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
}
|
||||
if (r < 0) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s,
|
||||
"failed to obtain identity from ssh-agent");
|
||||
session_error_report(s, "failed to obtain identity from ssh-agent");
|
||||
goto out;
|
||||
}
|
||||
r = libssh2_agent_userauth(agent, user, identity);
|
||||
@@ -496,8 +461,8 @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
}
|
||||
|
||||
ret = -EPERM;
|
||||
error_setg(errp, "failed to authenticate using publickey authentication "
|
||||
"and the identities held by your ssh-agent");
|
||||
error_report("failed to authenticate using publickey authentication "
|
||||
"and the identities held by your ssh-agent");
|
||||
|
||||
out:
|
||||
if (agent != NULL) {
|
||||
@@ -511,17 +476,13 @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
|
||||
}
|
||||
|
||||
static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
int ssh_flags, int creat_mode, Error **errp)
|
||||
int ssh_flags, int creat_mode)
|
||||
{
|
||||
int r, ret;
|
||||
Error *err = NULL;
|
||||
const char *host, *user, *path, *host_key_check;
|
||||
int port;
|
||||
|
||||
if (!qdict_haskey(options, "host")) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "No hostname was specified");
|
||||
goto err;
|
||||
}
|
||||
host = qdict_get_str(options, "host");
|
||||
|
||||
if (qdict_haskey(options, "port")) {
|
||||
@@ -530,11 +491,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
port = 22;
|
||||
}
|
||||
|
||||
if (!qdict_haskey(options, "path")) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "No path was specified");
|
||||
goto err;
|
||||
}
|
||||
path = qdict_get_str(options, "path");
|
||||
|
||||
if (qdict_haskey(options, "user")) {
|
||||
@@ -542,7 +498,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
} else {
|
||||
user = g_get_user_name();
|
||||
if (!user) {
|
||||
error_setg_errno(errp, errno, "Can't get user name");
|
||||
ret = -errno;
|
||||
goto err;
|
||||
}
|
||||
@@ -559,9 +514,11 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
s->hostport = g_strdup_printf("%s:%d", host, port);
|
||||
|
||||
/* Open the socket and connect. */
|
||||
s->sock = inet_connect(s->hostport, errp);
|
||||
if (s->sock < 0) {
|
||||
s->sock = inet_connect(s->hostport, &err);
|
||||
if (err != NULL) {
|
||||
ret = -errno;
|
||||
qerror_report_err(err);
|
||||
error_free(err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -569,7 +526,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
s->session = libssh2_session_init();
|
||||
if (!s->session) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s, "failed to initialize libssh2 session");
|
||||
session_error_report(s, "failed to initialize libssh2 session");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -580,18 +537,18 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
r = libssh2_session_handshake(s->session, s->sock);
|
||||
if (r != 0) {
|
||||
ret = -EINVAL;
|
||||
session_error_setg(errp, s, "failed to establish SSH session");
|
||||
session_error_report(s, "failed to establish SSH session");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check the remote host's key against known_hosts. */
|
||||
ret = check_host_key(s, host, port, host_key_check, errp);
|
||||
ret = check_host_key(s, host, port, host_key_check);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Authenticate. */
|
||||
ret = authenticate(s, user, errp);
|
||||
ret = authenticate(s, user);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -599,7 +556,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
/* Start SFTP. */
|
||||
s->sftp = libssh2_sftp_init(s->session);
|
||||
if (!s->sftp) {
|
||||
session_error_setg(errp, s, "failed to initialize sftp handle");
|
||||
session_error_report(s, "failed to initialize sftp handle");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
@@ -609,14 +566,14 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
path, ssh_flags, creat_mode);
|
||||
s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
|
||||
if (!s->sftp_handle) {
|
||||
session_error_setg(errp, s, "failed to open remote file '%s'", path);
|
||||
session_error_report(s, "failed to open remote file '%s'", path);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
|
||||
if (r < 0) {
|
||||
sftp_error_setg(errp, s, "failed to read file attributes");
|
||||
sftp_error_report(s, "failed to read file attributes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -666,7 +623,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
||||
}
|
||||
|
||||
/* Start up SSH. */
|
||||
ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
|
||||
ret = connect_to_ssh(s, options, ssh_flags, 0);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -685,22 +642,20 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QemuOptsList ssh_create_opts = {
|
||||
.name = "ssh-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(ssh_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter ssh_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int ssh_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
Error *local_err = NULL;
|
||||
int64_t total_size = 0;
|
||||
QDict *uri_options = NULL;
|
||||
BDRVSSHState s;
|
||||
@@ -710,21 +665,26 @@ static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ssh_state_init(&s);
|
||||
|
||||
/* Get desired file size. */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
DPRINTF("total_size=%" PRIi64, total_size);
|
||||
|
||||
uri_options = qdict_new();
|
||||
r = parse_uri(filename, uri_options, errp);
|
||||
r = parse_uri(filename, uri_options, &local_err);
|
||||
if (r < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
ret = r;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = connect_to_ssh(&s, uri_options,
|
||||
LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
|
||||
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
|
||||
0644, errp);
|
||||
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 0644);
|
||||
if (r < 0) {
|
||||
ret = r;
|
||||
goto out;
|
||||
@@ -734,7 +694,7 @@ static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
libssh2_sftp_seek64(s.sftp_handle, total_size-1);
|
||||
r2 = libssh2_sftp_write(s.sftp_handle, c, 1);
|
||||
if (r2 < 0) {
|
||||
sftp_error_setg(errp, &s, "truncate failed");
|
||||
sftp_error_report(&s, "truncate failed");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@@ -782,7 +742,7 @@ static void restart_coroutine(void *opaque)
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
}
|
||||
|
||||
static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
|
||||
static coroutine_fn void set_fd_handler(BDRVSSHState *s)
|
||||
{
|
||||
int r;
|
||||
IOHandler *rd_handler = NULL, *wr_handler = NULL;
|
||||
@@ -800,26 +760,24 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
|
||||
DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock,
|
||||
rd_handler, wr_handler);
|
||||
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
|
||||
rd_handler, wr_handler, co);
|
||||
qemu_aio_set_fd_handler(s->sock, rd_handler, wr_handler, co);
|
||||
}
|
||||
|
||||
static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
|
||||
BlockDriverState *bs)
|
||||
static coroutine_fn void clear_fd_handler(BDRVSSHState *s)
|
||||
{
|
||||
DPRINTF("s->sock=%d", s->sock);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* A non-blocking call returned EAGAIN, so yield, ensuring the
|
||||
* handlers are set up so that we'll be rescheduled when there is an
|
||||
* interesting event on the socket.
|
||||
*/
|
||||
static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
|
||||
static coroutine_fn void co_yield(BDRVSSHState *s)
|
||||
{
|
||||
set_fd_handler(s, bs);
|
||||
set_fd_handler(s);
|
||||
qemu_coroutine_yield();
|
||||
clear_fd_handler(s, bs);
|
||||
clear_fd_handler(s);
|
||||
}
|
||||
|
||||
/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
|
||||
@@ -849,7 +807,7 @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
|
||||
static coroutine_fn int ssh_read(BDRVSSHState *s,
|
||||
int64_t offset, size_t size,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
@@ -882,7 +840,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
|
||||
DPRINTF("sftp_read returned %zd", r);
|
||||
|
||||
if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
|
||||
co_yield(s, bs);
|
||||
co_yield(s);
|
||||
goto again;
|
||||
}
|
||||
if (r < 0) {
|
||||
@@ -917,14 +875,14 @@ static coroutine_fn int ssh_co_readv(BlockDriverState *bs,
|
||||
int ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = ssh_read(s, bs, sector_num * BDRV_SECTOR_SIZE,
|
||||
ret = ssh_read(s, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE, qiov);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
||||
static int ssh_write(BDRVSSHState *s,
|
||||
int64_t offset, size_t size,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
@@ -952,7 +910,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
||||
DPRINTF("sftp_write returned %zd", r);
|
||||
|
||||
if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
|
||||
co_yield(s, bs);
|
||||
co_yield(s);
|
||||
goto again;
|
||||
}
|
||||
if (r < 0) {
|
||||
@@ -971,7 +929,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
||||
*/
|
||||
if (r == 0) {
|
||||
ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
|
||||
co_yield(s, bs);
|
||||
co_yield(s);
|
||||
goto again;
|
||||
}
|
||||
|
||||
@@ -999,7 +957,7 @@ static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
|
||||
int ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
|
||||
ret = ssh_write(s, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE, qiov);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
@@ -1020,7 +978,7 @@ static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
|
||||
|
||||
#ifdef HAS_LIBSSH2_SFTP_FSYNC
|
||||
|
||||
static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
|
||||
static coroutine_fn int ssh_flush(BDRVSSHState *s)
|
||||
{
|
||||
int r;
|
||||
|
||||
@@ -1028,7 +986,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
|
||||
again:
|
||||
r = libssh2_sftp_fsync(s->sftp_handle);
|
||||
if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
|
||||
co_yield(s, bs);
|
||||
co_yield(s);
|
||||
goto again;
|
||||
}
|
||||
if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
|
||||
@@ -1050,7 +1008,7 @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
|
||||
int ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = ssh_flush(s, bs);
|
||||
ret = ssh_flush(s);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
@@ -1093,7 +1051,7 @@ static BlockDriver bdrv_ssh = {
|
||||
.bdrv_co_writev = ssh_co_writev,
|
||||
.bdrv_getlength = ssh_getlength,
|
||||
.bdrv_co_flush_to_disk = ssh_co_flush,
|
||||
.create_opts = &ssh_create_opts,
|
||||
.create_options = ssh_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_ssh_init(void)
|
||||
|
||||
+24
-45
@@ -32,7 +32,7 @@ typedef struct StreamBlockJob {
|
||||
RateLimit limit;
|
||||
BlockDriverState *base;
|
||||
BlockdevOnError on_error;
|
||||
char *backing_file_str;
|
||||
char backing_file_id[1024];
|
||||
} StreamBlockJob;
|
||||
|
||||
static int coroutine_fn stream_populate(BlockDriverState *bs,
|
||||
@@ -60,7 +60,7 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
||||
/* Must assign before bdrv_delete() to prevent traversing dangling pointer
|
||||
* while we delete backing image instances.
|
||||
*/
|
||||
bdrv_set_backing_hd(top, base);
|
||||
top->backing_hd = base;
|
||||
|
||||
while (intermediate) {
|
||||
BlockDriverState *unused;
|
||||
@@ -72,46 +72,16 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
||||
|
||||
unused = intermediate;
|
||||
intermediate = intermediate->backing_hd;
|
||||
bdrv_set_backing_hd(unused, NULL);
|
||||
unused->backing_hd = NULL;
|
||||
bdrv_unref(unused);
|
||||
}
|
||||
|
||||
bdrv_refresh_limits(top, NULL);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
bool reached_end;
|
||||
} StreamCompleteData;
|
||||
|
||||
static void stream_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
|
||||
StreamCompleteData *data = opaque;
|
||||
BlockDriverState *base = s->base;
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && data->reached_end &&
|
||||
data->ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
base_id = s->backing_file_str;
|
||||
if (base->drv) {
|
||||
base_fmt = base->drv->format_name;
|
||||
}
|
||||
}
|
||||
data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt);
|
||||
close_unused_images(job->bs, base, base_id);
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
block_job_completed(&s->common, data->ret);
|
||||
g_free(data);
|
||||
bdrv_refresh_limits(top);
|
||||
}
|
||||
|
||||
static void coroutine_fn stream_run(void *opaque)
|
||||
{
|
||||
StreamBlockJob *s = opaque;
|
||||
StreamCompleteData *data;
|
||||
BlockDriverState *bs = s->common.bs;
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t sector_num, end;
|
||||
@@ -189,14 +159,14 @@ wait:
|
||||
BlockErrorAction action =
|
||||
block_job_error_action(&s->common, s->common.bs, s->on_error,
|
||||
true, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||
if (action == BDRV_ACTION_STOP) {
|
||||
n = 0;
|
||||
continue;
|
||||
}
|
||||
if (error == 0) {
|
||||
error = ret;
|
||||
}
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
if (action == BDRV_ACTION_REPORT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -213,13 +183,20 @@ wait:
|
||||
/* Do not remove the backing file if an error was there but ignored. */
|
||||
ret = error;
|
||||
|
||||
qemu_vfree(buf);
|
||||
if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
base_id = s->backing_file_id;
|
||||
if (base->drv) {
|
||||
base_fmt = base->drv->format_name;
|
||||
}
|
||||
}
|
||||
ret = bdrv_change_backing_file(bs, base_id, base_fmt);
|
||||
close_unused_images(bs, base, base_id);
|
||||
}
|
||||
|
||||
/* Modify backing chain and close BDSes in main loop */
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
data->reached_end = sector_num == end;
|
||||
block_job_defer_to_main_loop(&s->common, stream_complete, data);
|
||||
qemu_vfree(buf);
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
|
||||
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -240,9 +217,9 @@ static const BlockJobDriver stream_job_driver = {
|
||||
};
|
||||
|
||||
void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
const char *backing_file_str, int64_t speed,
|
||||
const char *base_id, int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s;
|
||||
@@ -260,7 +237,9 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
}
|
||||
|
||||
s->base = base;
|
||||
s->backing_file_str = g_strdup(backing_file_str);
|
||||
if (base_id) {
|
||||
pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
|
||||
}
|
||||
|
||||
s->on_error = on_error;
|
||||
s->common.co = qemu_coroutine_create(stream_run);
|
||||
|
||||
+85
-126
@@ -120,18 +120,8 @@ typedef unsigned char uuid_t[16];
|
||||
|
||||
#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
|
||||
|
||||
/* The bmap will take up VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) bytes; since
|
||||
* the bmap is read and written in a single operation, its size needs to be
|
||||
* limited to INT_MAX; furthermore, when opening an image, the bmap size is
|
||||
* rounded up to be aligned on BDRV_SECTOR_SIZE.
|
||||
* Therefore this should satisfy the following:
|
||||
* VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) + BDRV_SECTOR_SIZE == INT_MAX + 1
|
||||
* (INT_MAX + 1 is the first value not representable as an int)
|
||||
* This guarantees that any value below or equal to the constant will, when
|
||||
* multiplied by sizeof(uint32_t) and rounded up to a BDRV_SECTOR_SIZE boundary,
|
||||
* still be below or equal to INT_MAX. */
|
||||
#define VDI_BLOCKS_IN_IMAGE_MAX \
|
||||
((unsigned)((INT_MAX + 1u - BDRV_SECTOR_SIZE) / sizeof(uint32_t)))
|
||||
/* max blocks in image is (0xffffffff / 4) */
|
||||
#define VDI_BLOCKS_IN_IMAGE_MAX 0x3fffffff
|
||||
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
|
||||
(uint64_t)DEFAULT_CLUSTER_SIZE)
|
||||
|
||||
@@ -147,14 +137,12 @@ static inline int uuid_is_null(const uuid_t uu)
|
||||
return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
|
||||
}
|
||||
|
||||
# if defined(CONFIG_VDI_DEBUG)
|
||||
static inline void uuid_unparse(const uuid_t uu, char *out)
|
||||
{
|
||||
snprintf(out, 37, UUID_FMT,
|
||||
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
|
||||
uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -251,6 +239,7 @@ static void vdi_header_to_le(VdiHeader *header)
|
||||
cpu_to_le32s(&header->block_extra);
|
||||
cpu_to_le32s(&header->blocks_in_image);
|
||||
cpu_to_le32s(&header->blocks_allocated);
|
||||
cpu_to_le32s(&header->blocks_allocated);
|
||||
uuid_convert(header->uuid_image);
|
||||
uuid_convert(header->uuid_last_snap);
|
||||
uuid_convert(header->uuid_link);
|
||||
@@ -304,12 +293,7 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bmap = g_try_new(uint32_t, s->header.blocks_in_image);
|
||||
if (s->header.blocks_in_image && bmap == NULL) {
|
||||
res->check_errors++;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bmap = g_malloc(s->header.blocks_in_image * sizeof(uint32_t));
|
||||
memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t));
|
||||
|
||||
/* Check block map and value of blocks_allocated. */
|
||||
@@ -367,23 +351,23 @@ static int vdi_make_empty(BlockDriverState *bs)
|
||||
static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const VdiHeader *header = (const VdiHeader *)buf;
|
||||
int ret = 0;
|
||||
int result = 0;
|
||||
|
||||
logout("\n");
|
||||
|
||||
if (buf_size < sizeof(*header)) {
|
||||
/* Header too small, no VDI. */
|
||||
} else if (le32_to_cpu(header->signature) == VDI_SIGNATURE) {
|
||||
ret = 100;
|
||||
result = 100;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (result == 0) {
|
||||
logout("no vdi image\n");
|
||||
} else {
|
||||
logout("%s", header->text);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
@@ -419,39 +403,39 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
We accept them but round the disk size to the next multiple of
|
||||
SECTOR_SIZE. */
|
||||
logout("odd disk size %" PRIu64 " B, round up\n", header.disk_size);
|
||||
header.disk_size = ROUND_UP(header.disk_size, SECTOR_SIZE);
|
||||
header.disk_size += SECTOR_SIZE - 1;
|
||||
header.disk_size &= ~(SECTOR_SIZE - 1);
|
||||
}
|
||||
|
||||
if (header.signature != VDI_SIGNATURE) {
|
||||
error_setg(errp, "Image not in VDI format (bad signature %08" PRIx32
|
||||
")", header.signature);
|
||||
error_setg(errp, "Image not in VDI format (bad signature %08x)", header.signature);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
} else if (header.version != VDI_VERSION_1_1) {
|
||||
error_setg(errp, "unsupported VDI image (version %" PRIu32 ".%" PRIu32
|
||||
")", header.version >> 16, header.version & 0xffff);
|
||||
error_setg(errp, "unsupported VDI image (version %u.%u)",
|
||||
header.version >> 16, header.version & 0xffff);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.offset_bmap % SECTOR_SIZE != 0) {
|
||||
/* We only support block maps which start on a sector boundary. */
|
||||
error_setg(errp, "unsupported VDI image (unaligned block map offset "
|
||||
"0x%" PRIx32 ")", header.offset_bmap);
|
||||
"0x%x)", header.offset_bmap);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.offset_data % SECTOR_SIZE != 0) {
|
||||
/* We only support data blocks which start on a sector boundary. */
|
||||
error_setg(errp, "unsupported VDI image (unaligned data offset 0x%"
|
||||
PRIx32 ")", header.offset_data);
|
||||
error_setg(errp, "unsupported VDI image (unaligned data offset 0x%x)",
|
||||
header.offset_data);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.sector_size != SECTOR_SIZE) {
|
||||
error_setg(errp, "unsupported VDI image (sector size %" PRIu32
|
||||
" is not %u)", header.sector_size, SECTOR_SIZE);
|
||||
error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
|
||||
header.sector_size, SECTOR_SIZE);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
|
||||
error_setg(errp, "unsupported VDI image (block size %" PRIu32
|
||||
" is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||
error_setg(errp, "unsupported VDI image (block size %u is not %u)",
|
||||
header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.disk_size >
|
||||
@@ -486,13 +470,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->header = header;
|
||||
|
||||
bmap_size = header.blocks_in_image * sizeof(uint32_t);
|
||||
bmap_size = DIV_ROUND_UP(bmap_size, SECTOR_SIZE);
|
||||
s->bmap = qemu_try_blockalign(bs->file, bmap_size * SECTOR_SIZE);
|
||||
if (s->bmap == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
||||
s->bmap = g_malloc(bmap_size * SECTOR_SIZE);
|
||||
ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size);
|
||||
if (ret < 0) {
|
||||
goto fail_free_bmap;
|
||||
@@ -501,13 +480,13 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Disable migration when vdi images are used */
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"vdi", bdrv_get_device_name(bs), "live migration");
|
||||
"vdi", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free_bmap:
|
||||
qemu_vfree(s->bmap);
|
||||
g_free(s->bmap);
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
@@ -693,9 +672,11 @@ static int vdi_co_write(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int vdi_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
int fd;
|
||||
int result = 0;
|
||||
uint64_t bytes = 0;
|
||||
uint32_t blocks;
|
||||
size_t block_size = DEFAULT_CLUSTER_SIZE;
|
||||
@@ -703,54 +684,52 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
VdiHeader header;
|
||||
size_t i;
|
||||
size_t bmap_size;
|
||||
int64_t offset = 0;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
uint32_t *bmap = NULL;
|
||||
|
||||
logout("\n");
|
||||
|
||||
/* Read out options. */
|
||||
bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
bytes = options->value.n;
|
||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||
/* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
|
||||
block_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
if (options->value.n) {
|
||||
/* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
|
||||
block_size = options->value.n;
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_VDI_STATIC_IMAGE)
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
|
||||
image_type = VDI_TYPE_STATIC;
|
||||
}
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_STATIC)) {
|
||||
if (options->value.n) {
|
||||
image_type = VDI_TYPE_STATIC;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
if (bytes > VDI_DISK_SIZE_MAX) {
|
||||
ret = -ENOTSUP;
|
||||
result = -ENOTSUP;
|
||||
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||
", max supported is 0x%" PRIx64 ")",
|
||||
bytes, VDI_DISK_SIZE_MAX);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* We need enough blocks to store the given disk size,
|
||||
so always round up. */
|
||||
blocks = DIV_ROUND_UP(bytes, block_size);
|
||||
blocks = (bytes + block_size - 1) / block_size;
|
||||
|
||||
bmap_size = blocks * sizeof(uint32_t);
|
||||
bmap_size = ROUND_UP(bmap_size, SECTOR_SIZE);
|
||||
bmap_size = ((bmap_size + SECTOR_SIZE - 1) & ~(SECTOR_SIZE -1));
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
pstrcpy(header.text, sizeof(header.text), VDI_TEXT);
|
||||
@@ -774,20 +753,12 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
vdi_header_print(&header);
|
||||
#endif
|
||||
vdi_header_to_le(&header);
|
||||
ret = bdrv_pwrite_sync(bs, offset, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing header to %s", filename);
|
||||
goto exit;
|
||||
if (write(fd, &header, sizeof(header)) < 0) {
|
||||
result = -errno;
|
||||
}
|
||||
offset += sizeof(header);
|
||||
|
||||
if (bmap_size > 0) {
|
||||
bmap = g_try_malloc0(bmap_size);
|
||||
if (bmap == NULL) {
|
||||
ret = -ENOMEM;
|
||||
error_setg(errp, "Could not allocate bmap");
|
||||
goto exit;
|
||||
}
|
||||
uint32_t *bmap = g_malloc0(bmap_size);
|
||||
for (i = 0; i < blocks; i++) {
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
bmap[i] = i;
|
||||
@@ -795,71 +766,59 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
bmap[i] = VDI_UNALLOCATED;
|
||||
}
|
||||
}
|
||||
ret = bdrv_pwrite_sync(bs, offset, bmap, bmap_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing bmap to %s", filename);
|
||||
goto exit;
|
||||
if (write(fd, bmap, bmap_size) < 0) {
|
||||
result = -errno;
|
||||
}
|
||||
offset += bmap_size;
|
||||
g_free(bmap);
|
||||
}
|
||||
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
ret = bdrv_truncate(bs, offset + blocks * block_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to statically allocate %s", filename);
|
||||
goto exit;
|
||||
if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) {
|
||||
result = -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (close(fd) < 0) {
|
||||
result = -errno;
|
||||
}
|
||||
|
||||
exit:
|
||||
bdrv_unref(bs);
|
||||
g_free(bmap);
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void vdi_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
|
||||
qemu_vfree(s->bmap);
|
||||
g_free(s->bmap);
|
||||
|
||||
migrate_del_blocker(s->migration_blocker);
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static QemuOptsList vdi_create_opts = {
|
||||
.name = "vdi-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vdi_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
static QEMUOptionParameter vdi_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "VDI cluster (block) size",
|
||||
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE)
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "VDI cluster (block) size",
|
||||
.value = { .n = DEFAULT_CLUSTER_SIZE },
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_VDI_STATIC_IMAGE)
|
||||
{
|
||||
.name = BLOCK_OPT_STATIC,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "VDI static (pre-allocated) image",
|
||||
.def_value_str = "off"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_STATIC,
|
||||
.type = OPT_FLAG,
|
||||
.help = "VDI static (pre-allocated) image"
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = BLOCK_OPT_NOCOW,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Turn off copy-on-write (valid only on btrfs)"
|
||||
},
|
||||
/* TODO: An additional option to set UUID values might be useful. */
|
||||
{ /* end of list */ }
|
||||
}
|
||||
/* TODO: An additional option to set UUID values might be useful. */
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_vdi = {
|
||||
@@ -881,7 +840,7 @@ static BlockDriver bdrv_vdi = {
|
||||
|
||||
.bdrv_get_info = vdi_get_info,
|
||||
|
||||
.create_opts = &vdi_create_opts,
|
||||
.create_options = vdi_create_options,
|
||||
.bdrv_check = vdi_check,
|
||||
};
|
||||
|
||||
|
||||
+2
-9
@@ -82,6 +82,8 @@ void vhdx_log_desc_le_import(VHDXLogDescriptor *d)
|
||||
assert(d != NULL);
|
||||
|
||||
le32_to_cpus(&d->signature);
|
||||
le32_to_cpus(&d->trailing_bytes);
|
||||
le64_to_cpus(&d->leading_bytes);
|
||||
le64_to_cpus(&d->file_offset);
|
||||
le64_to_cpus(&d->sequence_number);
|
||||
}
|
||||
@@ -97,15 +99,6 @@ void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
|
||||
cpu_to_le64s(&d->sequence_number);
|
||||
}
|
||||
|
||||
void vhdx_log_data_le_import(VHDXLogDataSector *d)
|
||||
{
|
||||
assert(d != NULL);
|
||||
|
||||
le32_to_cpus(&d->data_signature);
|
||||
le32_to_cpus(&d->sequence_high);
|
||||
le32_to_cpus(&d->sequence_low);
|
||||
}
|
||||
|
||||
void vhdx_log_data_le_export(VHDXLogDataSector *d)
|
||||
{
|
||||
assert(d != NULL);
|
||||
|
||||
+20
-37
@@ -84,7 +84,6 @@ static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log,
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
vhdx_log_entry_hdr_le_import(hdr);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
@@ -212,7 +211,7 @@ static bool vhdx_log_hdr_is_valid(VHDXLogEntries *log, VHDXLogEntryHeader *hdr,
|
||||
{
|
||||
int valid = false;
|
||||
|
||||
if (hdr->signature != VHDX_LOG_SIGNATURE) {
|
||||
if (memcmp(&hdr->signature, "loge", 4)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -276,12 +275,12 @@ static bool vhdx_log_desc_is_valid(VHDXLogDescriptor *desc,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (desc->signature == VHDX_LOG_ZERO_SIGNATURE) {
|
||||
if (!memcmp(&desc->signature, "zero", 4)) {
|
||||
if (desc->zero_length % VHDX_LOG_SECTOR_SIZE == 0) {
|
||||
/* valid */
|
||||
ret = true;
|
||||
}
|
||||
} else if (desc->signature == VHDX_LOG_DESC_SIGNATURE) {
|
||||
} else if (!memcmp(&desc->signature, "desc", 4)) {
|
||||
/* valid */
|
||||
ret = true;
|
||||
}
|
||||
@@ -328,15 +327,13 @@ static int vhdx_compute_desc_sectors(uint32_t desc_cnt)
|
||||
* passed into this function. Each descriptor will also be validated,
|
||||
* and error returned if any are invalid. */
|
||||
static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
VHDXLogEntries *log, VHDXLogDescEntries **buffer,
|
||||
bool convert_endian)
|
||||
VHDXLogEntries *log, VHDXLogDescEntries **buffer)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t desc_sectors;
|
||||
uint32_t sectors_read;
|
||||
VHDXLogEntryHeader hdr;
|
||||
VHDXLogDescEntries *desc_entries = NULL;
|
||||
VHDXLogDescriptor desc;
|
||||
int i;
|
||||
|
||||
assert(*buffer == NULL);
|
||||
@@ -345,19 +342,14 @@ static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
vhdx_log_entry_hdr_le_import(&hdr);
|
||||
if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
|
||||
desc_entries = qemu_try_blockalign(bs->file,
|
||||
desc_sectors * VHDX_LOG_SECTOR_SIZE);
|
||||
if (desc_entries == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
desc_entries = qemu_blockalign(bs, desc_sectors * VHDX_LOG_SECTOR_SIZE);
|
||||
|
||||
ret = vhdx_log_read_sectors(bs, log, §ors_read, desc_entries,
|
||||
desc_sectors, false);
|
||||
@@ -371,19 +363,12 @@ static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
|
||||
/* put in proper endianness, and validate each desc */
|
||||
for (i = 0; i < hdr.descriptor_count; i++) {
|
||||
desc = desc_entries->desc[i];
|
||||
vhdx_log_desc_le_import(&desc);
|
||||
if (convert_endian) {
|
||||
desc_entries->desc[i] = desc;
|
||||
}
|
||||
if (vhdx_log_desc_is_valid(&desc, &hdr) == false) {
|
||||
vhdx_log_desc_le_import(&desc_entries->desc[i]);
|
||||
if (vhdx_log_desc_is_valid(&desc_entries->desc[i], &hdr) == false) {
|
||||
ret = -EINVAL;
|
||||
goto free_and_exit;
|
||||
}
|
||||
}
|
||||
if (convert_endian) {
|
||||
desc_entries->hdr = hdr;
|
||||
}
|
||||
|
||||
*buffer = desc_entries;
|
||||
goto exit;
|
||||
@@ -418,7 +403,7 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc,
|
||||
|
||||
buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
|
||||
|
||||
if (desc->signature == VHDX_LOG_DESC_SIGNATURE) {
|
||||
if (!memcmp(&desc->signature, "desc", 4)) {
|
||||
/* data sector */
|
||||
if (data == NULL) {
|
||||
ret = -EFAULT;
|
||||
@@ -446,15 +431,10 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc,
|
||||
|
||||
memcpy(buffer+offset, &desc->trailing_bytes, 4);
|
||||
|
||||
} else if (desc->signature == VHDX_LOG_ZERO_SIGNATURE) {
|
||||
} else if (!memcmp(&desc->signature, "zero", 4)) {
|
||||
/* write 'count' sectors of sector */
|
||||
memset(buffer, 0, VHDX_LOG_SECTOR_SIZE);
|
||||
count = desc->zero_length / VHDX_LOG_SECTOR_SIZE;
|
||||
} else {
|
||||
error_report("Invalid VHDX log descriptor entry signature 0x%" PRIx32,
|
||||
desc->signature);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
file_offset = desc->file_offset;
|
||||
@@ -513,13 +493,13 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries, true);
|
||||
ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < desc_entries->hdr.descriptor_count; i++) {
|
||||
if (desc_entries->desc[i].signature == VHDX_LOG_DESC_SIGNATURE) {
|
||||
if (!memcmp(&desc_entries->desc[i].signature, "desc", 4)) {
|
||||
/* data sector, so read a sector to flush */
|
||||
ret = vhdx_log_read_sectors(bs, &logs->log, §ors_read,
|
||||
data, 1, false);
|
||||
@@ -530,7 +510,6 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
vhdx_log_data_le_import(data);
|
||||
}
|
||||
|
||||
ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data);
|
||||
@@ -579,6 +558,9 @@ static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
goto inc_and_exit;
|
||||
}
|
||||
|
||||
vhdx_log_entry_hdr_le_import(&hdr);
|
||||
|
||||
|
||||
if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) {
|
||||
goto inc_and_exit;
|
||||
}
|
||||
@@ -591,13 +573,13 @@ static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
|
||||
desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
|
||||
|
||||
/* Read all log sectors, and calculate log checksum */
|
||||
/* Read desc sectors, and calculate log checksum */
|
||||
|
||||
total_sectors = hdr.entry_length / VHDX_LOG_SECTOR_SIZE;
|
||||
|
||||
|
||||
/* read_desc() will increment the read idx */
|
||||
ret = vhdx_log_read_desc(bs, s, log, &desc_buffer, false);
|
||||
ret = vhdx_log_read_desc(bs, s, log, &desc_buffer);
|
||||
if (ret < 0) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
@@ -620,7 +602,7 @@ static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
}
|
||||
crc ^= 0xffffffff;
|
||||
if (crc != hdr.checksum) {
|
||||
if (crc != desc_buffer->hdr.checksum) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
@@ -923,7 +905,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
buffer = qemu_blockalign(bs, total_length);
|
||||
memcpy(buffer, &new_hdr, sizeof(new_hdr));
|
||||
|
||||
new_desc = buffer + sizeof(new_hdr);
|
||||
new_desc = (VHDXLogDescriptor *) (buffer + sizeof(new_hdr));
|
||||
data_sector = buffer + (desc_sectors * VHDX_LOG_SECTOR_SIZE);
|
||||
data_tmp = data;
|
||||
|
||||
@@ -980,6 +962,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
* last data sector */
|
||||
vhdx_update_checksum(buffer, total_length,
|
||||
offsetof(VHDXLogEntryHeader, checksum));
|
||||
cpu_to_le32s((uint32_t *)(buffer + 4));
|
||||
|
||||
/* now write to the log */
|
||||
ret = vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer,
|
||||
|
||||
+100
-130
@@ -99,8 +99,7 @@ static const MSGUID logical_sector_guid = { .data1 = 0x8141bf1d,
|
||||
/* Each parent type must have a valid GUID; this is for parent images
|
||||
* of type 'VHDX'. If we were to allow e.g. a QCOW2 parent, we would
|
||||
* need to make up our own QCOW2 GUID type */
|
||||
static const MSGUID parent_vhdx_guid __attribute__((unused))
|
||||
= { .data1 = 0xb04aefb7,
|
||||
static const MSGUID parent_vhdx_guid = { .data1 = 0xb04aefb7,
|
||||
.data2 = 0xd19e,
|
||||
.data3 = 0x4a81,
|
||||
.data4 = { 0xb7, 0x89, 0x25, 0xb8,
|
||||
@@ -136,8 +135,10 @@ typedef struct VHDXSectorInfo {
|
||||
* buf: buffer pointer
|
||||
* size: size of buffer (must be > crc_offset+4)
|
||||
*
|
||||
* Note: The buffer should have all multi-byte data in little-endian format,
|
||||
* and the resulting checksum is in little endian format.
|
||||
* Note: The resulting checksum is in the CPU endianness, not necessarily
|
||||
* in the file format endianness (LE). Any header export to disk should
|
||||
* make sure that vhdx_header_le_export() is used to convert to the
|
||||
* correct endianness
|
||||
*/
|
||||
uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
|
||||
{
|
||||
@@ -148,7 +149,6 @@ uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
|
||||
|
||||
memset(buf + crc_offset, 0, sizeof(crc));
|
||||
crc = crc32c(0xffffffff, buf, size);
|
||||
cpu_to_le32s(&crc);
|
||||
memcpy(buf + crc_offset, &crc, sizeof(crc));
|
||||
|
||||
return crc;
|
||||
@@ -300,7 +300,7 @@ static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr,
|
||||
{
|
||||
uint8_t *buffer = NULL;
|
||||
int ret;
|
||||
VHDXHeader *header_le;
|
||||
VHDXHeader header_le;
|
||||
|
||||
assert(bs_file != NULL);
|
||||
assert(hdr != NULL);
|
||||
@@ -321,12 +321,11 @@ static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr,
|
||||
}
|
||||
|
||||
/* overwrite the actual VHDXHeader portion */
|
||||
header_le = (VHDXHeader *)buffer;
|
||||
memcpy(header_le, hdr, sizeof(VHDXHeader));
|
||||
vhdx_header_le_export(hdr, header_le);
|
||||
vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
|
||||
offsetof(VHDXHeader, checksum));
|
||||
ret = bdrv_pwrite_sync(bs_file, offset, header_le, sizeof(VHDXHeader));
|
||||
memcpy(buffer, hdr, sizeof(VHDXHeader));
|
||||
hdr->checksum = vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
|
||||
offsetof(VHDXHeader, checksum));
|
||||
vhdx_header_le_export(hdr, &header_le);
|
||||
ret = bdrv_pwrite_sync(bs_file, offset, &header_le, sizeof(VHDXHeader));
|
||||
|
||||
exit:
|
||||
qemu_vfree(buffer);
|
||||
@@ -433,14 +432,13 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
/* copy over just the relevant portion that we need */
|
||||
memcpy(header1, buffer, sizeof(VHDXHeader));
|
||||
vhdx_header_le_import(header1);
|
||||
|
||||
if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4)) {
|
||||
vhdx_header_le_import(header1);
|
||||
if (header1->signature == VHDX_HEADER_SIGNATURE &&
|
||||
header1->version == 1) {
|
||||
h1_seq = header1->sequence_number;
|
||||
h1_valid = true;
|
||||
}
|
||||
if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4) &&
|
||||
!memcmp(&header1->signature, "head", 4) &&
|
||||
header1->version == 1) {
|
||||
h1_seq = header1->sequence_number;
|
||||
h1_valid = true;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE);
|
||||
@@ -449,14 +447,13 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
/* copy over just the relevant portion that we need */
|
||||
memcpy(header2, buffer, sizeof(VHDXHeader));
|
||||
vhdx_header_le_import(header2);
|
||||
|
||||
if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4)) {
|
||||
vhdx_header_le_import(header2);
|
||||
if (header2->signature == VHDX_HEADER_SIGNATURE &&
|
||||
header2->version == 1) {
|
||||
h2_seq = header2->sequence_number;
|
||||
h2_valid = true;
|
||||
}
|
||||
if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4) &&
|
||||
!memcmp(&header2->signature, "head", 4) &&
|
||||
header2->version == 1) {
|
||||
h2_seq = header2->sequence_number;
|
||||
h2_valid = true;
|
||||
}
|
||||
|
||||
/* If there is only 1 valid header (or no valid headers), we
|
||||
@@ -476,14 +473,7 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
} else if (h2_seq > h1_seq) {
|
||||
s->curr_header = 1;
|
||||
} else {
|
||||
/* The Microsoft Disk2VHD tool will create 2 identical
|
||||
* headers, with identical sequence numbers. If the headers are
|
||||
* identical, don't consider the file corrupt */
|
||||
if (!memcmp(header1, header2, sizeof(VHDXHeader))) {
|
||||
s->curr_header = 0;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,21 +512,15 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
goto fail;
|
||||
}
|
||||
memcpy(&s->rt, buffer, sizeof(s->rt));
|
||||
vhdx_region_header_le_import(&s->rt);
|
||||
offset += sizeof(s->rt);
|
||||
|
||||
if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4)) {
|
||||
if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4) ||
|
||||
memcmp(&s->rt.signature, "regi", 4)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vhdx_region_header_le_import(&s->rt);
|
||||
|
||||
if (s->rt.signature != VHDX_REGION_SIGNATURE) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* Per spec, maximum region table entry count is 2047 */
|
||||
if (s->rt.entry_count > 2047) {
|
||||
ret = -EINVAL;
|
||||
@@ -639,7 +623,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
|
||||
vhdx_metadata_header_le_import(&s->metadata_hdr);
|
||||
|
||||
if (s->metadata_hdr.signature != VHDX_METADATA_SIGNATURE) {
|
||||
if (memcmp(&s->metadata_hdr.signature, "metadata", 8)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
@@ -959,11 +943,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* s->bat is freed in vhdx_close() */
|
||||
s->bat = qemu_try_blockalign(bs->file, s->bat_rt.length);
|
||||
if (s->bat == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
s->bat = qemu_blockalign(bs, s->bat_rt.length);
|
||||
|
||||
ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length);
|
||||
if (ret < 0) {
|
||||
@@ -1004,7 +984,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Disable migration when VHDX images are used */
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"vhdx", bdrv_get_device_name(bs), "live migration");
|
||||
"vhdx", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
@@ -1382,7 +1362,7 @@ static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size,
|
||||
int ret = 0;
|
||||
VHDXHeader *hdr = NULL;
|
||||
|
||||
hdr = g_new0(VHDXHeader, 1);
|
||||
hdr = g_malloc0(sizeof(VHDXHeader));
|
||||
|
||||
hdr->signature = VHDX_HEADER_SIGNATURE;
|
||||
hdr->sequence_number = g_random_int();
|
||||
@@ -1408,12 +1388,6 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define VHDX_METADATA_ENTRY_BUFFER_SIZE \
|
||||
(sizeof(VHDXFileParameters) +\
|
||||
sizeof(VHDXVirtualDiskSize) +\
|
||||
sizeof(VHDXPage83Data) +\
|
||||
sizeof(VHDXVirtualDiskLogicalSectorSize) +\
|
||||
sizeof(VHDXVirtualDiskPhysicalSectorSize))
|
||||
|
||||
/*
|
||||
* Create the Metadata entries.
|
||||
@@ -1452,7 +1426,11 @@ static int vhdx_create_new_metadata(BlockDriverState *bs,
|
||||
VHDXVirtualDiskLogicalSectorSize *mt_log_sector_size;
|
||||
VHDXVirtualDiskPhysicalSectorSize *mt_phys_sector_size;
|
||||
|
||||
entry_buffer = g_malloc0(VHDX_METADATA_ENTRY_BUFFER_SIZE);
|
||||
entry_buffer = g_malloc0(sizeof(VHDXFileParameters) +
|
||||
sizeof(VHDXVirtualDiskSize) +
|
||||
sizeof(VHDXPage83Data) +
|
||||
sizeof(VHDXVirtualDiskLogicalSectorSize) +
|
||||
sizeof(VHDXVirtualDiskPhysicalSectorSize));
|
||||
|
||||
mt_file_params = entry_buffer;
|
||||
offset += sizeof(VHDXFileParameters);
|
||||
@@ -1533,7 +1511,7 @@ static int vhdx_create_new_metadata(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer,
|
||||
VHDX_METADATA_ENTRY_BUFFER_SIZE);
|
||||
VHDX_HEADER_BLOCK_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -1555,8 +1533,7 @@ exit:
|
||||
*/
|
||||
static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
uint64_t image_size, VHDXImageType type,
|
||||
bool use_zero_blocks, uint64_t file_offset,
|
||||
uint32_t length)
|
||||
bool use_zero_blocks, VHDXRegionTableEntry *rt_bat)
|
||||
{
|
||||
int ret = 0;
|
||||
uint64_t data_file_offset;
|
||||
@@ -1571,7 +1548,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
/* this gives a data start after BAT/bitmap entries, and well
|
||||
* past any metadata entries (with a 4 MB buffer for future
|
||||
* expansion */
|
||||
data_file_offset = file_offset + length + 5 * MiB;
|
||||
data_file_offset = rt_bat->file_offset + rt_bat->length + 5 * MiB;
|
||||
total_sectors = image_size >> s->logical_sector_size_bits;
|
||||
|
||||
if (type == VHDX_TYPE_DYNAMIC) {
|
||||
@@ -1595,11 +1572,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
use_zero_blocks ||
|
||||
bdrv_has_zero_init(bs) == 0) {
|
||||
/* for a fixed file, the default BAT entry is not zero */
|
||||
s->bat = g_try_malloc0(length);
|
||||
if (length && s->bat == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
s->bat = g_malloc0(rt_bat->length);
|
||||
block_state = type == VHDX_TYPE_FIXED ? PAYLOAD_BLOCK_FULLY_PRESENT :
|
||||
PAYLOAD_BLOCK_NOT_PRESENT;
|
||||
block_state = use_zero_blocks ? PAYLOAD_BLOCK_ZERO : block_state;
|
||||
@@ -1614,7 +1587,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
cpu_to_le64s(&s->bat[sinfo.bat_idx]);
|
||||
sector_num += s->sectors_per_block;
|
||||
}
|
||||
ret = bdrv_pwrite(bs, file_offset, s->bat, length);
|
||||
ret = bdrv_pwrite(bs, rt_bat->file_offset, s->bat, rt_bat->length);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -1646,8 +1619,6 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
|
||||
int ret = 0;
|
||||
uint32_t offset = 0;
|
||||
void *buffer = NULL;
|
||||
uint64_t bat_file_offset;
|
||||
uint32_t bat_length;
|
||||
BDRVVHDXState *s = NULL;
|
||||
VHDXRegionTableHeader *region_table;
|
||||
VHDXRegionTableEntry *rt_bat;
|
||||
@@ -1657,7 +1628,7 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
|
||||
|
||||
/* Populate enough of the BDRVVHDXState to be able to use the
|
||||
* pre-existing BAT calculation, translation, and update functions */
|
||||
s = g_new0(BDRVVHDXState, 1);
|
||||
s = g_malloc0(sizeof(BDRVVHDXState));
|
||||
|
||||
s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) *
|
||||
(uint64_t) sector_size / (uint64_t) block_size;
|
||||
@@ -1696,26 +1667,19 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
|
||||
rt_metadata->length = 1 * MiB; /* min size, and more than enough */
|
||||
*metadata_offset = rt_metadata->file_offset;
|
||||
|
||||
bat_file_offset = rt_bat->file_offset;
|
||||
bat_length = rt_bat->length;
|
||||
|
||||
vhdx_region_header_le_export(region_table);
|
||||
vhdx_region_entry_le_export(rt_bat);
|
||||
vhdx_region_entry_le_export(rt_metadata);
|
||||
|
||||
vhdx_update_checksum(buffer, VHDX_HEADER_BLOCK_SIZE,
|
||||
offsetof(VHDXRegionTableHeader, checksum));
|
||||
|
||||
|
||||
/* The region table gives us the data we need to create the BAT,
|
||||
* so do that now */
|
||||
ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks,
|
||||
bat_file_offset, bat_length);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks, rt_bat);
|
||||
|
||||
/* Now write out the region headers to disk */
|
||||
vhdx_region_header_le_export(region_table);
|
||||
vhdx_region_entry_le_export(rt_bat);
|
||||
vhdx_region_entry_le_export(rt_metadata);
|
||||
|
||||
ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer,
|
||||
VHDX_HEADER_BLOCK_SIZE);
|
||||
if (ret < 0) {
|
||||
@@ -1728,6 +1692,7 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
||||
exit:
|
||||
g_free(s);
|
||||
g_free(buffer);
|
||||
@@ -1751,7 +1716,8 @@ exit:
|
||||
* .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
|
||||
* 1MB
|
||||
*/
|
||||
static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int vhdx_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
uint64_t image_size = (uint64_t) 2 * GiB;
|
||||
@@ -1764,16 +1730,24 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
gunichar2 *creator = NULL;
|
||||
glong creator_items;
|
||||
BlockDriverState *bs;
|
||||
char *type = NULL;
|
||||
const char *type = NULL;
|
||||
VHDXImageType image_type;
|
||||
Error *local_err = NULL;
|
||||
|
||||
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0);
|
||||
block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0);
|
||||
type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, false);
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
image_size = options->value.n;
|
||||
} else if (!strcmp(options->name, VHDX_BLOCK_OPT_LOG_SIZE)) {
|
||||
log_size = options->value.n;
|
||||
} else if (!strcmp(options->name, VHDX_BLOCK_OPT_BLOCK_SIZE)) {
|
||||
block_size = options->value.n;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
|
||||
type = options->value.s;
|
||||
} else if (!strcmp(options->name, VHDX_BLOCK_OPT_ZERO)) {
|
||||
use_zero_blocks = options->value.n != 0;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
if (image_size > VHDX_MAX_IMAGE_SIZE) {
|
||||
error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
|
||||
@@ -1782,7 +1756,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
if (type == NULL) {
|
||||
type = g_strdup("dynamic");
|
||||
type = "dynamic";
|
||||
}
|
||||
|
||||
if (!strcmp(type, "dynamic")) {
|
||||
@@ -1822,7 +1796,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
|
||||
block_size;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
@@ -1878,10 +1852,10 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
|
||||
|
||||
delete_and_exit:
|
||||
bdrv_unref(bs);
|
||||
exit:
|
||||
g_free(type);
|
||||
g_free(creator);
|
||||
return ret;
|
||||
}
|
||||
@@ -1904,41 +1878,37 @@ static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList vhdx_create_opts = {
|
||||
.name = "vhdx-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vhdx_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size; max of 64TB."
|
||||
},
|
||||
{
|
||||
.name = VHDX_BLOCK_OPT_LOG_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.def_value_str = stringify(DEFAULT_LOG_SIZE),
|
||||
.help = "Log size; min 1MB."
|
||||
},
|
||||
{
|
||||
.name = VHDX_BLOCK_OPT_BLOCK_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.def_value_str = stringify(0),
|
||||
.help = "Block Size; min 1MB, max 256MB. " \
|
||||
"0 means auto-calculate based on image size."
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "VHDX format type, can be either 'dynamic' or 'fixed'. "\
|
||||
"Default is 'dynamic'."
|
||||
},
|
||||
{
|
||||
.name = VHDX_BLOCK_OPT_ZERO,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Force use of payload blocks of type 'ZERO'. Non-standard."
|
||||
},
|
||||
{ NULL }
|
||||
}
|
||||
static QEMUOptionParameter vhdx_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size; max of 64TB."
|
||||
},
|
||||
{
|
||||
.name = VHDX_BLOCK_OPT_LOG_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.value.n = 1 * MiB,
|
||||
.help = "Log size; min 1MB."
|
||||
},
|
||||
{
|
||||
.name = VHDX_BLOCK_OPT_BLOCK_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.value.n = 0,
|
||||
.help = "Block Size; min 1MB, max 256MB. " \
|
||||
"0 means auto-calculate based on image size."
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = OPT_STRING,
|
||||
.help = "VHDX format type, can be either 'dynamic' or 'fixed'. "\
|
||||
"Default is 'dynamic'."
|
||||
},
|
||||
{
|
||||
.name = VHDX_BLOCK_OPT_ZERO,
|
||||
.type = OPT_FLAG,
|
||||
.help = "Force use of payload blocks of type 'ZERO'. Non-standard."
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_vhdx = {
|
||||
@@ -1954,7 +1924,7 @@ static BlockDriver bdrv_vhdx = {
|
||||
.bdrv_get_info = vhdx_get_info,
|
||||
.bdrv_check = vhdx_check,
|
||||
|
||||
.create_opts = &vhdx_create_opts,
|
||||
.create_options = vhdx_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_vhdx_init(void)
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#define GiB (MiB * 1024)
|
||||
#define TiB ((uint64_t) GiB * 1024)
|
||||
|
||||
#define DEFAULT_LOG_SIZE 1048576 /* 1MiB */
|
||||
/* Structures and fields present in the VHDX file */
|
||||
|
||||
/* The header section has the following blocks,
|
||||
@@ -435,7 +434,6 @@ void vhdx_header_le_import(VHDXHeader *h);
|
||||
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h);
|
||||
void vhdx_log_desc_le_import(VHDXLogDescriptor *d);
|
||||
void vhdx_log_desc_le_export(VHDXLogDescriptor *d);
|
||||
void vhdx_log_data_le_import(VHDXLogDataSector *d);
|
||||
void vhdx_log_data_le_export(VHDXLogDataSector *d);
|
||||
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr);
|
||||
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr);
|
||||
|
||||
+178
-311
@@ -106,7 +106,6 @@ typedef struct VmdkExtent {
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
|
||||
int64_t cluster_sectors;
|
||||
int64_t next_cluster_sector;
|
||||
char *type;
|
||||
} VmdkExtent;
|
||||
|
||||
@@ -125,6 +124,7 @@ typedef struct BDRVVmdkState {
|
||||
} BDRVVmdkState;
|
||||
|
||||
typedef struct VmdkMetaData {
|
||||
uint32_t offset;
|
||||
unsigned int l1_index;
|
||||
unsigned int l2_index;
|
||||
unsigned int l2_offset;
|
||||
@@ -233,7 +233,7 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
|
||||
return;
|
||||
}
|
||||
s->num_extents--;
|
||||
s->extents = g_renew(VmdkExtent, s->extents, s->num_extents);
|
||||
s->extents = g_realloc(s->extents, s->num_extents * sizeof(VmdkExtent));
|
||||
}
|
||||
|
||||
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
@@ -262,7 +262,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
p_name = strstr(desc, cid_str);
|
||||
if (p_name != NULL) {
|
||||
p_name += cid_str_size;
|
||||
sscanf(p_name, "%" SCNx32, &cid);
|
||||
sscanf(p_name, "%x", &cid);
|
||||
}
|
||||
|
||||
return cid;
|
||||
@@ -290,7 +290,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
p_name = strstr(desc, "CID");
|
||||
if (p_name != NULL) {
|
||||
p_name += sizeof("CID");
|
||||
snprintf(p_name, sizeof(desc) - (p_name - desc), "%" PRIx32 "\n", cid);
|
||||
snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
|
||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
||||
}
|
||||
|
||||
@@ -397,7 +397,6 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
||||
{
|
||||
VmdkExtent *extent;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int64_t nb_sectors;
|
||||
|
||||
if (cluster_sectors > 0x200000) {
|
||||
/* 0x200000 * 512Bytes = 1GB for one cluster is unrealistic */
|
||||
@@ -413,12 +412,8 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
nb_sectors = bdrv_nb_sectors(file);
|
||||
if (nb_sectors < 0) {
|
||||
return nb_sectors;
|
||||
}
|
||||
|
||||
s->extents = g_renew(VmdkExtent, s->extents, s->num_extents + 1);
|
||||
s->extents = g_realloc(s->extents,
|
||||
(s->num_extents + 1) * sizeof(VmdkExtent));
|
||||
extent = &s->extents[s->num_extents];
|
||||
s->num_extents++;
|
||||
|
||||
@@ -432,7 +427,6 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
||||
extent->l1_entry_sectors = l2_size * cluster_sectors;
|
||||
extent->l2_size = l2_size;
|
||||
extent->cluster_sectors = flat ? sectors : cluster_sectors;
|
||||
extent->next_cluster_sector = ROUND_UP(nb_sectors, cluster_sectors);
|
||||
|
||||
if (s->num_extents > 1) {
|
||||
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
|
||||
@@ -454,11 +448,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
|
||||
/* read the L1 table */
|
||||
l1_size = extent->l1_size * sizeof(uint32_t);
|
||||
extent->l1_table = g_try_malloc(l1_size);
|
||||
if (l1_size && extent->l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
extent->l1_table = g_malloc(l1_size);
|
||||
ret = bdrv_pread(extent->file,
|
||||
extent->l1_table_offset,
|
||||
extent->l1_table,
|
||||
@@ -474,11 +464,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
}
|
||||
|
||||
if (extent->l1_backup_table_offset) {
|
||||
extent->l1_backup_table = g_try_malloc(l1_size);
|
||||
if (l1_size && extent->l1_backup_table == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_l1;
|
||||
}
|
||||
extent->l1_backup_table = g_malloc(l1_size);
|
||||
ret = bdrv_pread(extent->file,
|
||||
extent->l1_backup_table_offset,
|
||||
extent->l1_backup_table,
|
||||
@@ -495,7 +481,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
}
|
||||
|
||||
extent->l2_cache =
|
||||
g_new(uint32_t, extent->l2_size * L2_CACHE_SIZE);
|
||||
g_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
||||
return 0;
|
||||
fail_l1b:
|
||||
g_free(extent->l1_backup_table);
|
||||
@@ -654,10 +640,10 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
|
||||
if (le32_to_cpu(header.version) > 3) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "VMDK version %" PRId32,
|
||||
snprintf(buf, sizeof(buf), "VMDK version %d",
|
||||
le32_to_cpu(header.version));
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bdrv_get_device_name(bs), "vmdk", buf);
|
||||
bs->device_name, "vmdk", buf);
|
||||
return -ENOTSUP;
|
||||
} else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {
|
||||
/* VMware KB 2064959 explains that version 3 added support for
|
||||
@@ -683,10 +669,10 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {
|
||||
l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;
|
||||
}
|
||||
if (bdrv_nb_sectors(file) < le64_to_cpu(header.grain_offset)) {
|
||||
error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes",
|
||||
(int64_t)(le64_to_cpu(header.grain_offset)
|
||||
* BDRV_SECTOR_SIZE));
|
||||
if (bdrv_getlength(file) <
|
||||
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "File truncated, expecting at least %lld bytes",
|
||||
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -834,7 +820,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
ret = vmdk_add_extent(bs, extent_file, true, sectors,
|
||||
0, 0, 0, 0, 0, &extent, errp);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(extent_file);
|
||||
return ret;
|
||||
}
|
||||
extent->flat_start_offset = flat_offset << 9;
|
||||
@@ -846,15 +831,14 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
} else {
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
|
||||
}
|
||||
g_free(buf);
|
||||
if (ret) {
|
||||
g_free(buf);
|
||||
bdrv_unref(extent_file);
|
||||
return ret;
|
||||
}
|
||||
extent = &s->extents[s->num_extents - 1];
|
||||
} else {
|
||||
error_setg(errp, "Unsupported extent type '%s'", type);
|
||||
bdrv_unref(extent_file);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
extent->type = g_strdup(type);
|
||||
@@ -939,7 +923,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Disable migration when VMDK images are used */
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"vmdk", bdrv_get_device_name(bs), "live migration");
|
||||
"vmdk", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
g_free(buf);
|
||||
return 0;
|
||||
@@ -953,7 +937,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
static int vmdk_refresh_limits(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i;
|
||||
@@ -965,99 +949,61 @@ static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
s->extents[i].cluster_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_whole_cluster
|
||||
*
|
||||
* Copy backing file's cluster that covers @sector_num, otherwise write zero,
|
||||
* to the cluster at @cluster_sector_num.
|
||||
*
|
||||
* If @skip_start_sector < @skip_end_sector, the relative range
|
||||
* [@skip_start_sector, @skip_end_sector) is not copied or written, and leave
|
||||
* it for call to write user data in the request.
|
||||
*/
|
||||
static int get_whole_cluster(BlockDriverState *bs,
|
||||
VmdkExtent *extent,
|
||||
uint64_t cluster_sector_num,
|
||||
uint64_t sector_num,
|
||||
uint64_t skip_start_sector,
|
||||
uint64_t skip_end_sector)
|
||||
VmdkExtent *extent,
|
||||
uint64_t cluster_offset,
|
||||
uint64_t offset,
|
||||
bool allocate)
|
||||
{
|
||||
int ret = VMDK_OK;
|
||||
int64_t cluster_bytes;
|
||||
uint8_t *whole_grain;
|
||||
uint8_t *whole_grain = NULL;
|
||||
|
||||
/* For COW, align request sector_num to cluster start */
|
||||
sector_num = QEMU_ALIGN_DOWN(sector_num, extent->cluster_sectors);
|
||||
cluster_bytes = extent->cluster_sectors << BDRV_SECTOR_BITS;
|
||||
whole_grain = qemu_blockalign(bs, cluster_bytes);
|
||||
|
||||
if (!bs->backing_hd) {
|
||||
memset(whole_grain, 0, skip_start_sector << BDRV_SECTOR_BITS);
|
||||
memset(whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), 0,
|
||||
cluster_bytes - (skip_end_sector << BDRV_SECTOR_BITS));
|
||||
}
|
||||
|
||||
assert(skip_end_sector <= extent->cluster_sectors);
|
||||
/* we will be here if it's first write on non-exist grain(cluster).
|
||||
* try to read from parent image, if exist */
|
||||
if (bs->backing_hd && !vmdk_is_cid_valid(bs)) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read backing data before skip range */
|
||||
if (skip_start_sector > 0) {
|
||||
if (bs->backing_hd) {
|
||||
ret = bdrv_read(bs->backing_hd, sector_num,
|
||||
whole_grain, skip_start_sector);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (bs->backing_hd) {
|
||||
whole_grain =
|
||||
qemu_blockalign(bs, extent->cluster_sectors << BDRV_SECTOR_BITS);
|
||||
if (!vmdk_is_cid_valid(bs)) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_write(extent->file, cluster_sector_num, whole_grain,
|
||||
skip_start_sector);
|
||||
|
||||
/* floor offset to cluster */
|
||||
offset -= offset % (extent->cluster_sectors * 512);
|
||||
ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
|
||||
extent->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Write grain only into the active image */
|
||||
ret = bdrv_write(extent->file, cluster_offset, whole_grain,
|
||||
extent->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
/* Read backing data after skip range */
|
||||
if (skip_end_sector < extent->cluster_sectors) {
|
||||
if (bs->backing_hd) {
|
||||
ret = bdrv_read(bs->backing_hd, sector_num + skip_end_sector,
|
||||
whole_grain + (skip_end_sector << BDRV_SECTOR_BITS),
|
||||
extent->cluster_sectors - skip_end_sector);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ret = bdrv_write(extent->file, cluster_sector_num + skip_end_sector,
|
||||
whole_grain + (skip_end_sector << BDRV_SECTOR_BITS),
|
||||
extent->cluster_sectors - skip_end_sector);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
qemu_vfree(whole_grain);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
|
||||
uint32_t offset)
|
||||
static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data)
|
||||
{
|
||||
offset = cpu_to_le32(offset);
|
||||
uint32_t offset;
|
||||
QEMU_BUILD_BUG_ON(sizeof(offset) != sizeof(m_data->offset));
|
||||
offset = cpu_to_le32(m_data->offset);
|
||||
/* update L2 table */
|
||||
if (bdrv_pwrite_sync(
|
||||
extent->file,
|
||||
((int64_t)m_data->l2_offset * 512)
|
||||
+ (m_data->l2_index * sizeof(offset)),
|
||||
+ (m_data->l2_index * sizeof(m_data->offset)),
|
||||
&offset, sizeof(offset)) < 0) {
|
||||
return VMDK_ERROR;
|
||||
}
|
||||
@@ -1067,7 +1013,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
|
||||
if (bdrv_pwrite_sync(
|
||||
extent->file,
|
||||
((int64_t)m_data->l2_offset * 512)
|
||||
+ (m_data->l2_index * sizeof(offset)),
|
||||
+ (m_data->l2_index * sizeof(m_data->offset)),
|
||||
&offset, sizeof(offset)) < 0) {
|
||||
return VMDK_ERROR;
|
||||
}
|
||||
@@ -1079,41 +1025,17 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
|
||||
return VMDK_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_cluster_offset
|
||||
*
|
||||
* Look up cluster offset in extent file by sector number, and store in
|
||||
* @cluster_offset.
|
||||
*
|
||||
* For flat extents, the start offset as parsed from the description file is
|
||||
* returned.
|
||||
*
|
||||
* For sparse extents, look up in L1, L2 table. If allocate is true, return an
|
||||
* offset for a new cluster and update L2 cache. If there is a backing file,
|
||||
* COW is done before returning; otherwise, zeroes are written to the allocated
|
||||
* cluster. Both COW and zero writing skips the sector range
|
||||
* [@skip_start_sector, @skip_end_sector) passed in by caller, because caller
|
||||
* has new data to write there.
|
||||
*
|
||||
* Returns: VMDK_OK if cluster exists and mapped in the image.
|
||||
* VMDK_UNALLOC if cluster is not mapped and @allocate is false.
|
||||
* VMDK_ERROR if failed.
|
||||
*/
|
||||
static int get_cluster_offset(BlockDriverState *bs,
|
||||
VmdkExtent *extent,
|
||||
VmdkMetaData *m_data,
|
||||
uint64_t offset,
|
||||
bool allocate,
|
||||
uint64_t *cluster_offset,
|
||||
uint64_t skip_start_sector,
|
||||
uint64_t skip_end_sector)
|
||||
VmdkExtent *extent,
|
||||
VmdkMetaData *m_data,
|
||||
uint64_t offset,
|
||||
int allocate,
|
||||
uint64_t *cluster_offset)
|
||||
{
|
||||
unsigned int l1_index, l2_offset, l2_index;
|
||||
int min_index, i, j;
|
||||
uint32_t min_count, *l2_table;
|
||||
bool zeroed = false;
|
||||
int64_t ret;
|
||||
int64_t cluster_sector;
|
||||
|
||||
if (m_data) {
|
||||
m_data->valid = 0;
|
||||
@@ -1167,41 +1089,52 @@ static int get_cluster_offset(BlockDriverState *bs,
|
||||
extent->l2_cache_counts[min_index] = 1;
|
||||
found:
|
||||
l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
|
||||
cluster_sector = le32_to_cpu(l2_table[l2_index]);
|
||||
*cluster_offset = le32_to_cpu(l2_table[l2_index]);
|
||||
|
||||
if (m_data) {
|
||||
m_data->valid = 1;
|
||||
m_data->l1_index = l1_index;
|
||||
m_data->l2_index = l2_index;
|
||||
m_data->offset = *cluster_offset;
|
||||
m_data->l2_offset = l2_offset;
|
||||
m_data->l2_cache_entry = &l2_table[l2_index];
|
||||
}
|
||||
if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
|
||||
if (extent->has_zero_grain && *cluster_offset == VMDK_GTE_ZEROED) {
|
||||
zeroed = true;
|
||||
}
|
||||
|
||||
if (!cluster_sector || zeroed) {
|
||||
if (!*cluster_offset || zeroed) {
|
||||
if (!allocate) {
|
||||
return zeroed ? VMDK_ZEROED : VMDK_UNALLOC;
|
||||
}
|
||||
|
||||
cluster_sector = extent->next_cluster_sector;
|
||||
extent->next_cluster_sector += extent->cluster_sectors;
|
||||
/* Avoid the L2 tables update for the images that have snapshots. */
|
||||
*cluster_offset = bdrv_getlength(extent->file);
|
||||
if (!extent->compressed) {
|
||||
bdrv_truncate(
|
||||
extent->file,
|
||||
*cluster_offset + (extent->cluster_sectors << 9)
|
||||
);
|
||||
}
|
||||
|
||||
*cluster_offset >>= 9;
|
||||
l2_table[l2_index] = cpu_to_le32(*cluster_offset);
|
||||
|
||||
/* First of all we write grain itself, to avoid race condition
|
||||
* that may to corrupt the image.
|
||||
* This problem may occur because of insufficient space on host disk
|
||||
* or inappropriate VM shutdown.
|
||||
*/
|
||||
ret = get_whole_cluster(bs, extent,
|
||||
cluster_sector,
|
||||
offset >> BDRV_SECTOR_BITS,
|
||||
skip_start_sector, skip_end_sector);
|
||||
if (ret) {
|
||||
return ret;
|
||||
if (get_whole_cluster(
|
||||
bs, extent, *cluster_offset, offset, allocate) == -1) {
|
||||
return VMDK_ERROR;
|
||||
}
|
||||
|
||||
if (m_data) {
|
||||
m_data->offset = *cluster_offset;
|
||||
}
|
||||
}
|
||||
*cluster_offset = cluster_sector << BDRV_SECTOR_BITS;
|
||||
*cluster_offset <<= 9;
|
||||
return VMDK_OK;
|
||||
}
|
||||
|
||||
@@ -1236,8 +1169,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
}
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num * 512, false, &offset,
|
||||
0, 0);
|
||||
sector_num * 512, 0, &offset);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
switch (ret) {
|
||||
@@ -1390,9 +1322,9 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
if (!extent) {
|
||||
return -EIO;
|
||||
}
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num << 9, false, &cluster_offset,
|
||||
0, 0);
|
||||
ret = get_cluster_offset(
|
||||
bs, extent, NULL,
|
||||
sector_num << 9, 0, &cluster_offset);
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
@@ -1473,17 +1405,12 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
if (!extent) {
|
||||
return -EIO;
|
||||
}
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
}
|
||||
ret = get_cluster_offset(bs, extent, &m_data, sector_num << 9,
|
||||
!(extent->compressed || zeroed),
|
||||
&cluster_offset,
|
||||
index_in_cluster, index_in_cluster + n);
|
||||
ret = get_cluster_offset(
|
||||
bs,
|
||||
extent,
|
||||
&m_data,
|
||||
sector_num << 9, !extent->compressed,
|
||||
&cluster_offset);
|
||||
if (extent->compressed) {
|
||||
if (ret == VMDK_OK) {
|
||||
/* Refuse write to allocated cluster for streamOptimized */
|
||||
@@ -1492,13 +1419,24 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return -EIO;
|
||||
} else {
|
||||
/* allocate */
|
||||
ret = get_cluster_offset(bs, extent, &m_data, sector_num << 9,
|
||||
true, &cluster_offset, 0, 0);
|
||||
ret = get_cluster_offset(
|
||||
bs,
|
||||
extent,
|
||||
&m_data,
|
||||
sector_num << 9, 1,
|
||||
&cluster_offset);
|
||||
}
|
||||
}
|
||||
if (ret == VMDK_ERROR) {
|
||||
return -EINVAL;
|
||||
}
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
}
|
||||
if (zeroed) {
|
||||
/* Do zeroed write, buf is ignored */
|
||||
if (extent->has_zero_grain &&
|
||||
@@ -1506,9 +1444,9 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
n >= extent->cluster_sectors) {
|
||||
n = extent->cluster_sectors;
|
||||
if (!zero_dry_run) {
|
||||
m_data.offset = VMDK_GTE_ZEROED;
|
||||
/* update L2 tables */
|
||||
if (vmdk_L2update(extent, &m_data, VMDK_GTE_ZEROED)
|
||||
!= VMDK_OK) {
|
||||
if (vmdk_L2update(extent, &m_data) != VMDK_OK) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
@@ -1524,9 +1462,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
if (m_data.valid) {
|
||||
/* update L2 tables */
|
||||
if (vmdk_L2update(extent, &m_data,
|
||||
cluster_offset >> BDRV_SECTOR_BITS)
|
||||
!= VMDK_OK) {
|
||||
if (vmdk_L2update(extent, &m_data) != VMDK_OK) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
@@ -1559,19 +1495,6 @@ static coroutine_fn int vmdk_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_write_compressed(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
const uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
if (s->num_extents == 1 && s->extents[0].compressed) {
|
||||
return vmdk_write(bs, sector_num, buf, nb_sectors, false, false);
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
@@ -1592,17 +1515,17 @@ static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs,
|
||||
|
||||
static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
bool flat, bool compress, bool zeroed_grain,
|
||||
QemuOpts *opts, Error **errp)
|
||||
Error **errp)
|
||||
{
|
||||
int ret, i;
|
||||
BlockDriverState *bs = NULL;
|
||||
VMDK4Header header;
|
||||
Error *local_err = NULL;
|
||||
Error *local_err;
|
||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||
uint32_t *gd_buf = NULL;
|
||||
int gd_buf_size;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
@@ -1758,16 +1681,17 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
|
||||
return VMDK_OK;
|
||||
}
|
||||
|
||||
static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int idx = 0;
|
||||
BlockDriverState *new_bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
Error *local_err;
|
||||
char *desc = NULL;
|
||||
int64_t total_size = 0, filesize;
|
||||
char *adapter_type = NULL;
|
||||
char *backing_file = NULL;
|
||||
char *fmt = NULL;
|
||||
const char *adapter_type = NULL;
|
||||
const char *backing_file = NULL;
|
||||
const char *fmt = NULL;
|
||||
int flags = 0;
|
||||
int ret = 0;
|
||||
bool flat, split, compress;
|
||||
@@ -1783,8 +1707,8 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
const char desc_template[] =
|
||||
"# Disk DescriptorFile\n"
|
||||
"version=1\n"
|
||||
"CID=%" PRIx32 "\n"
|
||||
"parentCID=%" PRIx32 "\n"
|
||||
"CID=%x\n"
|
||||
"parentCID=%x\n"
|
||||
"createType=\"%s\"\n"
|
||||
"%s"
|
||||
"\n"
|
||||
@@ -1796,7 +1720,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
"\n"
|
||||
"ddb.virtualHWVersion = \"%d\"\n"
|
||||
"ddb.geometry.cylinders = \"%" PRId64 "\"\n"
|
||||
"ddb.geometry.heads = \"%" PRIu32 "\"\n"
|
||||
"ddb.geometry.heads = \"%d\"\n"
|
||||
"ddb.geometry.sectors = \"63\"\n"
|
||||
"ddb.adapterType = \"%s\"\n";
|
||||
|
||||
@@ -1807,20 +1731,24 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) {
|
||||
flags |= BLOCK_FLAG_COMPAT6;
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_ADAPTER_TYPE)) {
|
||||
adapter_type = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
||||
backing_file = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
|
||||
flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
|
||||
fmt = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_ZEROED_GRAIN)) {
|
||||
zeroed_grain |= options->value.n;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false)) {
|
||||
zeroed_grain = true;
|
||||
}
|
||||
|
||||
if (!adapter_type) {
|
||||
adapter_type = g_strdup("ide");
|
||||
adapter_type = "ide";
|
||||
} else if (strcmp(adapter_type, "ide") &&
|
||||
strcmp(adapter_type, "buslogic") &&
|
||||
strcmp(adapter_type, "lsilogic") &&
|
||||
@@ -1836,7 +1764,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
if (!fmt) {
|
||||
/* Default format to monolithicSparse */
|
||||
fmt = g_strdup("monolithicSparse");
|
||||
fmt = "monolithicSparse";
|
||||
} else if (strcmp(fmt, "monolithicFlat") &&
|
||||
strcmp(fmt, "monolithicSparse") &&
|
||||
strcmp(fmt, "twoGbMaxExtentSparse") &&
|
||||
@@ -1852,9 +1780,9 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
strcmp(fmt, "twoGbMaxExtentFlat"));
|
||||
compress = !strcmp(fmt, "streamOptimized");
|
||||
if (flat) {
|
||||
desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n";
|
||||
desc_extent_line = "RW %lld FLAT \"%s\" 0\n";
|
||||
} else {
|
||||
desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n";
|
||||
desc_extent_line = "RW %lld SPARSE \"%s\"\n";
|
||||
}
|
||||
if (flat && backing_file) {
|
||||
error_setg(errp, "Flat image can't have backing file");
|
||||
@@ -1909,7 +1837,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
path, desc_filename);
|
||||
|
||||
if (vmdk_create_extent(ext_filename, size,
|
||||
flat, compress, zeroed_grain, opts, errp)) {
|
||||
flat, compress, zeroed_grain, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
@@ -1922,7 +1850,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
/* generate descriptor file */
|
||||
desc = g_strdup_printf(desc_template,
|
||||
(uint32_t)time(NULL),
|
||||
(unsigned int)time(NULL),
|
||||
parent_cid,
|
||||
fmt,
|
||||
parent_desc_line,
|
||||
@@ -1937,9 +1865,9 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
if (!split && !flat) {
|
||||
desc_offset = 0x200;
|
||||
} else {
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
error_setg_errno(errp, -ret, "Could not create image file");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
@@ -1947,7 +1875,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
error_setg_errno(errp, -ret, "Could not write description");
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
||||
@@ -1967,9 +1895,6 @@ exit:
|
||||
if (new_bs) {
|
||||
bdrv_unref(new_bs);
|
||||
}
|
||||
g_free(adapter_type);
|
||||
g_free(backing_file);
|
||||
g_free(fmt);
|
||||
g_free(desc);
|
||||
g_string_free(ext_desc_lines, true);
|
||||
return ret;
|
||||
@@ -2065,7 +1990,7 @@ static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent = NULL;
|
||||
int64_t sector_num = 0;
|
||||
int64_t total_sectors = bdrv_nb_sectors(bs);
|
||||
int64_t total_sectors = bdrv_getlength(bs) / BDRV_SECTOR_SIZE;
|
||||
int ret;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
@@ -2086,7 +2011,7 @@ static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
}
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num << BDRV_SECTOR_BITS,
|
||||
false, &cluster_offset, 0, 0);
|
||||
0, &cluster_offset);
|
||||
if (ret == VMDK_ERROR) {
|
||||
fprintf(stderr,
|
||||
"ERROR: could not get cluster_offset for sector %"
|
||||
@@ -2137,94 +2062,41 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
||||
return spec_info;
|
||||
}
|
||||
|
||||
static bool vmdk_extents_type_eq(const VmdkExtent *a, const VmdkExtent *b)
|
||||
{
|
||||
return a->flat == b->flat &&
|
||||
a->compressed == b->compressed &&
|
||||
(a->flat || a->cluster_sectors == b->cluster_sectors);
|
||||
}
|
||||
|
||||
static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
int i;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
assert(s->num_extents);
|
||||
|
||||
/* See if we have multiple extents but they have different cases */
|
||||
for (i = 1; i < s->num_extents; i++) {
|
||||
if (!vmdk_extents_type_eq(&s->extents[0], &s->extents[i])) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
bdi->needs_compressed_writes = s->extents[0].compressed;
|
||||
if (!s->extents[0].flat) {
|
||||
bdi->cluster_size = s->extents[0].cluster_sectors << BDRV_SECTOR_BITS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmdk_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
bdrv_detach_aio_context(s->extents[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
static void vmdk_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
bdrv_attach_aio_context(s->extents[i].file, new_context);
|
||||
}
|
||||
}
|
||||
|
||||
static QemuOptsList vmdk_create_opts = {
|
||||
.name = "vmdk-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vmdk_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ADAPTER_TYPE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Virtual adapter type, can be one of "
|
||||
"ide (default), lsilogic, buslogic or legacyESX"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_COMPAT6,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "VMDK version 6 image",
|
||||
.def_value_str = "off"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help =
|
||||
"VMDK flat extent format, can be one of "
|
||||
"{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat | streamOptimized} "
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ZEROED_GRAIN,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Enable efficient zero writes "
|
||||
"using the zeroed-grain GTE feature"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter vmdk_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ADAPTER_TYPE,
|
||||
.type = OPT_STRING,
|
||||
.help = "Virtual adapter type, can be one of "
|
||||
"ide (default), lsilogic, buslogic or legacyESX"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_COMPAT6,
|
||||
.type = OPT_FLAG,
|
||||
.help = "VMDK version 6 image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = OPT_STRING,
|
||||
.help =
|
||||
"VMDK flat extent format, can be one of "
|
||||
"{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat | streamOptimized} "
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ZEROED_GRAIN,
|
||||
.type = OPT_FLAG,
|
||||
.help = "Enable efficient zero writes using the zeroed-grain GTE feature"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_vmdk = {
|
||||
@@ -2236,7 +2108,6 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_reopen_prepare = vmdk_reopen_prepare,
|
||||
.bdrv_read = vmdk_co_read,
|
||||
.bdrv_write = vmdk_co_write,
|
||||
.bdrv_write_compressed = vmdk_write_compressed,
|
||||
.bdrv_co_write_zeroes = vmdk_co_write_zeroes,
|
||||
.bdrv_close = vmdk_close,
|
||||
.bdrv_create = vmdk_create,
|
||||
@@ -2246,12 +2117,8 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_has_zero_init = vmdk_has_zero_init,
|
||||
.bdrv_get_specific_info = vmdk_get_specific_info,
|
||||
.bdrv_refresh_limits = vmdk_refresh_limits,
|
||||
.bdrv_get_info = vmdk_get_info,
|
||||
.bdrv_detach_aio_context = vmdk_detach_aio_context,
|
||||
.bdrv_attach_aio_context = vmdk_attach_aio_context,
|
||||
|
||||
.supports_backing = true,
|
||||
.create_opts = &vmdk_create_opts,
|
||||
.create_options = vmdk_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_vmdk_init(void)
|
||||
|
||||
+97
-113
@@ -207,7 +207,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
"incorrect.\n", bs->filename);
|
||||
|
||||
/* Write 'checksum' back to footer, or else will leave it with zero. */
|
||||
footer->checksum = cpu_to_be32(checksum);
|
||||
footer->checksum = be32_to_cpu(checksum);
|
||||
|
||||
// The visible size of a image in Virtual PC depends on the geometry
|
||||
// rather than on the size stored in the footer (the size in the footer
|
||||
@@ -269,11 +269,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->pagetable = qemu_try_blockalign(bs->file, s->max_table_entries * 4);
|
||||
if (s->pagetable == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4);
|
||||
|
||||
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||
|
||||
@@ -320,7 +316,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Disable migration when VHD images are used */
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"vpc", bdrv_get_device_name(bs), "live migration");
|
||||
"vpc", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
@@ -472,7 +468,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
|
||||
|
||||
// Write BAT entry to disk
|
||||
bat_offset = s->bat_offset + (4 * index);
|
||||
bat_value = cpu_to_be32(s->pagetable[index]);
|
||||
bat_value = be32_to_cpu(s->pagetable[index]);
|
||||
ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
@@ -489,7 +485,7 @@ static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
BDRVVPCState *s = (BDRVVPCState *)bs->opaque;
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) != VHD_FIXED) {
|
||||
if (cpu_to_be32(footer->type) != VHD_FIXED) {
|
||||
bdi->cluster_size = s->block_size;
|
||||
}
|
||||
|
||||
@@ -506,7 +502,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
int64_t sectors, sectors_per_block;
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
if (cpu_to_be32(footer->type) == VHD_FIXED) {
|
||||
return bdrv_read(bs->file, sector_num, buf, nb_sectors);
|
||||
}
|
||||
while (nb_sectors > 0) {
|
||||
@@ -555,7 +551,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
|
||||
int ret;
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
if (cpu_to_be32(footer->type) == VHD_FIXED) {
|
||||
return bdrv_write(bs->file, sector_num, buf, nb_sectors);
|
||||
}
|
||||
while (nb_sectors > 0) {
|
||||
@@ -653,41 +649,39 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t total_sectors)
|
||||
static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
|
||||
{
|
||||
VHDDynDiskHeader *dyndisk_header =
|
||||
(VHDDynDiskHeader *) buf;
|
||||
size_t block_size, num_bat_entries;
|
||||
int i;
|
||||
int ret;
|
||||
int64_t offset = 0;
|
||||
int ret = -EIO;
|
||||
|
||||
// Write the footer (twice: at the beginning and at the end)
|
||||
block_size = 0x200000;
|
||||
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
|
||||
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
||||
if (ret) {
|
||||
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Write the initial BAT
|
||||
offset = 3 * 512;
|
||||
if (lseek(fd, 3 * 512, SEEK_SET) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(buf, 0xFF, 512);
|
||||
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, 512);
|
||||
if (ret < 0) {
|
||||
if (write(fd, buf, 512) != 512) {
|
||||
goto fail;
|
||||
}
|
||||
offset += 512;
|
||||
}
|
||||
|
||||
// Prepare the Dynamic Disk Header
|
||||
@@ -699,53 +693,58 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
* Note: The spec is actually wrong here for data_offset, it says
|
||||
* 0xFFFFFFFF, but MS tools expect all 64 bits to be set.
|
||||
*/
|
||||
dyndisk_header->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL);
|
||||
dyndisk_header->table_offset = cpu_to_be64(3 * 512);
|
||||
dyndisk_header->version = cpu_to_be32(0x00010000);
|
||||
dyndisk_header->block_size = cpu_to_be32(block_size);
|
||||
dyndisk_header->max_table_entries = cpu_to_be32(num_bat_entries);
|
||||
dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
|
||||
dyndisk_header->table_offset = be64_to_cpu(3 * 512);
|
||||
dyndisk_header->version = be32_to_cpu(0x00010000);
|
||||
dyndisk_header->block_size = be32_to_cpu(block_size);
|
||||
dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries);
|
||||
|
||||
dyndisk_header->checksum = cpu_to_be32(vpc_checksum(buf, 1024));
|
||||
dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
|
||||
|
||||
// Write the header
|
||||
offset = 512;
|
||||
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, 1024);
|
||||
if (ret < 0) {
|
||||
if (lseek(fd, 512, SEEK_SET) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (write(fd, buf, 1024) != 1024) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t total_size)
|
||||
static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
|
||||
{
|
||||
int ret;
|
||||
int ret = -EIO;
|
||||
|
||||
/* Add footer to total size */
|
||||
total_size += HEADER_SIZE;
|
||||
|
||||
ret = bdrv_truncate(bs, total_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
total_size += 512;
|
||||
if (ftruncate(fd, total_size) != 0) {
|
||||
ret = -errno;
|
||||
goto fail;
|
||||
}
|
||||
if (lseek(fd, -512, SEEK_END) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int vpc_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
VHDFooter *footer = (VHDFooter *) buf;
|
||||
char *disk_type_param;
|
||||
int i;
|
||||
QEMUOptionParameter *disk_type_param;
|
||||
int fd, i;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
@@ -753,36 +752,27 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
if (disk_type_param) {
|
||||
if (!strcmp(disk_type_param, "dynamic")) {
|
||||
total_size = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n;
|
||||
|
||||
disk_type_param = get_option_parameter(options, BLOCK_OPT_SUBFMT);
|
||||
if (disk_type_param && disk_type_param->value.s) {
|
||||
if (!strcmp(disk_type_param->value.s, "dynamic")) {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
} else if (!strcmp(disk_type_param, "fixed")) {
|
||||
} else if (!strcmp(disk_type_param->value.s, "fixed")) {
|
||||
disk_type = VHD_FIXED;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
/* Create the file */
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -796,12 +786,11 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
&secs_per_cyl))
|
||||
{
|
||||
ret = -EFBIG;
|
||||
goto out;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||
total_size = total_sectors * BDRV_SECTOR_SIZE;
|
||||
|
||||
/* Prepare the Hard Disk Footer */
|
||||
memset(buf, 0, 1024);
|
||||
@@ -811,41 +800,45 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
memcpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = cpu_to_be32(0x02);
|
||||
footer->version = cpu_to_be32(0x00010000);
|
||||
footer->features = be32_to_cpu(0x02);
|
||||
footer->version = be32_to_cpu(0x00010000);
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
footer->data_offset = cpu_to_be64(HEADER_SIZE);
|
||||
footer->data_offset = be64_to_cpu(HEADER_SIZE);
|
||||
} else {
|
||||
footer->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL);
|
||||
footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
|
||||
}
|
||||
footer->timestamp = cpu_to_be32(time(NULL) - VHD_TIMESTAMP_BASE);
|
||||
footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
|
||||
|
||||
/* Version of Virtual PC 2007 */
|
||||
footer->major = cpu_to_be16(0x0005);
|
||||
footer->minor = cpu_to_be16(0x0003);
|
||||
footer->orig_size = cpu_to_be64(total_size);
|
||||
footer->size = cpu_to_be64(total_size);
|
||||
footer->cyls = cpu_to_be16(cyls);
|
||||
footer->major = be16_to_cpu(0x0005);
|
||||
footer->minor = be16_to_cpu(0x0003);
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
footer->orig_size = be64_to_cpu(total_sectors * 512);
|
||||
footer->size = be64_to_cpu(total_sectors * 512);
|
||||
} else {
|
||||
footer->orig_size = be64_to_cpu(total_size);
|
||||
footer->size = be64_to_cpu(total_size);
|
||||
}
|
||||
footer->cyls = be16_to_cpu(cyls);
|
||||
footer->heads = heads;
|
||||
footer->secs_per_cyl = secs_per_cyl;
|
||||
|
||||
footer->type = cpu_to_be32(disk_type);
|
||||
footer->type = be32_to_cpu(disk_type);
|
||||
|
||||
#if defined(CONFIG_UUID)
|
||||
uuid_generate(footer->uuid);
|
||||
#endif
|
||||
|
||||
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
|
||||
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
ret = create_dynamic_disk(bs, buf, total_sectors);
|
||||
ret = create_dynamic_disk(fd, buf, total_sectors);
|
||||
} else {
|
||||
ret = create_fixed_disk(bs, buf, total_size);
|
||||
ret = create_fixed_disk(fd, buf, total_size);
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_unref(bs);
|
||||
g_free(disk_type_param);
|
||||
fail:
|
||||
qemu_close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -854,7 +847,7 @@ static int vpc_has_zero_init(BlockDriverState *bs)
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
if (cpu_to_be32(footer->type) == VHD_FIXED) {
|
||||
return bdrv_has_zero_init(bs->file);
|
||||
} else {
|
||||
return 1;
|
||||
@@ -873,29 +866,20 @@ static void vpc_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static QemuOptsList vpc_create_opts = {
|
||||
.name = "vpc-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vpc_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help =
|
||||
"Type of virtual hard disk format. Supported formats are "
|
||||
"{dynamic (default) | fixed} "
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_NOCOW,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Turn off copy-on-write (valid only on btrfs)"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter vpc_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = OPT_STRING,
|
||||
.help =
|
||||
"Type of virtual hard disk format. Supported formats are "
|
||||
"{dynamic (default) | fixed} "
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_vpc = {
|
||||
@@ -913,7 +897,7 @@ static BlockDriver bdrv_vpc = {
|
||||
|
||||
.bdrv_get_info = vpc_get_info,
|
||||
|
||||
.create_opts = &vpc_create_opts,
|
||||
.create_options = vpc_create_options,
|
||||
.bdrv_has_zero_init = vpc_has_zero_init,
|
||||
};
|
||||
|
||||
|
||||
+39
-35
@@ -52,6 +52,10 @@
|
||||
|
||||
#define DLOG(a) a
|
||||
|
||||
#undef stderr
|
||||
#define stderr STDERR
|
||||
FILE* stderr = NULL;
|
||||
|
||||
static void checkpoint(void);
|
||||
|
||||
#ifdef __MINGW32__
|
||||
@@ -728,7 +732,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||
if(first_cluster == 0 && (is_dotdot || is_dot))
|
||||
continue;
|
||||
|
||||
buffer = g_malloc(length);
|
||||
buffer=(char*)g_malloc(length);
|
||||
snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
|
||||
|
||||
if(stat(buffer,&st)<0) {
|
||||
@@ -763,7 +767,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||
|
||||
/* create mapping for this file */
|
||||
if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
|
||||
s->current_mapping = array_get_next(&(s->mapping));
|
||||
s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
|
||||
s->current_mapping->begin=0;
|
||||
s->current_mapping->end=st.st_size;
|
||||
/*
|
||||
@@ -807,12 +811,12 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||
}
|
||||
|
||||
/* reget the mapping, since s->mapping was possibly realloc()ed */
|
||||
mapping = array_get(&(s->mapping), mapping_index);
|
||||
mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
|
||||
first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
|
||||
* 0x20 / s->cluster_size;
|
||||
mapping->end = first_cluster;
|
||||
|
||||
direntry = array_get(&(s->directory), mapping->dir_index);
|
||||
direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
|
||||
set_begin_of_direntry(direntry, mapping->begin);
|
||||
|
||||
return 0;
|
||||
@@ -829,8 +833,7 @@ static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
|
||||
}
|
||||
|
||||
static int init_directories(BDRVVVFATState* s,
|
||||
const char *dirname, int heads, int secs,
|
||||
Error **errp)
|
||||
const char *dirname, int heads, int secs)
|
||||
{
|
||||
bootsector_t* bootsector;
|
||||
mapping_t* mapping;
|
||||
@@ -891,8 +894,8 @@ static int init_directories(BDRVVVFATState* s,
|
||||
if (mapping->mode & MODE_DIRECTORY) {
|
||||
mapping->begin = cluster;
|
||||
if(read_directory(s, i)) {
|
||||
error_setg(errp, "Could not read directory %s",
|
||||
mapping->path);
|
||||
fprintf(stderr, "Could not read directory %s\n",
|
||||
mapping->path);
|
||||
return -1;
|
||||
}
|
||||
mapping = array_get(&(s->mapping), i);
|
||||
@@ -918,10 +921,9 @@ static int init_directories(BDRVVVFATState* s,
|
||||
cluster = mapping->end;
|
||||
|
||||
if(cluster > s->cluster_count) {
|
||||
error_setg(errp,
|
||||
"Directory does not fit in FAT%d (capacity %.2f MB)",
|
||||
s->fat_type, s->sector_count / 2000.0);
|
||||
return -1;
|
||||
fprintf(stderr,"Directory does not fit in FAT%d (capacity %.2f MB)\n",
|
||||
s->fat_type, s->sector_count / 2000.0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* fix fat for entry */
|
||||
@@ -979,7 +981,7 @@ static int init_directories(BDRVVVFATState* s,
|
||||
static BDRVVVFATState *vvv = NULL;
|
||||
#endif
|
||||
|
||||
static int enable_write_target(BDRVVVFATState *s, Error **errp);
|
||||
static int enable_write_target(BDRVVVFATState *s);
|
||||
static int is_consistent(BDRVVVFATState *s);
|
||||
|
||||
static void vvfat_rebind(BlockDriverState *bs)
|
||||
@@ -1078,6 +1080,11 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
vvv = s;
|
||||
#endif
|
||||
|
||||
DLOG(if (stderr == NULL) {
|
||||
stderr = fopen("vvfat.log", "a");
|
||||
setbuf(stderr, NULL);
|
||||
})
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
@@ -1155,7 +1162,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
|
||||
|
||||
if (qemu_opt_get_bool(opts, "rw", false)) {
|
||||
ret = enable_write_target(s, errp);
|
||||
ret = enable_write_target(s);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1164,7 +1171,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
bs->total_sectors = cyls * heads * secs;
|
||||
|
||||
if (init_directories(s, dirname, heads, secs, errp)) {
|
||||
if (init_directories(s, dirname, heads, secs)) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1182,7 +1189,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (s->qcow) {
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"vvfat (rw)", bdrv_get_device_name(bs), "live migration");
|
||||
"vvfat (rw)", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
}
|
||||
|
||||
@@ -2899,10 +2906,11 @@ static BlockDriver vvfat_write_target = {
|
||||
.bdrv_close = write_target_close,
|
||||
};
|
||||
|
||||
static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
static int enable_write_target(BDRVVVFATState *s)
|
||||
{
|
||||
BlockDriver *bdrv_qcow = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
BlockDriver *bdrv_qcow;
|
||||
QEMUOptionParameter *options;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
int size = sector2cluster(s, s->sector_count);
|
||||
s->used_clusters = calloc(size, 1);
|
||||
@@ -2912,32 +2920,28 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
s->qcow_filename = g_malloc(1024);
|
||||
ret = get_tmp_filename(s->qcow_filename, 1024);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "can't create temporary file");
|
||||
goto err;
|
||||
}
|
||||
|
||||
bdrv_qcow = bdrv_find_format("qcow");
|
||||
if (!bdrv_qcow) {
|
||||
error_setg(errp, "Failed to locate qcow driver");
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
|
||||
set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
|
||||
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
|
||||
|
||||
opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512);
|
||||
qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:");
|
||||
|
||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
|
||||
qemu_opts_del(opts);
|
||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
s->qcow = NULL;
|
||||
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
bdrv_qcow, errp);
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -2945,9 +2949,9 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
unlink(s->qcow_filename);
|
||||
#endif
|
||||
|
||||
bdrv_set_backing_hd(s->bs, bdrv_new());
|
||||
s->bs->backing_hd = bdrv_new("");
|
||||
s->bs->backing_hd->drv = &vvfat_write_target;
|
||||
s->bs->backing_hd->opaque = g_new(void *, 1);
|
||||
s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
|
||||
*(void**)s->bs->backing_hd->opaque = s;
|
||||
|
||||
return 0;
|
||||
|
||||
+22
-33
@@ -40,11 +40,10 @@ struct QEMUWin32AIOState {
|
||||
HANDLE hIOCP;
|
||||
EventNotifier e;
|
||||
int count;
|
||||
bool is_aio_context_attached;
|
||||
};
|
||||
|
||||
typedef struct QEMUWin32AIOCB {
|
||||
BlockAIOCB common;
|
||||
BlockDriverAIOCB common;
|
||||
struct QEMUWin32AIOState *ctx;
|
||||
int nbytes;
|
||||
OVERLAPPED ov;
|
||||
@@ -88,7 +87,7 @@ static void win32_aio_process_completion(QEMUWin32AIOState *s,
|
||||
|
||||
|
||||
waiocb->common.cb(waiocb->common.opaque, ret);
|
||||
qemu_aio_unref(waiocb);
|
||||
qemu_aio_release(waiocb);
|
||||
}
|
||||
|
||||
static void win32_aio_completion_cb(EventNotifier *e)
|
||||
@@ -106,14 +105,28 @@ static void win32_aio_completion_cb(EventNotifier *e)
|
||||
}
|
||||
}
|
||||
|
||||
static void win32_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QEMUWin32AIOCB *waiocb = (QEMUWin32AIOCB *)blockacb;
|
||||
|
||||
/*
|
||||
* CancelIoEx is only supported in Vista and newer. For now, just
|
||||
* wait for completion.
|
||||
*/
|
||||
while (!HasOverlappedIoCompleted(&waiocb->ov)) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo win32_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEMUWin32AIOCB),
|
||||
.cancel = win32_aio_cancel,
|
||||
};
|
||||
|
||||
BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
QEMUWin32AIOState *aio, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
struct QEMUWin32AIOCB *waiocb;
|
||||
uint64_t offset = sector_num * 512;
|
||||
@@ -125,10 +138,7 @@ BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
waiocb->is_read = (type == QEMU_AIO_READ);
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
waiocb->buf = qemu_try_blockalign(bs, qiov->size);
|
||||
if (waiocb->buf == NULL) {
|
||||
goto out;
|
||||
}
|
||||
waiocb->buf = qemu_blockalign(bs, qiov->size);
|
||||
if (type & QEMU_AIO_WRITE) {
|
||||
iov_to_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size);
|
||||
}
|
||||
@@ -157,8 +167,7 @@ BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
|
||||
out_dec_count:
|
||||
aio->count--;
|
||||
out:
|
||||
qemu_aio_unref(waiocb);
|
||||
qemu_aio_release(waiocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -171,20 +180,6 @@ int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile)
|
||||
}
|
||||
}
|
||||
|
||||
void win32_aio_detach_aio_context(QEMUWin32AIOState *aio,
|
||||
AioContext *old_context)
|
||||
{
|
||||
aio_set_event_notifier(old_context, &aio->e, NULL);
|
||||
aio->is_aio_context_attached = false;
|
||||
}
|
||||
|
||||
void win32_aio_attach_aio_context(QEMUWin32AIOState *aio,
|
||||
AioContext *new_context)
|
||||
{
|
||||
aio->is_aio_context_attached = true;
|
||||
aio_set_event_notifier(new_context, &aio->e, win32_aio_completion_cb);
|
||||
}
|
||||
|
||||
QEMUWin32AIOState *win32_aio_init(void)
|
||||
{
|
||||
QEMUWin32AIOState *s;
|
||||
@@ -199,6 +194,8 @@ QEMUWin32AIOState *win32_aio_init(void)
|
||||
goto out_close_efd;
|
||||
}
|
||||
|
||||
qemu_aio_set_event_notifier(&s->e, win32_aio_completion_cb);
|
||||
|
||||
return s;
|
||||
|
||||
out_close_efd:
|
||||
@@ -207,11 +204,3 @@ out_free_state:
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void win32_aio_cleanup(QEMUWin32AIOState *aio)
|
||||
{
|
||||
assert(!aio->is_aio_context_attached);
|
||||
CloseHandle(aio->hIOCP);
|
||||
event_notifier_cleanup(&aio->e);
|
||||
g_free(aio);
|
||||
}
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||
|
||||
nbd_export_set_name(exp, device);
|
||||
|
||||
n = g_new0(NBDCloseNotifier, 1);
|
||||
n = g_malloc0(sizeof(NBDCloseNotifier));
|
||||
n->n.notify = nbd_close_notifier;
|
||||
n->exp = exp;
|
||||
bdrv_add_close_notifier(bs, &n->n);
|
||||
|
||||
+230
-570
File diff suppressed because it is too large
Load Diff
+41
-133
@@ -26,6 +26,7 @@
|
||||
#include "config-host.h"
|
||||
#include "qemu-common.h"
|
||||
#include "trace.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "block/block.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
@@ -33,25 +34,21 @@
|
||||
#include "block/coroutine.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockCompletionFunc *cb,
|
||||
int64_t speed, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
BlockJob *job;
|
||||
|
||||
if (bs->job) {
|
||||
if (bs->job || bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
||||
return NULL;
|
||||
}
|
||||
bdrv_ref(bs);
|
||||
job = g_malloc0(driver->instance_size);
|
||||
error_setg(&job->blocker, "block device is in use by block job: %s",
|
||||
BlockJobType_lookup[driver->job_type]);
|
||||
bdrv_op_block_all(bs, job->blocker);
|
||||
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
|
||||
bdrv_set_in_use(bs, 1);
|
||||
|
||||
job = g_malloc0(driver->instance_size);
|
||||
job->driver = driver;
|
||||
job->bs = bs;
|
||||
job->cb = cb;
|
||||
@@ -66,9 +63,8 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
bs->job = NULL;
|
||||
bdrv_op_unblock_all(bs, job->blocker);
|
||||
error_free(job->blocker);
|
||||
g_free(job);
|
||||
bdrv_set_in_use(bs, 0);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
@@ -83,9 +79,8 @@ void block_job_completed(BlockJob *job, int ret)
|
||||
assert(bs->job == job);
|
||||
job->cb(job->opaque, ret);
|
||||
bs->job = NULL;
|
||||
bdrv_op_unblock_all(bs, job->blocker);
|
||||
error_free(job->blocker);
|
||||
g_free(job);
|
||||
bdrv_set_in_use(bs, 0);
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -93,7 +88,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!job->driver->set_speed) {
|
||||
error_set(errp, QERR_UNSUPPORTED);
|
||||
error_set(errp, QERR_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
job->driver->set_speed(job, speed, &local_err);
|
||||
@@ -108,8 +103,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
void block_job_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
if (job->paused || job->cancelled || !job->driver->complete) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY,
|
||||
bdrv_get_device_name(job->bs));
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,30 +148,27 @@ void block_job_iostatus_reset(BlockJob *job)
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockFinishData {
|
||||
struct BlockCancelData {
|
||||
BlockJob *job;
|
||||
BlockCompletionFunc *cb;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
bool cancelled;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void block_job_finish_cb(void *opaque, int ret)
|
||||
static void block_job_cancel_cb(void *opaque, int ret)
|
||||
{
|
||||
struct BlockFinishData *data = opaque;
|
||||
struct BlockCancelData *data = opaque;
|
||||
|
||||
data->cancelled = block_job_is_cancelled(data->job);
|
||||
data->ret = ret;
|
||||
data->cb(data->opaque, ret);
|
||||
}
|
||||
|
||||
static int block_job_finish_sync(BlockJob *job,
|
||||
void (*finish)(BlockJob *, Error **errp),
|
||||
Error **errp)
|
||||
int block_job_cancel_sync(BlockJob *job)
|
||||
{
|
||||
struct BlockFinishData data;
|
||||
struct BlockCancelData data;
|
||||
BlockDriverState *bs = job->bs;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(bs->job == job);
|
||||
|
||||
@@ -188,37 +179,15 @@ static int block_job_finish_sync(BlockJob *job,
|
||||
data.cb = job->cb;
|
||||
data.opaque = job->opaque;
|
||||
data.ret = -EINPROGRESS;
|
||||
job->cb = block_job_finish_cb;
|
||||
job->cb = block_job_cancel_cb;
|
||||
job->opaque = &data;
|
||||
finish(job, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EBUSY;
|
||||
}
|
||||
block_job_cancel(job);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
|
||||
}
|
||||
|
||||
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
|
||||
* used with block_job_finish_sync() without the need for (rather nasty)
|
||||
* function pointer casts there. */
|
||||
static void block_job_cancel_err(BlockJob *job, Error **errp)
|
||||
{
|
||||
block_job_cancel(job);
|
||||
}
|
||||
|
||||
int block_job_cancel_sync(BlockJob *job)
|
||||
{
|
||||
return block_job_finish_sync(job, &block_job_cancel_err, NULL);
|
||||
}
|
||||
|
||||
int block_job_complete_sync(BlockJob *job, Error **errp)
|
||||
{
|
||||
return block_job_finish_sync(job, &block_job_complete, errp);
|
||||
}
|
||||
|
||||
void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
||||
{
|
||||
assert(job->busy);
|
||||
@@ -232,7 +201,7 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
||||
if (block_job_is_paused(job)) {
|
||||
qemu_coroutine_yield();
|
||||
} else {
|
||||
co_aio_sleep_ns(bdrv_get_aio_context(job->bs), type, ns);
|
||||
co_sleep_ns(type, ns);
|
||||
}
|
||||
job->busy = true;
|
||||
}
|
||||
@@ -262,7 +231,6 @@ BlockJobInfo *block_job_query(BlockJob *job)
|
||||
info->offset = job->offset;
|
||||
info->speed = job->speed;
|
||||
info->io_status = job->iostatus;
|
||||
info->ready = job->ready;
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -274,37 +242,26 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_event_cancelled(BlockJob *job)
|
||||
|
||||
QObject *qobject_from_block_job(BlockJob *job)
|
||||
{
|
||||
qapi_event_send_block_job_cancelled(job->driver->job_type,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed,
|
||||
&error_abort);
|
||||
return qobject_from_jsonf("{ 'type': %s,"
|
||||
"'device': %s,"
|
||||
"'len': %" PRId64 ","
|
||||
"'offset': %" PRId64 ","
|
||||
"'speed': %" PRId64 " }",
|
||||
BlockJobType_lookup[job->driver->job_type],
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed);
|
||||
}
|
||||
|
||||
void block_job_event_completed(BlockJob *job, const char *msg)
|
||||
void block_job_ready(BlockJob *job)
|
||||
{
|
||||
qapi_event_send_block_job_completed(job->driver->job_type,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed,
|
||||
!!msg,
|
||||
msg,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
void block_job_event_ready(BlockJob *job)
|
||||
{
|
||||
job->ready = true;
|
||||
|
||||
qapi_event_send_block_job_ready(job->driver->job_type,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed, &error_abort);
|
||||
QObject *data = qobject_from_block_job(job);
|
||||
monitor_protocol_event(QEVENT_BLOCK_JOB_READY, data);
|
||||
qobject_decref(data);
|
||||
}
|
||||
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
@@ -315,26 +272,22 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
|
||||
switch (on_err) {
|
||||
case BLOCKDEV_ON_ERROR_ENOSPC:
|
||||
action = (error == ENOSPC) ?
|
||||
BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT;
|
||||
action = (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT;
|
||||
break;
|
||||
case BLOCKDEV_ON_ERROR_STOP:
|
||||
action = BLOCK_ERROR_ACTION_STOP;
|
||||
action = BDRV_ACTION_STOP;
|
||||
break;
|
||||
case BLOCKDEV_ON_ERROR_REPORT:
|
||||
action = BLOCK_ERROR_ACTION_REPORT;
|
||||
action = BDRV_ACTION_REPORT;
|
||||
break;
|
||||
case BLOCKDEV_ON_ERROR_IGNORE:
|
||||
action = BLOCK_ERROR_ACTION_IGNORE;
|
||||
action = BDRV_ACTION_IGNORE;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
qapi_event_send_block_job_error(bdrv_get_device_name(job->bs),
|
||||
is_read ? IO_OPERATION_TYPE_READ :
|
||||
IO_OPERATION_TYPE_WRITE,
|
||||
action, &error_abort);
|
||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||
bdrv_emit_qmp_error_event(job->bs, QEVENT_BLOCK_JOB_ERROR, action, is_read);
|
||||
if (action == BDRV_ACTION_STOP) {
|
||||
block_job_pause(job);
|
||||
block_job_iostatus_set_err(job, error);
|
||||
if (bs != job->bs) {
|
||||
@@ -343,48 +296,3 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BlockJob *job;
|
||||
QEMUBH *bh;
|
||||
AioContext *aio_context;
|
||||
BlockJobDeferToMainLoopFn *fn;
|
||||
void *opaque;
|
||||
} BlockJobDeferToMainLoopData;
|
||||
|
||||
static void block_job_defer_to_main_loop_bh(void *opaque)
|
||||
{
|
||||
BlockJobDeferToMainLoopData *data = opaque;
|
||||
AioContext *aio_context;
|
||||
|
||||
qemu_bh_delete(data->bh);
|
||||
|
||||
/* Prevent race with block_job_defer_to_main_loop() */
|
||||
aio_context_acquire(data->aio_context);
|
||||
|
||||
/* Fetch BDS AioContext again, in case it has changed */
|
||||
aio_context = bdrv_get_aio_context(data->job->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
data->fn(data->job, data->opaque);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
|
||||
aio_context_release(data->aio_context);
|
||||
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
void block_job_defer_to_main_loop(BlockJob *job,
|
||||
BlockJobDeferToMainLoopFn *fn,
|
||||
void *opaque)
|
||||
{
|
||||
BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data));
|
||||
data->job = job;
|
||||
data->bh = qemu_bh_new(block_job_defer_to_main_loop_bh, data);
|
||||
data->aio_context = bdrv_get_aio_context(job->bs);
|
||||
data->fn = fn;
|
||||
data->opaque = opaque;
|
||||
|
||||
qemu_bh_schedule(data->bh);
|
||||
}
|
||||
|
||||
-258
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* QEMU Boot Device Implement
|
||||
*
|
||||
* Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD.
|
||||
*
|
||||
* 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 "sysemu/sysemu.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
typedef struct FWBootEntry FWBootEntry;
|
||||
|
||||
struct FWBootEntry {
|
||||
QTAILQ_ENTRY(FWBootEntry) link;
|
||||
int32_t bootindex;
|
||||
DeviceState *dev;
|
||||
char *suffix;
|
||||
};
|
||||
|
||||
static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
|
||||
QTAILQ_HEAD_INITIALIZER(fw_boot_order);
|
||||
|
||||
void check_boot_index(int32_t bootindex, Error **errp)
|
||||
{
|
||||
FWBootEntry *i;
|
||||
|
||||
if (bootindex >= 0) {
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
if (i->bootindex == bootindex) {
|
||||
error_setg(errp, "The bootindex %d has already been used",
|
||||
bootindex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void del_boot_device_path(DeviceState *dev, const char *suffix)
|
||||
{
|
||||
FWBootEntry *i;
|
||||
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
|
||||
i->dev == dev) {
|
||||
QTAILQ_REMOVE(&fw_boot_order, i, link);
|
||||
g_free(i->suffix);
|
||||
g_free(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_boot_device_path(int32_t bootindex, DeviceState *dev,
|
||||
const char *suffix)
|
||||
{
|
||||
FWBootEntry *node, *i;
|
||||
|
||||
if (bootindex < 0) {
|
||||
del_boot_device_path(dev, suffix);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(dev != NULL || suffix != NULL);
|
||||
|
||||
del_boot_device_path(dev, suffix);
|
||||
|
||||
node = g_malloc0(sizeof(FWBootEntry));
|
||||
node->bootindex = bootindex;
|
||||
node->suffix = g_strdup(suffix);
|
||||
node->dev = dev;
|
||||
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
if (i->bootindex == bootindex) {
|
||||
error_report("Two devices with same boot index %d", bootindex);
|
||||
exit(1);
|
||||
} else if (i->bootindex < bootindex) {
|
||||
continue;
|
||||
}
|
||||
QTAILQ_INSERT_BEFORE(i, node, link);
|
||||
return;
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
|
||||
}
|
||||
|
||||
DeviceState *get_boot_device(uint32_t position)
|
||||
{
|
||||
uint32_t counter = 0;
|
||||
FWBootEntry *i = NULL;
|
||||
DeviceState *res = NULL;
|
||||
|
||||
if (!QTAILQ_EMPTY(&fw_boot_order)) {
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
if (counter == position) {
|
||||
res = i->dev;
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns null terminated string that consist of new line
|
||||
* separated device paths.
|
||||
*
|
||||
* memory pointed by "size" is assigned total length of the array in bytes
|
||||
*
|
||||
*/
|
||||
char *get_boot_devices_list(size_t *size, bool ignore_suffixes)
|
||||
{
|
||||
FWBootEntry *i;
|
||||
size_t total = 0;
|
||||
char *list = NULL;
|
||||
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
char *devpath = NULL, *bootpath;
|
||||
size_t len;
|
||||
|
||||
if (i->dev) {
|
||||
devpath = qdev_get_fw_dev_path(i->dev);
|
||||
assert(devpath);
|
||||
}
|
||||
|
||||
if (i->suffix && !ignore_suffixes && devpath) {
|
||||
size_t bootpathlen = strlen(devpath) + strlen(i->suffix) + 1;
|
||||
|
||||
bootpath = g_malloc(bootpathlen);
|
||||
snprintf(bootpath, bootpathlen, "%s%s", devpath, i->suffix);
|
||||
g_free(devpath);
|
||||
} else if (devpath) {
|
||||
bootpath = devpath;
|
||||
} else if (!ignore_suffixes) {
|
||||
assert(i->suffix);
|
||||
bootpath = g_strdup(i->suffix);
|
||||
} else {
|
||||
bootpath = g_strdup("");
|
||||
}
|
||||
|
||||
if (total) {
|
||||
list[total-1] = '\n';
|
||||
}
|
||||
len = strlen(bootpath) + 1;
|
||||
list = g_realloc(list, total + len);
|
||||
memcpy(&list[total], bootpath, len);
|
||||
total += len;
|
||||
g_free(bootpath);
|
||||
}
|
||||
|
||||
*size = total;
|
||||
|
||||
if (boot_strict && *size > 0) {
|
||||
list[total-1] = '\n';
|
||||
list = g_realloc(list, total + 5);
|
||||
memcpy(&list[total], "HALT", 5);
|
||||
*size = total + 5;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int32_t *bootindex;
|
||||
const char *suffix;
|
||||
DeviceState *dev;
|
||||
} BootIndexProperty;
|
||||
|
||||
static void device_get_bootindex(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
BootIndexProperty *prop = opaque;
|
||||
visit_type_int32(v, prop->bootindex, name, 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, &boot_index, name, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
/* check whether bootindex is present in fw_boot_order list */
|
||||
check_boot_index(boot_index, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
/* change bootindex to a new one */
|
||||
*prop->bootindex = boot_index;
|
||||
|
||||
add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
|
||||
|
||||
out:
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
}
|
||||
|
||||
static void property_release_bootindex(Object *obj, const char *name,
|
||||
void *opaque)
|
||||
|
||||
{
|
||||
BootIndexProperty *prop = opaque;
|
||||
|
||||
del_boot_device_path(prop->dev, prop->suffix);
|
||||
g_free(prop);
|
||||
}
|
||||
|
||||
void device_add_bootindex_property(Object *obj, int32_t *bootindex,
|
||||
const char *name, const char *suffix,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BootIndexProperty *prop = g_malloc0(sizeof(*prop));
|
||||
|
||||
prop->bootindex = bootindex;
|
||||
prop->suffix = suffix;
|
||||
prop->dev = dev;
|
||||
|
||||
object_property_add(obj, name, "int32",
|
||||
device_get_bootindex,
|
||||
device_set_bootindex,
|
||||
property_release_bootindex,
|
||||
prop, &local_err);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
g_free(prop);
|
||||
return;
|
||||
}
|
||||
/* initialize devices' bootindex property to -1 */
|
||||
object_property_set_int(obj, -1, name, NULL);
|
||||
}
|
||||
@@ -1,37 +1,7 @@
|
||||
/*
|
||||
* FreeBSD strace list
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
{ TARGET_FREEBSD_NR___acl_aclcheck_fd, "__acl_aclcheck_fd", "%s(%d, %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_aclcheck_file, "__acl_aclcheck_file", "%s(\"%s\", %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_aclcheck_link, "__acl_aclcheck_link", "%s(\"%s\", %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_delete_fd, "__acl_delete_fd", "%s(%d, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_delete_file, "__acl_delete_file", "%s(\"%s\", %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_delete_link, "__acl_delete_link", "%s(\"%s\", %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_get_fd, "__acl_get_fd", "%s(%d, %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_get_file, "__acl_get_file", "%s(\"%s\", %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_get_link, "__acl_get_link", "%s(\"%s\", %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_set_fd, "__acl_set_fd", "%s(%d, %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_set_file, "__acl_set_file", "%s(\"%s\", %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___acl_set_link, "__acl_set_link", "%s(\"%s\", %d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___semctl, "__semctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, print_sysctl, NULL },
|
||||
{ TARGET_FREEBSD_NR__umtx_op, "_umtx_op", "%s(%#x, %d, %d, %#x, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_acct, "acct", NULL, NULL, NULL },
|
||||
@@ -50,41 +20,24 @@
|
||||
{ TARGET_FREEBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_dup, "dup", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_dup2, "dup2", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_eaccess, "eaccess", "%s(\"%s\",%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_execve, "execve", NULL, print_execve, NULL },
|
||||
{ TARGET_FREEBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattrctl, "extattrctl", "%s(\"%s\", %d, \"%s\", %d, \"%s\"", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_delete_fd, "extattr_delete_fd", "%s(%d, %d, \"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_delete_file, "extattr_delete_file", "%s(\"%s\", %d, \"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_delete_link, "extattr_delete_link", "%s(\"%s\", %d, \"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_get_fd, "extattr_get_fd", "%s(%d, %d, \"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_get_file, "extattr_get_file", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_get_file, "extattr_get_link", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_list_fd, "extattr_list_fd", "%s(%d, %d, %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_list_file, "extattr_list_file", "%s(\"%s\", %d, %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_list_link, "extattr_list_link", "%s(\"%s\", %d, %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_set_fd, "extattr_set_fd", "%s(%d, %d, \"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_set_file, "extattr_set_file", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_extattr_set_link, "extattr_set_link", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchdir, "fchdir", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchflags, "fchflags", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchown, "fchown", "%s(%d,%d,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fcntl, "fcntl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fexecve, "fexecve", NULL, print_execve, NULL },
|
||||
{ TARGET_FREEBSD_NR_fhopen, "fhopen", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fhstat, "fhstat", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_flock, "flock", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fork, "fork", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstat, "fstat", "%s(%d,%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstatat, "fstatat", "%s(%d,\"%s\", %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstatfs, "fstatfs", "%s(%d,%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstat, "fstat", "%s(%d,%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstatfs, "fstatfs", "%s(%d,%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fsync, "fsync", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_futimes, "futimes", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getcontext, "getcontext", "%s(%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_freebsd6_mmap, "freebsd6_mmap", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getegid, "getegid", "%s()", NULL, NULL },
|
||||
@@ -110,7 +63,7 @@
|
||||
{ TARGET_FREEBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getuid, "getuid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_ioctl, "ioctl", NULL, print_ioctl, NULL },
|
||||
{ TARGET_FREEBSD_NR_ioctl, "ioctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_kevent, "kevent", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_kill, "kill", NULL, NULL, NULL },
|
||||
@@ -119,7 +72,6 @@
|
||||
{ TARGET_FREEBSD_NR_lchown, "lchown", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_listen, "listen", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_lpathconf, "lpathconf", "%s(\"%s\", %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_lseek, "lseek", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_madvise, "madvise", NULL, NULL, NULL },
|
||||
@@ -144,8 +96,7 @@
|
||||
{ TARGET_FREEBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_openat, "openat", "%s(%d, \"%s\",%#x,%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pathconf, "pathconf", "%s(\"%s\", %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pathconf, "pathconf", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pipe, "pipe", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_poll, "poll", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pread, "pread", NULL, NULL, NULL },
|
||||
@@ -165,7 +116,6 @@
|
||||
{ TARGET_FREEBSD_NR_revoke, "revoke", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_rfork, "rfork", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_rmdir, "rmdir", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_rtprio_thread, "rtprio_thread", "%s(%d, %d, %p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sbrk, "sbrk", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_select, "select", NULL, NULL, NULL },
|
||||
@@ -173,7 +123,6 @@
|
||||
{ TARGET_FREEBSD_NR_semop, "semop", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sendto, "sendto", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setcontext, "setcontext", "%s(%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setegid, "setegid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_seteuid, "seteuid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setgid, "setgid", NULL, NULL, NULL },
|
||||
@@ -202,7 +151,7 @@
|
||||
{ TARGET_FREEBSD_NR_sigprocmask, "sigprocmask", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigreturn, "sigreturn", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigsuspend, "sigsuspend", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_socket, "socket", "%s(%d,%d,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_socket, "socket", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_socketpair, "socketpair", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sstk, "sstk", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL },
|
||||
@@ -211,15 +160,6 @@
|
||||
{ TARGET_FREEBSD_NR_sync, "sync", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sysarch, "sysarch", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_syscall, "syscall", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_create, "thr_create", "%s(%#x, %#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_exit, "thr_exit", "%s(%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_kill, "thr_kill", "%s(%d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_kill2, "thr_kill2", "%s(%d, %d, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_new, "thr_new", "%s(%#x, %d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_self, "thr_self", "%s(%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_set_name, "thr_set_name", "%s(%d, \"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_suspend, "thr_suspend", "%s(%d, %#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_thr_wake, "thr_wake", "%s(%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_truncate, "truncate", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL },
|
||||
|
||||
+368
-445
@@ -1,450 +1,373 @@
|
||||
/*
|
||||
* System call numbers.
|
||||
*
|
||||
* created from FreeBSD: releng/9.1/sys/kern/syscalls.master 229723
|
||||
* 2012-01-06 19:29:16Z jhb
|
||||
* $FreeBSD: src/sys/sys/syscall.h,v 1.224 2008/08/24 21:23:08 rwatson Exp $
|
||||
* created from FreeBSD: head/sys/kern/syscalls.master 182123 2008-08-24 21:20:35Z rwatson
|
||||
*/
|
||||
|
||||
#define TARGET_FREEBSD_NR_syscall 0
|
||||
#define TARGET_FREEBSD_NR_exit 1
|
||||
#define TARGET_FREEBSD_NR_fork 2
|
||||
#define TARGET_FREEBSD_NR_read 3
|
||||
#define TARGET_FREEBSD_NR_write 4
|
||||
#define TARGET_FREEBSD_NR_open 5
|
||||
#define TARGET_FREEBSD_NR_close 6
|
||||
#define TARGET_FREEBSD_NR_wait4 7
|
||||
/* 8 is old creat */
|
||||
#define TARGET_FREEBSD_NR_link 9
|
||||
#define TARGET_FREEBSD_NR_unlink 10
|
||||
/* 11 is obsolete execv */
|
||||
#define TARGET_FREEBSD_NR_chdir 12
|
||||
#define TARGET_FREEBSD_NR_fchdir 13
|
||||
#define TARGET_FREEBSD_NR_mknod 14
|
||||
#define TARGET_FREEBSD_NR_chmod 15
|
||||
#define TARGET_FREEBSD_NR_chown 16
|
||||
#define TARGET_FREEBSD_NR_break 17
|
||||
#define TARGET_FREEBSD_NR_freebsd4_getfsstat 18
|
||||
/* 19 is old lseek */
|
||||
#define TARGET_FREEBSD_NR_getpid 20
|
||||
#define TARGET_FREEBSD_NR_mount 21
|
||||
#define TARGET_FREEBSD_NR_unmount 22
|
||||
#define TARGET_FREEBSD_NR_setuid 23
|
||||
#define TARGET_FREEBSD_NR_getuid 24
|
||||
#define TARGET_FREEBSD_NR_geteuid 25
|
||||
#define TARGET_FREEBSD_NR_ptrace 26
|
||||
#define TARGET_FREEBSD_NR_recvmsg 27
|
||||
#define TARGET_FREEBSD_NR_sendmsg 28
|
||||
#define TARGET_FREEBSD_NR_recvfrom 29
|
||||
#define TARGET_FREEBSD_NR_accept 30
|
||||
#define TARGET_FREEBSD_NR_getpeername 31
|
||||
#define TARGET_FREEBSD_NR_getsockname 32
|
||||
#define TARGET_FREEBSD_NR_access 33
|
||||
#define TARGET_FREEBSD_NR_chflags 34
|
||||
#define TARGET_FREEBSD_NR_fchflags 35
|
||||
#define TARGET_FREEBSD_NR_sync 36
|
||||
#define TARGET_FREEBSD_NR_kill 37
|
||||
/* 38 is old stat */
|
||||
#define TARGET_FREEBSD_NR_getppid 39
|
||||
/* 40 is old lstat */
|
||||
#define TARGET_FREEBSD_NR_dup 41
|
||||
#define TARGET_FREEBSD_NR_pipe 42
|
||||
#define TARGET_FREEBSD_NR_getegid 43
|
||||
#define TARGET_FREEBSD_NR_profil 44
|
||||
#define TARGET_FREEBSD_NR_ktrace 45
|
||||
/* 46 is old sigaction */
|
||||
#define TARGET_FREEBSD_NR_getgid 47
|
||||
/* 48 is old sigprocmask */
|
||||
#define TARGET_FREEBSD_NR_getlogin 49
|
||||
#define TARGET_FREEBSD_NR_setlogin 50
|
||||
#define TARGET_FREEBSD_NR_acct 51
|
||||
/* 52 is old sigpending */
|
||||
#define TARGET_FREEBSD_NR_sigaltstack 53
|
||||
#define TARGET_FREEBSD_NR_ioctl 54
|
||||
#define TARGET_FREEBSD_NR_reboot 55
|
||||
#define TARGET_FREEBSD_NR_revoke 56
|
||||
#define TARGET_FREEBSD_NR_symlink 57
|
||||
#define TARGET_FREEBSD_NR_readlink 58
|
||||
#define TARGET_FREEBSD_NR_execve 59
|
||||
#define TARGET_FREEBSD_NR_umask 60
|
||||
#define TARGET_FREEBSD_NR_chroot 61
|
||||
/* 62 is old fstat */
|
||||
/* 63 is old getkerninfo */
|
||||
/* 64 is old getpagesize */
|
||||
#define TARGET_FREEBSD_NR_msync 65
|
||||
#define TARGET_FREEBSD_NR_vfork 66
|
||||
/* 67 is obsolete vread */
|
||||
/* 68 is obsolete vwrite */
|
||||
#define TARGET_FREEBSD_NR_sbrk 69
|
||||
#define TARGET_FREEBSD_NR_sstk 70
|
||||
/* 71 is old mmap */
|
||||
#define TARGET_FREEBSD_NR_vadvise 72
|
||||
#define TARGET_FREEBSD_NR_munmap 73
|
||||
#define TARGET_FREEBSD_NR_mprotect 74
|
||||
#define TARGET_FREEBSD_NR_madvise 75
|
||||
/* 76 is obsolete vhangup */
|
||||
/* 77 is obsolete vlimit */
|
||||
#define TARGET_FREEBSD_NR_mincore 78
|
||||
#define TARGET_FREEBSD_NR_getgroups 79
|
||||
#define TARGET_FREEBSD_NR_setgroups 80
|
||||
#define TARGET_FREEBSD_NR_getpgrp 81
|
||||
#define TARGET_FREEBSD_NR_setpgid 82
|
||||
#define TARGET_FREEBSD_NR_setitimer 83
|
||||
/* 84 is old wait */
|
||||
#define TARGET_FREEBSD_NR_swapon 85
|
||||
#define TARGET_FREEBSD_NR_getitimer 86
|
||||
/* 87 is old gethostname */
|
||||
/* 88 is old sethostname */
|
||||
#define TARGET_FREEBSD_NR_getdtablesize 89
|
||||
#define TARGET_FREEBSD_NR_dup2 90
|
||||
#define TARGET_FREEBSD_NR_fcntl 92
|
||||
#define TARGET_FREEBSD_NR_select 93
|
||||
#define TARGET_FREEBSD_NR_fsync 95
|
||||
#define TARGET_FREEBSD_NR_setpriority 96
|
||||
#define TARGET_FREEBSD_NR_socket 97
|
||||
#define TARGET_FREEBSD_NR_connect 98
|
||||
/* 99 is old accept */
|
||||
#define TARGET_FREEBSD_NR_getpriority 100
|
||||
/* 101 is old send */
|
||||
/* 102 is old recv */
|
||||
/* 103 is old sigreturn */
|
||||
#define TARGET_FREEBSD_NR_bind 104
|
||||
#define TARGET_FREEBSD_NR_setsockopt 105
|
||||
#define TARGET_FREEBSD_NR_listen 106
|
||||
/* 107 is obsolete vtimes */
|
||||
/* 108 is old sigvec */
|
||||
/* 109 is old sigblock */
|
||||
/* 110 is old sigsetmask */
|
||||
/* 111 is old sigsuspend */
|
||||
/* 112 is old sigstack */
|
||||
/* 113 is old recvmsg */
|
||||
/* 114 is old sendmsg */
|
||||
/* 115 is obsolete vtrace */
|
||||
#define TARGET_FREEBSD_NR_gettimeofday 116
|
||||
#define TARGET_FREEBSD_NR_getrusage 117
|
||||
#define TARGET_FREEBSD_NR_getsockopt 118
|
||||
#define TARGET_FREEBSD_NR_readv 120
|
||||
#define TARGET_FREEBSD_NR_writev 121
|
||||
#define TARGET_FREEBSD_NR_settimeofday 122
|
||||
#define TARGET_FREEBSD_NR_fchown 123
|
||||
#define TARGET_FREEBSD_NR_fchmod 124
|
||||
/* 125 is old recvfrom */
|
||||
#define TARGET_FREEBSD_NR_setreuid 126
|
||||
#define TARGET_FREEBSD_NR_setregid 127
|
||||
#define TARGET_FREEBSD_NR_rename 128
|
||||
/* 129 is old truncate */
|
||||
/* 130 is old ftruncate */
|
||||
#define TARGET_FREEBSD_NR_flock 131
|
||||
#define TARGET_FREEBSD_NR_mkfifo 132
|
||||
#define TARGET_FREEBSD_NR_sendto 133
|
||||
#define TARGET_FREEBSD_NR_shutdown 134
|
||||
#define TARGET_FREEBSD_NR_socketpair 135
|
||||
#define TARGET_FREEBSD_NR_mkdir 136
|
||||
#define TARGET_FREEBSD_NR_rmdir 137
|
||||
#define TARGET_FREEBSD_NR_utimes 138
|
||||
/* 139 is obsolete 4.2 sigreturn */
|
||||
#define TARGET_FREEBSD_NR_adjtime 140
|
||||
/* 141 is old getpeername */
|
||||
/* 142 is old gethostid */
|
||||
/* 143 is old sethostid */
|
||||
/* 144 is old getrlimit */
|
||||
/* 145 is old setrlimit */
|
||||
/* 146 is old killpg */
|
||||
#define TARGET_FREEBSD_NR_killpg 146 /* COMPAT */
|
||||
#define TARGET_FREEBSD_NR_setsid 147
|
||||
#define TARGET_FREEBSD_NR_quotactl 148
|
||||
/* 149 is old quota */
|
||||
/* 150 is old getsockname */
|
||||
#define TARGET_FREEBSD_NR_nlm_syscall 154
|
||||
#define TARGET_FREEBSD_NR_nfssvc 155
|
||||
/* 156 is old getdirentries */
|
||||
#define TARGET_FREEBSD_NR_freebsd4_statfs 157
|
||||
#define TARGET_FREEBSD_NR_freebsd4_fstatfs 158
|
||||
#define TARGET_FREEBSD_NR_lgetfh 160
|
||||
#define TARGET_FREEBSD_NR_getfh 161
|
||||
#define TARGET_FREEBSD_NR_freebsd4_getdomainname 162
|
||||
#define TARGET_FREEBSD_NR_freebsd4_setdomainname 163
|
||||
#define TARGET_FREEBSD_NR_freebsd4_uname 164
|
||||
#define TARGET_FREEBSD_NR_sysarch 165
|
||||
#define TARGET_FREEBSD_NR_rtprio 166
|
||||
#define TARGET_FREEBSD_NR_semsys 169
|
||||
#define TARGET_FREEBSD_NR_msgsys 170
|
||||
#define TARGET_FREEBSD_NR_shmsys 171
|
||||
#define TARGET_FREEBSD_NR_freebsd6_pread 173
|
||||
#define TARGET_FREEBSD_NR_freebsd6_pwrite 174
|
||||
#define TARGET_FREEBSD_NR_setfib 175
|
||||
#define TARGET_FREEBSD_NR_ntp_adjtime 176
|
||||
#define TARGET_FREEBSD_NR_setgid 181
|
||||
#define TARGET_FREEBSD_NR_setegid 182
|
||||
#define TARGET_FREEBSD_NR_seteuid 183
|
||||
#define TARGET_FREEBSD_NR_stat 188
|
||||
#define TARGET_FREEBSD_NR_fstat 189
|
||||
#define TARGET_FREEBSD_NR_lstat 190
|
||||
#define TARGET_FREEBSD_NR_pathconf 191
|
||||
#define TARGET_FREEBSD_NR_fpathconf 192
|
||||
#define TARGET_FREEBSD_NR_getrlimit 194
|
||||
#define TARGET_FREEBSD_NR_setrlimit 195
|
||||
#define TARGET_FREEBSD_NR_getdirentries 196
|
||||
#define TARGET_FREEBSD_NR_freebsd6_mmap 197
|
||||
#define TARGET_FREEBSD_NR___syscall 198
|
||||
#define TARGET_FREEBSD_NR_freebsd6_lseek 199
|
||||
#define TARGET_FREEBSD_NR_freebsd6_truncate 200
|
||||
#define TARGET_FREEBSD_NR_freebsd6_ftruncate 201
|
||||
#define TARGET_FREEBSD_NR___sysctl 202
|
||||
#define TARGET_FREEBSD_NR_mlock 203
|
||||
#define TARGET_FREEBSD_NR_munlock 204
|
||||
#define TARGET_FREEBSD_NR_undelete 205
|
||||
#define TARGET_FREEBSD_NR_futimes 206
|
||||
#define TARGET_FREEBSD_NR_getpgid 207
|
||||
#define TARGET_FREEBSD_NR_poll 209
|
||||
#define TARGET_FREEBSD_NR_freebsd7___semctl 220
|
||||
#define TARGET_FREEBSD_NR_semget 221
|
||||
#define TARGET_FREEBSD_NR_semop 222
|
||||
#define TARGET_FREEBSD_NR_freebsd7_msgctl 224
|
||||
#define TARGET_FREEBSD_NR_msgget 225
|
||||
#define TARGET_FREEBSD_NR_msgsnd 226
|
||||
#define TARGET_FREEBSD_NR_msgrcv 227
|
||||
#define TARGET_FREEBSD_NR_shmat 228
|
||||
#define TARGET_FREEBSD_NR_freebsd7_shmctl 229
|
||||
#define TARGET_FREEBSD_NR_shmdt 230
|
||||
#define TARGET_FREEBSD_NR_shmget 231
|
||||
#define TARGET_FREEBSD_NR_clock_gettime 232
|
||||
#define TARGET_FREEBSD_NR_clock_settime 233
|
||||
#define TARGET_FREEBSD_NR_clock_getres 234
|
||||
#define TARGET_FREEBSD_NR_ktimer_create 235
|
||||
#define TARGET_FREEBSD_NR_ktimer_delete 236
|
||||
#define TARGET_FREEBSD_NR_ktimer_settime 237
|
||||
#define TARGET_FREEBSD_NR_ktimer_gettime 238
|
||||
#define TARGET_FREEBSD_NR_ktimer_getoverrun 239
|
||||
#define TARGET_FREEBSD_NR_nanosleep 240
|
||||
#define TARGET_FREEBSD_NR_ntp_gettime 248
|
||||
#define TARGET_FREEBSD_NR_minherit 250
|
||||
#define TARGET_FREEBSD_NR_rfork 251
|
||||
#define TARGET_FREEBSD_NR_openbsd_poll 252
|
||||
#define TARGET_FREEBSD_NR_issetugid 253
|
||||
#define TARGET_FREEBSD_NR_lchown 254
|
||||
#define TARGET_FREEBSD_NR_aio_read 255
|
||||
#define TARGET_FREEBSD_NR_aio_write 256
|
||||
#define TARGET_FREEBSD_NR_lio_listio 257
|
||||
#define TARGET_FREEBSD_NR_getdents 272
|
||||
#define TARGET_FREEBSD_NR_lchmod 274
|
||||
#define TARGET_FREEBSD_NR_netbsd_lchown 275
|
||||
#define TARGET_FREEBSD_NR_lutimes 276
|
||||
#define TARGET_FREEBSD_NR_netbsd_msync 277
|
||||
#define TARGET_FREEBSD_NR_nstat 278
|
||||
#define TARGET_FREEBSD_NR_nfstat 279
|
||||
#define TARGET_FREEBSD_NR_nlstat 280
|
||||
#define TARGET_FREEBSD_NR_preadv 289
|
||||
#define TARGET_FREEBSD_NR_pwritev 290
|
||||
#define TARGET_FREEBSD_NR_freebsd4_fhstatfs 297
|
||||
#define TARGET_FREEBSD_NR_fhopen 298
|
||||
#define TARGET_FREEBSD_NR_fhstat 299
|
||||
#define TARGET_FREEBSD_NR_modnext 300
|
||||
#define TARGET_FREEBSD_NR_modstat 301
|
||||
#define TARGET_FREEBSD_NR_modfnext 302
|
||||
#define TARGET_FREEBSD_NR_modfind 303
|
||||
#define TARGET_FREEBSD_NR_kldload 304
|
||||
#define TARGET_FREEBSD_NR_kldunload 305
|
||||
#define TARGET_FREEBSD_NR_kldfind 306
|
||||
#define TARGET_FREEBSD_NR_kldnext 307
|
||||
#define TARGET_FREEBSD_NR_kldstat 308
|
||||
#define TARGET_FREEBSD_NR_kldfirstmod 309
|
||||
#define TARGET_FREEBSD_NR_getsid 310
|
||||
#define TARGET_FREEBSD_NR_setresuid 311
|
||||
#define TARGET_FREEBSD_NR_setresgid 312
|
||||
/* 313 is obsolete signanosleep */
|
||||
#define TARGET_FREEBSD_NR_aio_return 314
|
||||
#define TARGET_FREEBSD_NR_aio_suspend 315
|
||||
#define TARGET_FREEBSD_NR_aio_cancel 316
|
||||
#define TARGET_FREEBSD_NR_aio_error 317
|
||||
#define TARGET_FREEBSD_NR_oaio_read 318
|
||||
#define TARGET_FREEBSD_NR_oaio_write 319
|
||||
#define TARGET_FREEBSD_NR_olio_listio 320
|
||||
#define TARGET_FREEBSD_NR_yield 321
|
||||
/* 322 is obsolete thr_sleep */
|
||||
/* 323 is obsolete thr_wakeup */
|
||||
#define TARGET_FREEBSD_NR_mlockall 324
|
||||
#define TARGET_FREEBSD_NR_munlockall 325
|
||||
#define TARGET_FREEBSD_NR___getcwd 326
|
||||
#define TARGET_FREEBSD_NR_sched_setparam 327
|
||||
#define TARGET_FREEBSD_NR_sched_getparam 328
|
||||
#define TARGET_FREEBSD_NR_sched_setscheduler 329
|
||||
#define TARGET_FREEBSD_NR_sched_getscheduler 330
|
||||
#define TARGET_FREEBSD_NR_sched_yield 331
|
||||
#define TARGET_FREEBSD_NR_sched_get_priority_max 332
|
||||
#define TARGET_FREEBSD_NR_sched_get_priority_min 333
|
||||
#define TARGET_FREEBSD_NR_sched_rr_get_interval 334
|
||||
#define TARGET_FREEBSD_NR_utrace 335
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sendfile 336
|
||||
#define TARGET_FREEBSD_NR_kldsym 337
|
||||
#define TARGET_FREEBSD_NR_jail 338
|
||||
#define TARGET_FREEBSD_NR_nnpfs_syscall 339
|
||||
#define TARGET_FREEBSD_NR_sigprocmask 340
|
||||
#define TARGET_FREEBSD_NR_sigsuspend 341
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sigaction 342
|
||||
#define TARGET_FREEBSD_NR_sigpending 343
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sigreturn 344
|
||||
#define TARGET_FREEBSD_NR_sigtimedwait 345
|
||||
#define TARGET_FREEBSD_NR_sigwaitinfo 346
|
||||
#define TARGET_FREEBSD_NR___acl_get_file 347
|
||||
#define TARGET_FREEBSD_NR___acl_set_file 348
|
||||
#define TARGET_FREEBSD_NR___acl_get_fd 349
|
||||
#define TARGET_FREEBSD_NR___acl_set_fd 350
|
||||
#define TARGET_FREEBSD_NR___acl_delete_file 351
|
||||
#define TARGET_FREEBSD_NR___acl_delete_fd 352
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_file 353
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354
|
||||
#define TARGET_FREEBSD_NR_extattrctl 355
|
||||
#define TARGET_FREEBSD_NR_extattr_set_file 356
|
||||
#define TARGET_FREEBSD_NR_extattr_get_file 357
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_file 358
|
||||
#define TARGET_FREEBSD_NR_aio_waitcomplete 359
|
||||
#define TARGET_FREEBSD_NR_getresuid 360
|
||||
#define TARGET_FREEBSD_NR_getresgid 361
|
||||
#define TARGET_FREEBSD_NR_kqueue 362
|
||||
#define TARGET_FREEBSD_NR_kevent 363
|
||||
#define TARGET_FREEBSD_NR_extattr_set_fd 371
|
||||
#define TARGET_FREEBSD_NR_extattr_get_fd 372
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_fd 373
|
||||
#define TARGET_FREEBSD_NR___setugid 374
|
||||
#define TARGET_FREEBSD_NR_eaccess 376
|
||||
#define TARGET_FREEBSD_NR_afs3_syscall 377
|
||||
#define TARGET_FREEBSD_NR_nmount 378
|
||||
#define TARGET_FREEBSD_NR___mac_get_proc 384
|
||||
#define TARGET_FREEBSD_NR___mac_set_proc 385
|
||||
#define TARGET_FREEBSD_NR___mac_get_fd 386
|
||||
#define TARGET_FREEBSD_NR___mac_get_file 387
|
||||
#define TARGET_FREEBSD_NR___mac_set_fd 388
|
||||
#define TARGET_FREEBSD_NR___mac_set_file 389
|
||||
#define TARGET_FREEBSD_NR_kenv 390
|
||||
#define TARGET_FREEBSD_NR_lchflags 391
|
||||
#define TARGET_FREEBSD_NR_uuidgen 392
|
||||
#define TARGET_FREEBSD_NR_sendfile 393
|
||||
#define TARGET_FREEBSD_NR_mac_syscall 394
|
||||
#define TARGET_FREEBSD_NR_getfsstat 395
|
||||
#define TARGET_FREEBSD_NR_statfs 396
|
||||
#define TARGET_FREEBSD_NR_fstatfs 397
|
||||
#define TARGET_FREEBSD_NR_fhstatfs 398
|
||||
#define TARGET_FREEBSD_NR_ksem_close 400
|
||||
#define TARGET_FREEBSD_NR_ksem_post 401
|
||||
#define TARGET_FREEBSD_NR_ksem_wait 402
|
||||
#define TARGET_FREEBSD_NR_ksem_trywait 403
|
||||
#define TARGET_FREEBSD_NR_ksem_init 404
|
||||
#define TARGET_FREEBSD_NR_ksem_open 405
|
||||
#define TARGET_FREEBSD_NR_ksem_unlink 406
|
||||
#define TARGET_FREEBSD_NR_ksem_getvalue 407
|
||||
#define TARGET_FREEBSD_NR_ksem_destroy 408
|
||||
#define TARGET_FREEBSD_NR___mac_get_pid 409
|
||||
#define TARGET_FREEBSD_NR___mac_get_link 410
|
||||
#define TARGET_FREEBSD_NR___mac_set_link 411
|
||||
#define TARGET_FREEBSD_NR_extattr_set_link 412
|
||||
#define TARGET_FREEBSD_NR_extattr_get_link 413
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_link 414
|
||||
#define TARGET_FREEBSD_NR___mac_execve 415
|
||||
#define TARGET_FREEBSD_NR_sigaction 416
|
||||
#define TARGET_FREEBSD_NR_sigreturn 417
|
||||
#define TARGET_FREEBSD_NR_getcontext 421
|
||||
#define TARGET_FREEBSD_NR_setcontext 422
|
||||
#define TARGET_FREEBSD_NR_swapcontext 423
|
||||
#define TARGET_FREEBSD_NR_swapoff 424
|
||||
#define TARGET_FREEBSD_NR___acl_get_link 425
|
||||
#define TARGET_FREEBSD_NR___acl_set_link 426
|
||||
#define TARGET_FREEBSD_NR___acl_delete_link 427
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_link 428
|
||||
#define TARGET_FREEBSD_NR_sigwait 429
|
||||
#define TARGET_FREEBSD_NR_thr_create 430
|
||||
#define TARGET_FREEBSD_NR_thr_exit 431
|
||||
#define TARGET_FREEBSD_NR_thr_self 432
|
||||
#define TARGET_FREEBSD_NR_thr_kill 433
|
||||
#define TARGET_FREEBSD_NR__umtx_lock 434
|
||||
#define TARGET_FREEBSD_NR__umtx_unlock 435
|
||||
#define TARGET_FREEBSD_NR_jail_attach 436
|
||||
#define TARGET_FREEBSD_NR_extattr_list_fd 437
|
||||
#define TARGET_FREEBSD_NR_extattr_list_file 438
|
||||
#define TARGET_FREEBSD_NR_extattr_list_link 439
|
||||
#define TARGET_FREEBSD_NR_ksem_timedwait 441
|
||||
#define TARGET_FREEBSD_NR_thr_suspend 442
|
||||
#define TARGET_FREEBSD_NR_thr_wake 443
|
||||
#define TARGET_FREEBSD_NR_kldunloadf 444
|
||||
#define TARGET_FREEBSD_NR_audit 445
|
||||
#define TARGET_FREEBSD_NR_auditon 446
|
||||
#define TARGET_FREEBSD_NR_getauid 447
|
||||
#define TARGET_FREEBSD_NR_setauid 448
|
||||
#define TARGET_FREEBSD_NR_getaudit 449
|
||||
#define TARGET_FREEBSD_NR_setaudit 450
|
||||
#define TARGET_FREEBSD_NR_getaudit_addr 451
|
||||
#define TARGET_FREEBSD_NR_setaudit_addr 452
|
||||
#define TARGET_FREEBSD_NR_auditctl 453
|
||||
#define TARGET_FREEBSD_NR__umtx_op 454
|
||||
#define TARGET_FREEBSD_NR_thr_new 455
|
||||
#define TARGET_FREEBSD_NR_sigqueue 456
|
||||
#define TARGET_FREEBSD_NR_kmq_open 457
|
||||
#define TARGET_FREEBSD_NR_kmq_setattr 458
|
||||
#define TARGET_FREEBSD_NR_kmq_timedreceive 459
|
||||
#define TARGET_FREEBSD_NR_kmq_timedsend 460
|
||||
#define TARGET_FREEBSD_NR_kmq_notify 461
|
||||
#define TARGET_FREEBSD_NR_kmq_unlink 462
|
||||
#define TARGET_FREEBSD_NR_abort2 463
|
||||
#define TARGET_FREEBSD_NR_thr_set_name 464
|
||||
#define TARGET_FREEBSD_NR_aio_fsync 465
|
||||
#define TARGET_FREEBSD_NR_rtprio_thread 466
|
||||
#define TARGET_FREEBSD_NR_sctp_peeloff 471
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_sendmsg 472
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_recvmsg 474
|
||||
#define TARGET_FREEBSD_NR_pread 475
|
||||
#define TARGET_FREEBSD_NR_pwrite 476
|
||||
#define TARGET_FREEBSD_NR_mmap 477
|
||||
#define TARGET_FREEBSD_NR_lseek 478
|
||||
#define TARGET_FREEBSD_NR_truncate 479
|
||||
#define TARGET_FREEBSD_NR_ftruncate 480
|
||||
#define TARGET_FREEBSD_NR_thr_kill2 481
|
||||
#define TARGET_FREEBSD_NR_shm_open 482
|
||||
#define TARGET_FREEBSD_NR_shm_unlink 483
|
||||
#define TARGET_FREEBSD_NR_cpuset 484
|
||||
#define TARGET_FREEBSD_NR_cpuset_setid 485
|
||||
#define TARGET_FREEBSD_NR_cpuset_getid 486
|
||||
#define TARGET_FREEBSD_NR_cpuset_getaffinity 487
|
||||
#define TARGET_FREEBSD_NR_cpuset_setaffinity 488
|
||||
#define TARGET_FREEBSD_NR_faccessat 489
|
||||
#define TARGET_FREEBSD_NR_fchmodat 490
|
||||
#define TARGET_FREEBSD_NR_fchownat 491
|
||||
#define TARGET_FREEBSD_NR_fexecve 492
|
||||
#define TARGET_FREEBSD_NR_fstatat 493
|
||||
#define TARGET_FREEBSD_NR_futimesat 494
|
||||
#define TARGET_FREEBSD_NR_linkat 495
|
||||
#define TARGET_FREEBSD_NR_mkdirat 496
|
||||
#define TARGET_FREEBSD_NR_mkfifoat 497
|
||||
#define TARGET_FREEBSD_NR_mknodat 498
|
||||
#define TARGET_FREEBSD_NR_openat 499
|
||||
#define TARGET_FREEBSD_NR_readlinkat 500
|
||||
#define TARGET_FREEBSD_NR_renameat 501
|
||||
#define TARGET_FREEBSD_NR_symlinkat 502
|
||||
#define TARGET_FREEBSD_NR_unlinkat 503
|
||||
#define TARGET_FREEBSD_NR_posix_openpt 504
|
||||
#define TARGET_FREEBSD_NR_gssd_syscall 505
|
||||
#define TARGET_FREEBSD_NR_jail_get 506
|
||||
#define TARGET_FREEBSD_NR_jail_set 507
|
||||
#define TARGET_FREEBSD_NR_jail_remove 508
|
||||
#define TARGET_FREEBSD_NR_closefrom 509
|
||||
#define TARGET_FREEBSD_NR___semctl 510
|
||||
#define TARGET_FREEBSD_NR_msgctl 511
|
||||
#define TARGET_FREEBSD_NR_shmctl 512
|
||||
#define TARGET_FREEBSD_NR_lpathconf 513
|
||||
#define TARGET_FREEBSD_NR_cap_new 514
|
||||
#define TARGET_FREEBSD_NR_cap_getrights 515
|
||||
#define TARGET_FREEBSD_NR_cap_enter 516
|
||||
#define TARGET_FREEBSD_NR_cap_getmode 517
|
||||
#define TARGET_FREEBSD_NR_pdfork 518
|
||||
#define TARGET_FREEBSD_NR_pdkill 519
|
||||
#define TARGET_FREEBSD_NR_pdgetpid 520
|
||||
#define TARGET_FREEBSD_NR_pselect 522
|
||||
#define TARGET_FREEBSD_NR_getloginclass 523
|
||||
#define TARGET_FREEBSD_NR_setloginclass 524
|
||||
#define TARGET_FREEBSD_NR_rctl_get_racct 525
|
||||
#define TARGET_FREEBSD_NR_rctl_get_rules 526
|
||||
#define TARGET_FREEBSD_NR_rctl_get_limits 527
|
||||
#define TARGET_FREEBSD_NR_rctl_add_rule 528
|
||||
#define TARGET_FREEBSD_NR_rctl_remove_rule 529
|
||||
#define TARGET_FREEBSD_NR_posix_fallocate 530
|
||||
#define TARGET_FREEBSD_NR_posix_fadvise 531
|
||||
#define TARGET_FREEBSD_NR_MAXSYSCALL 532
|
||||
#define TARGET_FREEBSD_NR_syscall 0
|
||||
#define TARGET_FREEBSD_NR_exit 1
|
||||
#define TARGET_FREEBSD_NR_fork 2
|
||||
#define TARGET_FREEBSD_NR_read 3
|
||||
#define TARGET_FREEBSD_NR_write 4
|
||||
#define TARGET_FREEBSD_NR_open 5
|
||||
#define TARGET_FREEBSD_NR_close 6
|
||||
#define TARGET_FREEBSD_NR_wait4 7
|
||||
#define TARGET_FREEBSD_NR_link 9
|
||||
#define TARGET_FREEBSD_NR_unlink 10
|
||||
#define TARGET_FREEBSD_NR_chdir 12
|
||||
#define TARGET_FREEBSD_NR_fchdir 13
|
||||
#define TARGET_FREEBSD_NR_mknod 14
|
||||
#define TARGET_FREEBSD_NR_chmod 15
|
||||
#define TARGET_FREEBSD_NR_chown 16
|
||||
#define TARGET_FREEBSD_NR_break 17
|
||||
#define TARGET_FREEBSD_NR_freebsd4_getfsstat 18
|
||||
#define TARGET_FREEBSD_NR_getpid 20
|
||||
#define TARGET_FREEBSD_NR_mount 21
|
||||
#define TARGET_FREEBSD_NR_unmount 22
|
||||
#define TARGET_FREEBSD_NR_setuid 23
|
||||
#define TARGET_FREEBSD_NR_getuid 24
|
||||
#define TARGET_FREEBSD_NR_geteuid 25
|
||||
#define TARGET_FREEBSD_NR_ptrace 26
|
||||
#define TARGET_FREEBSD_NR_recvmsg 27
|
||||
#define TARGET_FREEBSD_NR_sendmsg 28
|
||||
#define TARGET_FREEBSD_NR_recvfrom 29
|
||||
#define TARGET_FREEBSD_NR_accept 30
|
||||
#define TARGET_FREEBSD_NR_getpeername 31
|
||||
#define TARGET_FREEBSD_NR_getsockname 32
|
||||
#define TARGET_FREEBSD_NR_access 33
|
||||
#define TARGET_FREEBSD_NR_chflags 34
|
||||
#define TARGET_FREEBSD_NR_fchflags 35
|
||||
#define TARGET_FREEBSD_NR_sync 36
|
||||
#define TARGET_FREEBSD_NR_kill 37
|
||||
#define TARGET_FREEBSD_NR_getppid 39
|
||||
#define TARGET_FREEBSD_NR_dup 41
|
||||
#define TARGET_FREEBSD_NR_pipe 42
|
||||
#define TARGET_FREEBSD_NR_getegid 43
|
||||
#define TARGET_FREEBSD_NR_profil 44
|
||||
#define TARGET_FREEBSD_NR_ktrace 45
|
||||
#define TARGET_FREEBSD_NR_getgid 47
|
||||
#define TARGET_FREEBSD_NR_getlogin 49
|
||||
#define TARGET_FREEBSD_NR_setlogin 50
|
||||
#define TARGET_FREEBSD_NR_acct 51
|
||||
#define TARGET_FREEBSD_NR_sigaltstack 53
|
||||
#define TARGET_FREEBSD_NR_ioctl 54
|
||||
#define TARGET_FREEBSD_NR_reboot 55
|
||||
#define TARGET_FREEBSD_NR_revoke 56
|
||||
#define TARGET_FREEBSD_NR_symlink 57
|
||||
#define TARGET_FREEBSD_NR_readlink 58
|
||||
#define TARGET_FREEBSD_NR_execve 59
|
||||
#define TARGET_FREEBSD_NR_umask 60
|
||||
#define TARGET_FREEBSD_NR_chroot 61
|
||||
#define TARGET_FREEBSD_NR_msync 65
|
||||
#define TARGET_FREEBSD_NR_vfork 66
|
||||
#define TARGET_FREEBSD_NR_sbrk 69
|
||||
#define TARGET_FREEBSD_NR_sstk 70
|
||||
#define TARGET_FREEBSD_NR_vadvise 72
|
||||
#define TARGET_FREEBSD_NR_munmap 73
|
||||
#define TARGET_FREEBSD_NR_mprotect 74
|
||||
#define TARGET_FREEBSD_NR_madvise 75
|
||||
#define TARGET_FREEBSD_NR_mincore 78
|
||||
#define TARGET_FREEBSD_NR_getgroups 79
|
||||
#define TARGET_FREEBSD_NR_setgroups 80
|
||||
#define TARGET_FREEBSD_NR_getpgrp 81
|
||||
#define TARGET_FREEBSD_NR_setpgid 82
|
||||
#define TARGET_FREEBSD_NR_setitimer 83
|
||||
#define TARGET_FREEBSD_NR_swapon 85
|
||||
#define TARGET_FREEBSD_NR_getitimer 86
|
||||
#define TARGET_FREEBSD_NR_getdtablesize 89
|
||||
#define TARGET_FREEBSD_NR_dup2 90
|
||||
#define TARGET_FREEBSD_NR_fcntl 92
|
||||
#define TARGET_FREEBSD_NR_select 93
|
||||
#define TARGET_FREEBSD_NR_fsync 95
|
||||
#define TARGET_FREEBSD_NR_setpriority 96
|
||||
#define TARGET_FREEBSD_NR_socket 97
|
||||
#define TARGET_FREEBSD_NR_connect 98
|
||||
#define TARGET_FREEBSD_NR_getpriority 100
|
||||
#define TARGET_FREEBSD_NR_bind 104
|
||||
#define TARGET_FREEBSD_NR_setsockopt 105
|
||||
#define TARGET_FREEBSD_NR_listen 106
|
||||
#define TARGET_FREEBSD_NR_gettimeofday 116
|
||||
#define TARGET_FREEBSD_NR_getrusage 117
|
||||
#define TARGET_FREEBSD_NR_getsockopt 118
|
||||
#define TARGET_FREEBSD_NR_readv 120
|
||||
#define TARGET_FREEBSD_NR_writev 121
|
||||
#define TARGET_FREEBSD_NR_settimeofday 122
|
||||
#define TARGET_FREEBSD_NR_fchown 123
|
||||
#define TARGET_FREEBSD_NR_fchmod 124
|
||||
#define TARGET_FREEBSD_NR_setreuid 126
|
||||
#define TARGET_FREEBSD_NR_setregid 127
|
||||
#define TARGET_FREEBSD_NR_rename 128
|
||||
#define TARGET_FREEBSD_NR_flock 131
|
||||
#define TARGET_FREEBSD_NR_mkfifo 132
|
||||
#define TARGET_FREEBSD_NR_sendto 133
|
||||
#define TARGET_FREEBSD_NR_shutdown 134
|
||||
#define TARGET_FREEBSD_NR_socketpair 135
|
||||
#define TARGET_FREEBSD_NR_mkdir 136
|
||||
#define TARGET_FREEBSD_NR_rmdir 137
|
||||
#define TARGET_FREEBSD_NR_utimes 138
|
||||
#define TARGET_FREEBSD_NR_adjtime 140
|
||||
#define TARGET_FREEBSD_NR_setsid 147
|
||||
#define TARGET_FREEBSD_NR_quotactl 148
|
||||
#define TARGET_FREEBSD_NR_nlm_syscall 154
|
||||
#define TARGET_FREEBSD_NR_nfssvc 155
|
||||
#define TARGET_FREEBSD_NR_freebsd4_statfs 157
|
||||
#define TARGET_FREEBSD_NR_freebsd4_fstatfs 158
|
||||
#define TARGET_FREEBSD_NR_lgetfh 160
|
||||
#define TARGET_FREEBSD_NR_getfh 161
|
||||
#define TARGET_FREEBSD_NR_getdomainname 162
|
||||
#define TARGET_FREEBSD_NR_setdomainname 163
|
||||
#define TARGET_FREEBSD_NR_uname 164
|
||||
#define TARGET_FREEBSD_NR_sysarch 165
|
||||
#define TARGET_FREEBSD_NR_rtprio 166
|
||||
#define TARGET_FREEBSD_NR_semsys 169
|
||||
#define TARGET_FREEBSD_NR_msgsys 170
|
||||
#define TARGET_FREEBSD_NR_shmsys 171
|
||||
#define TARGET_FREEBSD_NR_freebsd6_pread 173
|
||||
#define TARGET_FREEBSD_NR_freebsd6_pwrite 174
|
||||
#define TARGET_FREEBSD_NR_setfib 175
|
||||
#define TARGET_FREEBSD_NR_ntp_adjtime 176
|
||||
#define TARGET_FREEBSD_NR_setgid 181
|
||||
#define TARGET_FREEBSD_NR_setegid 182
|
||||
#define TARGET_FREEBSD_NR_seteuid 183
|
||||
#define TARGET_FREEBSD_NR_stat 188
|
||||
#define TARGET_FREEBSD_NR_fstat 189
|
||||
#define TARGET_FREEBSD_NR_lstat 190
|
||||
#define TARGET_FREEBSD_NR_pathconf 191
|
||||
#define TARGET_FREEBSD_NR_fpathconf 192
|
||||
#define TARGET_FREEBSD_NR_getrlimit 194
|
||||
#define TARGET_FREEBSD_NR_setrlimit 195
|
||||
#define TARGET_FREEBSD_NR_getdirentries 196
|
||||
#define TARGET_FREEBSD_NR_freebsd6_mmap 197
|
||||
#define TARGET_FREEBSD_NR___syscall 198
|
||||
#define TARGET_FREEBSD_NR_freebsd6_lseek 199
|
||||
#define TARGET_FREEBSD_NR_freebsd6_truncate 200
|
||||
#define TARGET_FREEBSD_NR_freebsd6_ftruncate 201
|
||||
#define TARGET_FREEBSD_NR___sysctl 202
|
||||
#define TARGET_FREEBSD_NR_mlock 203
|
||||
#define TARGET_FREEBSD_NR_munlock 204
|
||||
#define TARGET_FREEBSD_NR_undelete 205
|
||||
#define TARGET_FREEBSD_NR_futimes 206
|
||||
#define TARGET_FREEBSD_NR_getpgid 207
|
||||
#define TARGET_FREEBSD_NR_poll 209
|
||||
#define TARGET_FREEBSD_NR___semctl 220
|
||||
#define TARGET_FREEBSD_NR_semget 221
|
||||
#define TARGET_FREEBSD_NR_semop 222
|
||||
#define TARGET_FREEBSD_NR_msgctl 224
|
||||
#define TARGET_FREEBSD_NR_msgget 225
|
||||
#define TARGET_FREEBSD_NR_msgsnd 226
|
||||
#define TARGET_FREEBSD_NR_msgrcv 227
|
||||
#define TARGET_FREEBSD_NR_shmat 228
|
||||
#define TARGET_FREEBSD_NR_shmctl 229
|
||||
#define TARGET_FREEBSD_NR_shmdt 230
|
||||
#define TARGET_FREEBSD_NR_shmget 231
|
||||
#define TARGET_FREEBSD_NR_clock_gettime 232
|
||||
#define TARGET_FREEBSD_NR_clock_settime 233
|
||||
#define TARGET_FREEBSD_NR_clock_getres 234
|
||||
#define TARGET_FREEBSD_NR_ktimer_create 235
|
||||
#define TARGET_FREEBSD_NR_ktimer_delete 236
|
||||
#define TARGET_FREEBSD_NR_ktimer_settime 237
|
||||
#define TARGET_FREEBSD_NR_ktimer_gettime 238
|
||||
#define TARGET_FREEBSD_NR_ktimer_getoverrun 239
|
||||
#define TARGET_FREEBSD_NR_nanosleep 240
|
||||
#define TARGET_FREEBSD_NR_ntp_gettime 248
|
||||
#define TARGET_FREEBSD_NR_minherit 250
|
||||
#define TARGET_FREEBSD_NR_rfork 251
|
||||
#define TARGET_FREEBSD_NR_openbsd_poll 252
|
||||
#define TARGET_FREEBSD_NR_issetugid 253
|
||||
#define TARGET_FREEBSD_NR_lchown 254
|
||||
#define TARGET_FREEBSD_NR_aio_read 255
|
||||
#define TARGET_FREEBSD_NR_aio_write 256
|
||||
#define TARGET_FREEBSD_NR_lio_listio 257
|
||||
#define TARGET_FREEBSD_NR_getdents 272
|
||||
#define TARGET_FREEBSD_NR_lchmod 274
|
||||
#define TARGET_FREEBSD_NR_netbsd_lchown 275
|
||||
#define TARGET_FREEBSD_NR_lutimes 276
|
||||
#define TARGET_FREEBSD_NR_netbsd_msync 277
|
||||
#define TARGET_FREEBSD_NR_nstat 278
|
||||
#define TARGET_FREEBSD_NR_nfstat 279
|
||||
#define TARGET_FREEBSD_NR_nlstat 280
|
||||
#define TARGET_FREEBSD_NR_preadv 289
|
||||
#define TARGET_FREEBSD_NR_pwritev 290
|
||||
#define TARGET_FREEBSD_NR_freebsd4_fhstatfs 297
|
||||
#define TARGET_FREEBSD_NR_fhopen 298
|
||||
#define TARGET_FREEBSD_NR_fhstat 299
|
||||
#define TARGET_FREEBSD_NR_modnext 300
|
||||
#define TARGET_FREEBSD_NR_modstat 301
|
||||
#define TARGET_FREEBSD_NR_modfnext 302
|
||||
#define TARGET_FREEBSD_NR_modfind 303
|
||||
#define TARGET_FREEBSD_NR_kldload 304
|
||||
#define TARGET_FREEBSD_NR_kldunload 305
|
||||
#define TARGET_FREEBSD_NR_kldfind 306
|
||||
#define TARGET_FREEBSD_NR_kldnext 307
|
||||
#define TARGET_FREEBSD_NR_kldstat 308
|
||||
#define TARGET_FREEBSD_NR_kldfirstmod 309
|
||||
#define TARGET_FREEBSD_NR_getsid 310
|
||||
#define TARGET_FREEBSD_NR_setresuid 311
|
||||
#define TARGET_FREEBSD_NR_setresgid 312
|
||||
#define TARGET_FREEBSD_NR_aio_return 314
|
||||
#define TARGET_FREEBSD_NR_aio_suspend 315
|
||||
#define TARGET_FREEBSD_NR_aio_cancel 316
|
||||
#define TARGET_FREEBSD_NR_aio_error 317
|
||||
#define TARGET_FREEBSD_NR_oaio_read 318
|
||||
#define TARGET_FREEBSD_NR_oaio_write 319
|
||||
#define TARGET_FREEBSD_NR_olio_listio 320
|
||||
#define TARGET_FREEBSD_NR_yield 321
|
||||
#define TARGET_FREEBSD_NR_mlockall 324
|
||||
#define TARGET_FREEBSD_NR_munlockall 325
|
||||
#define TARGET_FREEBSD_NR___getcwd 326
|
||||
#define TARGET_FREEBSD_NR_sched_setparam 327
|
||||
#define TARGET_FREEBSD_NR_sched_getparam 328
|
||||
#define TARGET_FREEBSD_NR_sched_setscheduler 329
|
||||
#define TARGET_FREEBSD_NR_sched_getscheduler 330
|
||||
#define TARGET_FREEBSD_NR_sched_yield 331
|
||||
#define TARGET_FREEBSD_NR_sched_get_priority_max 332
|
||||
#define TARGET_FREEBSD_NR_sched_get_priority_min 333
|
||||
#define TARGET_FREEBSD_NR_sched_rr_get_interval 334
|
||||
#define TARGET_FREEBSD_NR_utrace 335
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sendfile 336
|
||||
#define TARGET_FREEBSD_NR_kldsym 337
|
||||
#define TARGET_FREEBSD_NR_jail 338
|
||||
#define TARGET_FREEBSD_NR_sigprocmask 340
|
||||
#define TARGET_FREEBSD_NR_sigsuspend 341
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sigaction 342
|
||||
#define TARGET_FREEBSD_NR_sigpending 343
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sigreturn 344
|
||||
#define TARGET_FREEBSD_NR_sigtimedwait 345
|
||||
#define TARGET_FREEBSD_NR_sigwaitinfo 346
|
||||
#define TARGET_FREEBSD_NR___acl_get_file 347
|
||||
#define TARGET_FREEBSD_NR___acl_set_file 348
|
||||
#define TARGET_FREEBSD_NR___acl_get_fd 349
|
||||
#define TARGET_FREEBSD_NR___acl_set_fd 350
|
||||
#define TARGET_FREEBSD_NR___acl_delete_file 351
|
||||
#define TARGET_FREEBSD_NR___acl_delete_fd 352
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_file 353
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354
|
||||
#define TARGET_FREEBSD_NR_extattrctl 355
|
||||
#define TARGET_FREEBSD_NR_extattr_set_file 356
|
||||
#define TARGET_FREEBSD_NR_extattr_get_file 357
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_file 358
|
||||
#define TARGET_FREEBSD_NR_aio_waitcomplete 359
|
||||
#define TARGET_FREEBSD_NR_getresuid 360
|
||||
#define TARGET_FREEBSD_NR_getresgid 361
|
||||
#define TARGET_FREEBSD_NR_kqueue 362
|
||||
#define TARGET_FREEBSD_NR_kevent 363
|
||||
#define TARGET_FREEBSD_NR_extattr_set_fd 371
|
||||
#define TARGET_FREEBSD_NR_extattr_get_fd 372
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_fd 373
|
||||
#define TARGET_FREEBSD_NR___setugid 374
|
||||
#define TARGET_FREEBSD_NR_nfsclnt 375
|
||||
#define TARGET_FREEBSD_NR_eaccess 376
|
||||
#define TARGET_FREEBSD_NR_nmount 378
|
||||
#define TARGET_FREEBSD_NR___mac_get_proc 384
|
||||
#define TARGET_FREEBSD_NR___mac_set_proc 385
|
||||
#define TARGET_FREEBSD_NR___mac_get_fd 386
|
||||
#define TARGET_FREEBSD_NR___mac_get_file 387
|
||||
#define TARGET_FREEBSD_NR___mac_set_fd 388
|
||||
#define TARGET_FREEBSD_NR___mac_set_file 389
|
||||
#define TARGET_FREEBSD_NR_kenv 390
|
||||
#define TARGET_FREEBSD_NR_lchflags 391
|
||||
#define TARGET_FREEBSD_NR_uuidgen 392
|
||||
#define TARGET_FREEBSD_NR_sendfile 393
|
||||
#define TARGET_FREEBSD_NR_mac_syscall 394
|
||||
#define TARGET_FREEBSD_NR_getfsstat 395
|
||||
#define TARGET_FREEBSD_NR_statfs 396
|
||||
#define TARGET_FREEBSD_NR_fstatfs 397
|
||||
#define TARGET_FREEBSD_NR_fhstatfs 398
|
||||
#define TARGET_FREEBSD_NR_ksem_close 400
|
||||
#define TARGET_FREEBSD_NR_ksem_post 401
|
||||
#define TARGET_FREEBSD_NR_ksem_wait 402
|
||||
#define TARGET_FREEBSD_NR_ksem_trywait 403
|
||||
#define TARGET_FREEBSD_NR_ksem_init 404
|
||||
#define TARGET_FREEBSD_NR_ksem_open 405
|
||||
#define TARGET_FREEBSD_NR_ksem_unlink 406
|
||||
#define TARGET_FREEBSD_NR_ksem_getvalue 407
|
||||
#define TARGET_FREEBSD_NR_ksem_destroy 408
|
||||
#define TARGET_FREEBSD_NR___mac_get_pid 409
|
||||
#define TARGET_FREEBSD_NR___mac_get_link 410
|
||||
#define TARGET_FREEBSD_NR___mac_set_link 411
|
||||
#define TARGET_FREEBSD_NR_extattr_set_link 412
|
||||
#define TARGET_FREEBSD_NR_extattr_get_link 413
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_link 414
|
||||
#define TARGET_FREEBSD_NR___mac_execve 415
|
||||
#define TARGET_FREEBSD_NR_sigaction 416
|
||||
#define TARGET_FREEBSD_NR_sigreturn 417
|
||||
#define TARGET_FREEBSD_NR_getcontext 421
|
||||
#define TARGET_FREEBSD_NR_setcontext 422
|
||||
#define TARGET_FREEBSD_NR_swapcontext 423
|
||||
#define TARGET_FREEBSD_NR_swapoff 424
|
||||
#define TARGET_FREEBSD_NR___acl_get_link 425
|
||||
#define TARGET_FREEBSD_NR___acl_set_link 426
|
||||
#define TARGET_FREEBSD_NR___acl_delete_link 427
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_link 428
|
||||
#define TARGET_FREEBSD_NR_sigwait 429
|
||||
#define TARGET_FREEBSD_NR_thr_create 430
|
||||
#define TARGET_FREEBSD_NR_thr_exit 431
|
||||
#define TARGET_FREEBSD_NR_thr_self 432
|
||||
#define TARGET_FREEBSD_NR_thr_kill 433
|
||||
#define TARGET_FREEBSD_NR__umtx_lock 434
|
||||
#define TARGET_FREEBSD_NR__umtx_unlock 435
|
||||
#define TARGET_FREEBSD_NR_jail_attach 436
|
||||
#define TARGET_FREEBSD_NR_extattr_list_fd 437
|
||||
#define TARGET_FREEBSD_NR_extattr_list_file 438
|
||||
#define TARGET_FREEBSD_NR_extattr_list_link 439
|
||||
#define TARGET_FREEBSD_NR_ksem_timedwait 441
|
||||
#define TARGET_FREEBSD_NR_thr_suspend 442
|
||||
#define TARGET_FREEBSD_NR_thr_wake 443
|
||||
#define TARGET_FREEBSD_NR_kldunloadf 444
|
||||
#define TARGET_FREEBSD_NR_audit 445
|
||||
#define TARGET_FREEBSD_NR_auditon 446
|
||||
#define TARGET_FREEBSD_NR_getauid 447
|
||||
#define TARGET_FREEBSD_NR_setauid 448
|
||||
#define TARGET_FREEBSD_NR_getaudit 449
|
||||
#define TARGET_FREEBSD_NR_setaudit 450
|
||||
#define TARGET_FREEBSD_NR_getaudit_addr 451
|
||||
#define TARGET_FREEBSD_NR_setaudit_addr 452
|
||||
#define TARGET_FREEBSD_NR_auditctl 453
|
||||
#define TARGET_FREEBSD_NR__umtx_op 454
|
||||
#define TARGET_FREEBSD_NR_thr_new 455
|
||||
#define TARGET_FREEBSD_NR_sigqueue 456
|
||||
#define TARGET_FREEBSD_NR_kmq_open 457
|
||||
#define TARGET_FREEBSD_NR_kmq_setattr 458
|
||||
#define TARGET_FREEBSD_NR_kmq_timedreceive 459
|
||||
#define TARGET_FREEBSD_NR_kmq_timedsend 460
|
||||
#define TARGET_FREEBSD_NR_kmq_notify 461
|
||||
#define TARGET_FREEBSD_NR_kmq_unlink 462
|
||||
#define TARGET_FREEBSD_NR_abort2 463
|
||||
#define TARGET_FREEBSD_NR_thr_set_name 464
|
||||
#define TARGET_FREEBSD_NR_aio_fsync 465
|
||||
#define TARGET_FREEBSD_NR_rtprio_thread 466
|
||||
#define TARGET_FREEBSD_NR_sctp_peeloff 471
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_sendmsg 472
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_recvmsg 474
|
||||
#define TARGET_FREEBSD_NR_pread 475
|
||||
#define TARGET_FREEBSD_NR_pwrite 476
|
||||
#define TARGET_FREEBSD_NR_mmap 477
|
||||
#define TARGET_FREEBSD_NR_lseek 478
|
||||
#define TARGET_FREEBSD_NR_truncate 479
|
||||
#define TARGET_FREEBSD_NR_ftruncate 480
|
||||
#define TARGET_FREEBSD_NR_thr_kill2 481
|
||||
#define TARGET_FREEBSD_NR_shm_open 482
|
||||
#define TARGET_FREEBSD_NR_shm_unlink 483
|
||||
#define TARGET_FREEBSD_NR_cpuset 484
|
||||
#define TARGET_FREEBSD_NR_cpuset_setid 485
|
||||
#define TARGET_FREEBSD_NR_cpuset_getid 486
|
||||
#define TARGET_FREEBSD_NR_cpuset_getaffinity 487
|
||||
#define TARGET_FREEBSD_NR_cpuset_setaffinity 488
|
||||
#define TARGET_FREEBSD_NR_faccessat 489
|
||||
#define TARGET_FREEBSD_NR_fchmodat 490
|
||||
#define TARGET_FREEBSD_NR_fchownat 491
|
||||
#define TARGET_FREEBSD_NR_fexecve 492
|
||||
#define TARGET_FREEBSD_NR_fstatat 493
|
||||
#define TARGET_FREEBSD_NR_futimesat 494
|
||||
#define TARGET_FREEBSD_NR_linkat 495
|
||||
#define TARGET_FREEBSD_NR_mkdirat 496
|
||||
#define TARGET_FREEBSD_NR_mkfifoat 497
|
||||
#define TARGET_FREEBSD_NR_mknodat 498
|
||||
#define TARGET_FREEBSD_NR_openat 499
|
||||
#define TARGET_FREEBSD_NR_readlinkat 500
|
||||
#define TARGET_FREEBSD_NR_renameat 501
|
||||
#define TARGET_FREEBSD_NR_symlinkat 502
|
||||
#define TARGET_FREEBSD_NR_unlinkat 503
|
||||
#define TARGET_FREEBSD_NR_posix_openpt 504
|
||||
|
||||
+4
-2
@@ -43,7 +43,7 @@ unsigned long reserved_va;
|
||||
#endif
|
||||
|
||||
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
|
||||
const char *qemu_uname_release;
|
||||
const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
|
||||
extern char **environ;
|
||||
enum BSDType bsd_type;
|
||||
|
||||
@@ -1003,8 +1003,10 @@ int main(int argc, char **argv)
|
||||
cpu->opaque = ts;
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
cpu_x86_set_cpl(env, 3);
|
||||
|
||||
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
||||
env->hflags |= HF_PE_MASK | HF_CPL_MASK;
|
||||
env->hflags |= HF_PE_MASK;
|
||||
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
|
||||
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||
env->hflags |= HF_OSFXSR_MASK;
|
||||
|
||||
@@ -74,6 +74,66 @@ void mmap_unlock(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *bsd_vmalloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
mmap_lock();
|
||||
/* Use map and mark the pages as used. */
|
||||
p = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
|
||||
if (h2g_valid(p)) {
|
||||
/* Allocated region overlaps guest address space.
|
||||
This may recurse. */
|
||||
abi_ulong addr = h2g(p);
|
||||
page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
|
||||
PAGE_RESERVED);
|
||||
}
|
||||
|
||||
mmap_unlock();
|
||||
return p;
|
||||
}
|
||||
|
||||
void *g_malloc(size_t size)
|
||||
{
|
||||
char * p;
|
||||
size += 16;
|
||||
p = bsd_vmalloc(size);
|
||||
*(size_t *)p = size;
|
||||
return p + 16;
|
||||
}
|
||||
|
||||
/* We use map, which is always zero initialized. */
|
||||
void * g_malloc0(size_t size)
|
||||
{
|
||||
return g_malloc(size);
|
||||
}
|
||||
|
||||
void g_free(void *ptr)
|
||||
{
|
||||
/* FIXME: We should unmark the reserved pages here. However this gets
|
||||
complicated when one target page spans multiple host pages, so we
|
||||
don't bother. */
|
||||
size_t *p;
|
||||
p = (size_t *)((char *)ptr - 16);
|
||||
munmap(p, *p);
|
||||
}
|
||||
|
||||
void *g_realloc(void *ptr, size_t size)
|
||||
{
|
||||
size_t old_size, copy;
|
||||
void *new_ptr;
|
||||
|
||||
if (!ptr)
|
||||
return g_malloc(size);
|
||||
old_size = *(size_t *)((char *)ptr - 16);
|
||||
copy = old_size < size ? old_size : size;
|
||||
new_ptr = g_malloc(size);
|
||||
memcpy(new_ptr, ptr, copy);
|
||||
g_free(ptr);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
/* NOTE: all the constants are the HOST ones, but addresses are target. */
|
||||
int target_mprotect(abi_ulong start, abi_ulong len, int prot)
|
||||
{
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
/*
|
||||
* qemu bsd user mode definition
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef QEMU_H
|
||||
#define QEMU_H
|
||||
|
||||
@@ -21,7 +5,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#undef DEBUG_REMAP
|
||||
#ifdef DEBUG_REMAP
|
||||
@@ -166,16 +149,6 @@ void fork_end(int child);
|
||||
#include "qemu/log.h"
|
||||
|
||||
/* strace.c */
|
||||
struct syscallname {
|
||||
int nr;
|
||||
const char *name;
|
||||
const char *format;
|
||||
void (*call)(const struct syscallname *,
|
||||
abi_long, abi_long, abi_long,
|
||||
abi_long, abi_long, abi_long);
|
||||
void (*result)(const struct syscallname *, abi_long);
|
||||
};
|
||||
|
||||
void
|
||||
print_freebsd_syscall(int num,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
|
||||
+58
-109
@@ -1,71 +1,37 @@
|
||||
/*
|
||||
* System call tracing and debugging
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/ioccom.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
int do_strace;
|
||||
int do_strace=0;
|
||||
|
||||
struct syscallname {
|
||||
int nr;
|
||||
const char *name;
|
||||
const char *format;
|
||||
void (*call)(const struct syscallname *,
|
||||
abi_long, abi_long, abi_long,
|
||||
abi_long, abi_long, abi_long);
|
||||
void (*result)(const struct syscallname *, abi_long);
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility functions
|
||||
*/
|
||||
|
||||
static void print_sysctl(const struct syscallname *name, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5,
|
||||
abi_long arg6)
|
||||
{
|
||||
uint32_t i;
|
||||
int32_t *namep;
|
||||
|
||||
gemu_log("%s({ ", name->name);
|
||||
namep = lock_user(VERIFY_READ, arg1, sizeof(int32_t) * arg2, 1);
|
||||
if (namep) {
|
||||
int32_t *p = namep;
|
||||
|
||||
for (i = 0; i < (uint32_t)arg2; i++) {
|
||||
gemu_log("%d ", tswap32(*p++));
|
||||
}
|
||||
unlock_user(namep, arg1, 0);
|
||||
}
|
||||
gemu_log("}, %u, 0x" TARGET_ABI_FMT_lx ", 0x" TARGET_ABI_FMT_lx ", 0x"
|
||||
TARGET_ABI_FMT_lx ", 0x" TARGET_ABI_FMT_lx ")",
|
||||
(uint32_t)arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
static void print_execve(const struct syscallname *name, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5,
|
||||
abi_long arg6)
|
||||
static void
|
||||
print_execve(const struct syscallname *name,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
abi_ulong arg_ptr_addr;
|
||||
char *s;
|
||||
|
||||
s = lock_user_string(arg1);
|
||||
if (s == NULL) {
|
||||
if (!(s = lock_user_string(arg1)))
|
||||
return;
|
||||
}
|
||||
gemu_log("%s(\"%s\",{", name->name, s);
|
||||
unlock_user(s, arg1, 0);
|
||||
|
||||
@@ -73,48 +39,29 @@ static void print_execve(const struct syscallname *name, abi_long arg1,
|
||||
abi_ulong *arg_ptr, arg_addr;
|
||||
|
||||
arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1);
|
||||
if (!arg_ptr) {
|
||||
if (!arg_ptr)
|
||||
return;
|
||||
}
|
||||
arg_addr = tswapl(*arg_ptr);
|
||||
unlock_user(arg_ptr, arg_ptr_addr, 0);
|
||||
if (!arg_addr) {
|
||||
if (!arg_addr)
|
||||
break;
|
||||
}
|
||||
if ((s = lock_user_string(arg_addr))) {
|
||||
gemu_log("\"%s\",", s);
|
||||
unlock_user(s, arg_addr, 0);
|
||||
}
|
||||
}
|
||||
gemu_log("NULL})");
|
||||
}
|
||||
|
||||
static void print_ioctl(const struct syscallname *name,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6)
|
||||
{
|
||||
/* Decode the ioctl request */
|
||||
gemu_log("%s(%d, 0x%0lx { IO%s%s GRP:0x%x('%c') CMD:%d LEN:%d }, 0x"
|
||||
TARGET_ABI_FMT_lx ", ...)",
|
||||
name->name,
|
||||
(int)arg1,
|
||||
(unsigned long)arg2,
|
||||
arg2 & IOC_OUT ? "R" : "",
|
||||
arg2 & IOC_IN ? "W" : "",
|
||||
(unsigned)IOCGROUP(arg2),
|
||||
isprint(IOCGROUP(arg2)) ? (char)IOCGROUP(arg2) : '?',
|
||||
(int)arg2 & 0xFF,
|
||||
(int)IOCPARM_LEN(arg2),
|
||||
arg3);
|
||||
gemu_log("NULL})");
|
||||
}
|
||||
|
||||
/*
|
||||
* Variants for the return value output function
|
||||
*/
|
||||
|
||||
static void print_syscall_ret_addr(const struct syscallname *name, abi_long ret)
|
||||
static void
|
||||
print_syscall_ret_addr(const struct syscallname *name, abi_long ret)
|
||||
{
|
||||
if (ret == -1) {
|
||||
if( ret == -1 ) {
|
||||
gemu_log(" = -1 errno=%d (%s)\n", errno, strerror(errno));
|
||||
} else {
|
||||
gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret);
|
||||
@@ -143,9 +90,10 @@ static const struct syscallname openbsd_scnames[] = {
|
||||
#include "openbsd/strace.list"
|
||||
};
|
||||
|
||||
static void print_syscall(int num, const struct syscallname *scnames,
|
||||
unsigned int nscnames, abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
static void
|
||||
print_syscall(int num, const struct syscallname *scnames, unsigned int nscnames,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
unsigned int i;
|
||||
const char *format="%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ","
|
||||
@@ -154,37 +102,36 @@ static void print_syscall(int num, const struct syscallname *scnames,
|
||||
|
||||
gemu_log("%d ", getpid() );
|
||||
|
||||
for (i = 0; i < nscnames; i++) {
|
||||
for (i = 0; i < nscnames; i++)
|
||||
if (scnames[i].nr == num) {
|
||||
if (scnames[i].call != NULL) {
|
||||
scnames[i].call(&scnames[i], arg1, arg2, arg3, arg4, arg5,
|
||||
arg6);
|
||||
arg6);
|
||||
} else {
|
||||
/* XXX: this format system is broken because it uses
|
||||
host types and host pointers for strings */
|
||||
if (scnames[i].format != NULL) {
|
||||
if (scnames[i].format != NULL)
|
||||
format = scnames[i].format;
|
||||
}
|
||||
gemu_log(format, scnames[i].name, arg1, arg2, arg3, arg4, arg5,
|
||||
arg6);
|
||||
gemu_log(format, scnames[i].name, arg1, arg2, arg3, arg4,
|
||||
arg5, arg6);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
gemu_log("Unknown syscall %d\n", num);
|
||||
}
|
||||
|
||||
static void print_syscall_ret(int num, abi_long ret,
|
||||
const struct syscallname *scnames, unsigned int nscnames)
|
||||
static void
|
||||
print_syscall_ret(int num, abi_long ret, const struct syscallname *scnames,
|
||||
unsigned int nscnames)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < nscnames; i++) {
|
||||
for (i = 0; i < nscnames; i++)
|
||||
if (scnames[i].nr == num) {
|
||||
if (scnames[i].result != NULL) {
|
||||
scnames[i].result(&scnames[i], ret);
|
||||
} else {
|
||||
if (ret < 0) {
|
||||
if( ret < 0 ) {
|
||||
gemu_log(" = -1 errno=" TARGET_ABI_FMT_ld " (%s)\n", -ret,
|
||||
strerror(-ret));
|
||||
} else {
|
||||
@@ -193,50 +140,52 @@ static void print_syscall_ret(int num, abi_long ret,
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The public interface to this module.
|
||||
*/
|
||||
void print_freebsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
void
|
||||
print_freebsd_syscall(int num,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
|
||||
print_syscall(num, freebsd_scnames, ARRAY_SIZE(freebsd_scnames), arg1, arg2,
|
||||
arg3, arg4, arg5, arg6);
|
||||
print_syscall(num, freebsd_scnames, ARRAY_SIZE(freebsd_scnames),
|
||||
arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
void print_freebsd_syscall_ret(int num, abi_long ret)
|
||||
void
|
||||
print_freebsd_syscall_ret(int num, abi_long ret)
|
||||
{
|
||||
|
||||
print_syscall_ret(num, ret, freebsd_scnames, ARRAY_SIZE(freebsd_scnames));
|
||||
}
|
||||
|
||||
void print_netbsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
void
|
||||
print_netbsd_syscall(int num,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
|
||||
print_syscall(num, netbsd_scnames, ARRAY_SIZE(netbsd_scnames),
|
||||
arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
void print_netbsd_syscall_ret(int num, abi_long ret)
|
||||
void
|
||||
print_netbsd_syscall_ret(int num, abi_long ret)
|
||||
{
|
||||
|
||||
print_syscall_ret(num, ret, netbsd_scnames, ARRAY_SIZE(netbsd_scnames));
|
||||
}
|
||||
|
||||
void print_openbsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
void
|
||||
print_openbsd_syscall(int num,
|
||||
abi_long arg1, abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
|
||||
print_syscall(num, openbsd_scnames, ARRAY_SIZE(openbsd_scnames), arg1, arg2,
|
||||
arg3, arg4, arg5, arg6);
|
||||
print_syscall(num, openbsd_scnames, ARRAY_SIZE(openbsd_scnames),
|
||||
arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
void print_openbsd_syscall_ret(int num, abi_long ret)
|
||||
void
|
||||
print_openbsd_syscall_ret(int num, abi_long ret)
|
||||
{
|
||||
|
||||
print_syscall_ret(num, ret, openbsd_scnames, ARRAY_SIZE(openbsd_scnames));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user