Compare commits
219
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adba377ea7 | ||
|
|
8fde73e138 | ||
|
|
14d9fb02c2 | ||
|
|
53e4895c98 | ||
|
|
bb485bf2e8 | ||
|
|
02835d5744 | ||
|
|
41ee91810e | ||
|
|
0c60b74a0c | ||
|
|
25351f6a9a | ||
|
|
cf392d2c7c | ||
|
|
3c3d8c6d19 | ||
|
|
62c754e67c | ||
|
|
0fd14a5564 | ||
|
|
7a3cd5ab40 | ||
|
|
8a93721d04 | ||
|
|
b47506f55c | ||
|
|
f0c609dede | ||
|
|
79bd7781dd | ||
|
|
36afdba00a | ||
|
|
63bf1e0ea5 | ||
|
|
23dbc56d22 | ||
|
|
4041945624 | ||
|
|
5019106862 | ||
|
|
f784615221 | ||
|
|
e34feec264 | ||
|
|
ccb08f53d5 | ||
|
|
cb34d1e9e9 | ||
|
|
df9c108acd | ||
|
|
d3cd48a85f | ||
|
|
501da9369c | ||
|
|
7267e51b32 | ||
|
|
d1775fe94a | ||
|
|
d2b987479a | ||
|
|
c2fb0f2870 | ||
|
|
26b51027f9 | ||
|
|
44564f8226 | ||
|
|
2f1eb049df | ||
|
|
b53d8665a2 | ||
|
|
8b17eb6e6c | ||
|
|
e6c55cf7c2 | ||
|
|
41819e90af | ||
|
|
97a0e27e71 | ||
|
|
750336bc90 | ||
|
|
cfa8008cc0 | ||
|
|
d99c4e2d85 | ||
|
|
641c3ec442 | ||
|
|
c2c52728f5 | ||
|
|
759d38652a | ||
|
|
b6f7fbdd1d | ||
|
|
d400b5dc4a | ||
|
|
758c4840c6 | ||
|
|
4b50bd7357 | ||
|
|
4ee5b9c8cb | ||
|
|
ad08cae75c | ||
|
|
dedf4a5f79 | ||
|
|
3c6347ce8c | ||
|
|
e1c8770f56 | ||
|
|
c874837475 | ||
|
|
610ab7bd3d | ||
|
|
7a6088c870 | ||
|
|
ffa3ab0217 | ||
|
|
aeba41549d | ||
|
|
2f59c95f16 | ||
|
|
5ba151f4dc | ||
|
|
cd598d4161 | ||
|
|
04bc6981ca | ||
|
|
818ce8487e | ||
|
|
f6027f805b | ||
|
|
6f6db0c7af | ||
|
|
665f3ad58f | ||
|
|
4854971ac1 | ||
|
|
1786c4225d | ||
|
|
37173f54b7 | ||
|
|
76d1eddbe5 | ||
|
|
b2390c7008 | ||
|
|
6ee0d5fdc7 | ||
|
|
b0a7517c24 | ||
|
|
6b94cfeca8 | ||
|
|
0e748624bd | ||
|
|
bb8b201815 | ||
|
|
ae9b5df877 | ||
|
|
dbd3e4a75c | ||
|
|
0fda3e2d63 | ||
|
|
7dcffbb2bf | ||
|
|
d723971b5d | ||
|
|
1f6bda9301 | ||
|
|
46c5cacbb4 | ||
|
|
95139b786a | ||
|
|
69b7aacc01 | ||
|
|
3102b1a221 | ||
|
|
84321ba2b6 | ||
|
|
9fbc298a47 | ||
|
|
74dd27cecb | ||
|
|
b6760b6203 | ||
|
|
64b210d4d5 | ||
|
|
f6de3526a0 | ||
|
|
7c56952183 | ||
|
|
3c1162e471 | ||
|
|
73d8965bcc | ||
|
|
2003205fd2 | ||
|
|
7abee6c988 | ||
|
|
c4bd2e4cb0 | ||
|
|
0776525e77 | ||
|
|
a7fcb4c5e0 | ||
|
|
8d948a000d | ||
|
|
c75e43b871 | ||
|
|
af443645c3 | ||
|
|
45edb0ca7a | ||
|
|
d92a7683e8 | ||
|
|
68801b7be1 | ||
|
|
609f5bf6fe | ||
|
|
8f0e369a52 | ||
|
|
630ebeffb4 | ||
|
|
a2b4e846b3 | ||
|
|
f217f379a8 | ||
|
|
e83444f71e | ||
|
|
d8aba740f2 | ||
|
|
d34e6f7960 | ||
|
|
5544b7e419 | ||
|
|
7b6444a2e4 | ||
|
|
2b15f410bd | ||
|
|
95f118fa82 | ||
|
|
29e2bbef19 | ||
|
|
a075a3a27e | ||
|
|
25062a7521 | ||
|
|
f93614c936 | ||
|
|
709cc04345 | ||
|
|
ed995c6c2f | ||
|
|
6bbbb937aa | ||
|
|
636fa8aec3 | ||
|
|
1a6ea31052 | ||
|
|
012d778c07 | ||
|
|
dd8f80b83c | ||
|
|
b1a86eb532 | ||
|
|
792a40384f | ||
|
|
0655eeed18 | ||
|
|
5cfd43b79d | ||
|
|
c5dae2f4c5 | ||
|
|
3239a20294 | ||
|
|
a8b7e73901 | ||
|
|
38a55f3070 | ||
|
|
7d09facec7 | ||
|
|
11088abadf | ||
|
|
0fd56fb844 | ||
|
|
8211eeb7d2 | ||
|
|
0414abe04f | ||
|
|
151be4f61f | ||
|
|
a290aeebc4 | ||
|
|
7e42cd6f35 | ||
|
|
4f577e9e69 | ||
|
|
6be38ee9e7 | ||
|
|
2e191f8e54 | ||
|
|
91ae1d30ec | ||
|
|
0a77a92d74 | ||
|
|
8b8dd2c4b5 | ||
|
|
248de52cf8 | ||
|
|
ba014af39c | ||
|
|
d689974b51 | ||
|
|
e50218c269 | ||
|
|
fa98e47a25 | ||
|
|
ff51a1d589 | ||
|
|
5444df1581 | ||
|
|
e498311693 | ||
|
|
4736fb34f7 | ||
|
|
6d0a48acd8 | ||
|
|
5e5d4fc68e | ||
|
|
68e3bb1128 | ||
|
|
c885105bf3 | ||
|
|
2cd72adb1c | ||
|
|
819ddf7d1f | ||
|
|
ec6428b598 | ||
|
|
424388980d | ||
|
|
6b579c8c53 | ||
|
|
47c6edce7a | ||
|
|
a5221ee143 | ||
|
|
30a0fc3607 | ||
|
|
ad0a6444ad | ||
|
|
6b7ed87665 | ||
|
|
b54720b5d6 | ||
|
|
c426a2da12 | ||
|
|
15a14f2eeb | ||
|
|
88d08de7e5 | ||
|
|
109b2439f0 | ||
|
|
c2f6dc66bc | ||
|
|
dc9e1e798c | ||
|
|
02e1c55ddd | ||
|
|
9692bad34d | ||
|
|
6ec62b79e3 | ||
|
|
0e282aca86 | ||
|
|
75b4b747a2 | ||
|
|
0bc4142e7f | ||
|
|
b9cabc36a2 | ||
|
|
03bc4f6628 | ||
|
|
8b6d92a565 | ||
|
|
44c68b84ae | ||
|
|
4c3e00d83f | ||
|
|
6a108c4802 | ||
|
|
e480a1b8ff | ||
|
|
29b0fcc181 | ||
|
|
b8fca09eec | ||
|
|
50a203c3b9 | ||
|
|
f227ed1842 | ||
|
|
2dc7975300 | ||
|
|
8fa58fe910 | ||
|
|
97f74de48c | ||
|
|
30a08ab4e1 | ||
|
|
df3e347891 | ||
|
|
810766d9dd | ||
|
|
3220207c27 | ||
|
|
def56d28cf | ||
|
|
478f1f6ccf | ||
|
|
8f08550ee2 | ||
|
|
e6c007056c | ||
|
|
e84e23de35 | ||
|
|
40699a469e | ||
|
|
cbf23fdf21 | ||
|
|
a9b9ca7e0e | ||
|
|
d765275bb1 | ||
|
|
f47542925e |
+80
-75
@@ -1,67 +1,68 @@
|
||||
/config-devices.*
|
||||
/config-all-devices.*
|
||||
/config-all-disas.*
|
||||
/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-ust-provider.h
|
||||
/trace/generated-ust.c
|
||||
/libcacard/trace/generated-tracers.c
|
||||
config-devices.*
|
||||
config-all-devices.*
|
||||
config-all-disas.*
|
||||
config-host.*
|
||||
config-target.*
|
||||
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
|
||||
libcacard/trace/generated-tracers.c
|
||||
*-timestamp
|
||||
/*-softmmu
|
||||
/*-darwin-user
|
||||
/*-linux-user
|
||||
/*-bsd-user
|
||||
/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
|
||||
/qemu-tech.html
|
||||
/qemu-doc.info
|
||||
/qemu-tech.info
|
||||
/qemu.1
|
||||
/qemu.pod
|
||||
/qemu-img.1
|
||||
/qemu-img.pod
|
||||
/qemu-img
|
||||
/qemu-nbd
|
||||
/qemu-nbd.8
|
||||
/qemu-nbd.pod
|
||||
/qemu-options.def
|
||||
/qemu-options.texi
|
||||
/qemu-img-cmds.texi
|
||||
/qemu-img-cmds.h
|
||||
/qemu-io
|
||||
/qemu-ga
|
||||
/qemu-bridge-helper
|
||||
/qemu-monitor.texi
|
||||
/qmp-commands.txt
|
||||
/vscclient
|
||||
/fsdev/virtfs-proxy-helper
|
||||
/fsdev/virtfs-proxy-helper.1
|
||||
/fsdev/virtfs-proxy-helper.pod
|
||||
*-softmmu
|
||||
*-darwin-user
|
||||
*-linux-user
|
||||
*-bsd-user
|
||||
libdis*
|
||||
libuser
|
||||
linux-headers/asm
|
||||
qapi-generated
|
||||
qapi-types.[ch]
|
||||
qapi-visit.[ch]
|
||||
qmp-commands.h
|
||||
qmp-marshal.c
|
||||
qemu-doc.html
|
||||
qemu-tech.html
|
||||
qemu-doc.info
|
||||
qemu-tech.info
|
||||
qemu.1
|
||||
qemu.pod
|
||||
qemu-img.1
|
||||
qemu-img.pod
|
||||
qemu-img
|
||||
qemu-nbd
|
||||
qemu-nbd.8
|
||||
qemu-nbd.pod
|
||||
qemu-options.def
|
||||
qemu-options.texi
|
||||
qemu-img-cmds.texi
|
||||
qemu-img-cmds.h
|
||||
qemu-io
|
||||
qemu-ga
|
||||
qemu-bridge-helper
|
||||
qemu-monitor.texi
|
||||
vscclient
|
||||
qmp-commands.txt
|
||||
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
|
||||
*.dvi
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.mo
|
||||
*.fn
|
||||
*.ky
|
||||
*.log
|
||||
@@ -75,31 +76,35 @@
|
||||
*.tp
|
||||
*.vr
|
||||
*.d
|
||||
!/scripts/qemu-guest-agent/fsfreeze-hook.d
|
||||
!scripts/qemu-guest-agent/fsfreeze-hook.d
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.pc
|
||||
.libs
|
||||
.sdk
|
||||
*.swp
|
||||
*.orig
|
||||
.pc
|
||||
*.gcda
|
||||
*.gcno
|
||||
/pc-bios/bios-pq/status
|
||||
/pc-bios/vgabios-pq/status
|
||||
/pc-bios/optionrom/linuxboot.asm
|
||||
/pc-bios/optionrom/linuxboot.bin
|
||||
/pc-bios/optionrom/linuxboot.raw
|
||||
/pc-bios/optionrom/linuxboot.img
|
||||
/pc-bios/optionrom/multiboot.asm
|
||||
/pc-bios/optionrom/multiboot.bin
|
||||
/pc-bios/optionrom/multiboot.raw
|
||||
/pc-bios/optionrom/multiboot.img
|
||||
/pc-bios/optionrom/kvmvapic.asm
|
||||
/pc-bios/optionrom/kvmvapic.bin
|
||||
/pc-bios/optionrom/kvmvapic.raw
|
||||
/pc-bios/optionrom/kvmvapic.img
|
||||
/pc-bios/s390-ccw/s390-ccw.elf
|
||||
/pc-bios/s390-ccw/s390-ccw.img
|
||||
patches
|
||||
pc-bios/bios-pq/status
|
||||
pc-bios/vgabios-pq/status
|
||||
pc-bios/optionrom/linuxboot.asm
|
||||
pc-bios/optionrom/linuxboot.bin
|
||||
pc-bios/optionrom/linuxboot.raw
|
||||
pc-bios/optionrom/linuxboot.img
|
||||
pc-bios/optionrom/multiboot.asm
|
||||
pc-bios/optionrom/multiboot.bin
|
||||
pc-bios/optionrom/multiboot.raw
|
||||
pc-bios/optionrom/multiboot.img
|
||||
pc-bios/optionrom/kvmvapic.asm
|
||||
pc-bios/optionrom/kvmvapic.bin
|
||||
pc-bios/optionrom/kvmvapic.raw
|
||||
pc-bios/optionrom/kvmvapic.img
|
||||
pc-bios/s390-ccw/s390-ccw.elf
|
||||
pc-bios/s390-ccw/s390-ccw.img
|
||||
.stgit-*
|
||||
cscope.*
|
||||
tags
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = git://git.qemu-project.org/openbios.git
|
||||
[submodule "roms/openhackware"]
|
||||
path = roms/openhackware
|
||||
url = git://git.qemu-project.org/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = git://github.com/rth7680/qemu-palcode.git
|
||||
@@ -28,6 +25,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
|
||||
|
||||
+24
-34
@@ -4,12 +4,6 @@ python:
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "irc.oftc.net#qemu"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
env:
|
||||
global:
|
||||
- TEST_CMD="make check"
|
||||
@@ -20,23 +14,22 @@ env:
|
||||
- GUI_PKGS="libgtk-3-dev libvte-2.90-dev libsdl1.2-dev libpng12-dev libpixman-1-dev"
|
||||
- EXTRA_PKGS=""
|
||||
matrix:
|
||||
- TARGETS=alpha-softmmu,alpha-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=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
|
||||
- TARGETS=alpha-softmmu,alpha-linux-user
|
||||
- TARGETS=arm-softmmu,arm-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=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:
|
||||
- git submodule update --init --recursive
|
||||
- sudo apt-get update -qq
|
||||
@@ -52,10 +45,6 @@ matrix:
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
compiler: gcc
|
||||
# All the extra -dev packages
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_PKGS="libaio-dev libcap-ng-dev libattr1-dev libbrlapi-dev uuid-dev libusb-1.0.0-dev"
|
||||
compiler: gcc
|
||||
# Currently configure doesn't force --disable-pie
|
||||
- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
EXTRA_CONFIG="--enable-gprof --enable-gcov --disable-pie"
|
||||
@@ -66,16 +55,17 @@ 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"
|
||||
compiler: gcc
|
||||
# This disabled make check for the ftrace backend which needs more setting up
|
||||
# Currently broken on 12.04 due to mis-packaged liburcu and changed API, will be pulled.
|
||||
#- env: TARGETS=i386-softmmu,x86_64-softmmu
|
||||
# EXTRA_PKGS="liblttng-ust-dev liburcu-dev"
|
||||
# EXTRA_CONFIG="--enable-trace-backend=ust"
|
||||
|
||||
@@ -84,10 +84,3 @@ and clarity it comes on a line by itself:
|
||||
Rationale: a consistent (except for functions...) bracing style reduces
|
||||
ambiguity and avoids needless churn when lines are added or removed.
|
||||
Furthermore, it is the QEMU coding style.
|
||||
|
||||
5. Declarations
|
||||
|
||||
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.
|
||||
|
||||
+16
-113
@@ -52,13 +52,6 @@ General Project Administration
|
||||
------------------------------
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
|
||||
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):
|
||||
----------------------
|
||||
Alpha
|
||||
@@ -165,6 +158,7 @@ Guest CPU Cores (KVM):
|
||||
----------------------
|
||||
|
||||
Overall
|
||||
M: Gleb Natapov <gleb@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
@@ -176,25 +170,18 @@ 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
|
||||
F: target-ppc/kvm.c
|
||||
|
||||
S390
|
||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
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.[hc]
|
||||
|
||||
X86
|
||||
M: Gleb Natapov <gleb@redhat.com>
|
||||
M: Marcelo Tosatti <mtosatti@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
@@ -232,13 +219,6 @@ F: *win32*
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
Allwinner-a10
|
||||
M: Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
S: Maintained
|
||||
F: hw/*/allwinner-a10*
|
||||
F: include/hw/*/allwinner-a10*
|
||||
F: hw/arm/cubieboard.c
|
||||
|
||||
Exynos
|
||||
M: Evgeny Voevodin <e.voevodin@samsung.com>
|
||||
M: Maksim Kozlov <m.kozlov@samsung.com>
|
||||
@@ -248,17 +228,11 @@ 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
|
||||
|
||||
Canon DIGIC
|
||||
M: Antony Pavlov <antonynpavlov@gmail.com>
|
||||
S: Maintained
|
||||
F: include/hw/arm/digic.h
|
||||
F: hw/*/digic*
|
||||
|
||||
Gumstix
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
@@ -316,7 +290,7 @@ S: Maintained
|
||||
F: hw/*/versatile*
|
||||
|
||||
Xilinx Zynq
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
S: Maintained
|
||||
F: hw/arm/xilinx_zynq.c
|
||||
F: hw/misc/zynq_slcr.c
|
||||
@@ -365,7 +339,7 @@ S: Maintained
|
||||
F: hw/microblaze/petalogix_s3adsp1800_mmu.c
|
||||
|
||||
petalogix_ml605
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
S: Maintained
|
||||
F: hw/microblaze/petalogix_ml605_mmu.c
|
||||
|
||||
@@ -508,13 +482,10 @@ F: hw/s390x/s390-*.c
|
||||
|
||||
S390 Virtio-ccw
|
||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Supported
|
||||
F: hw/s390x/s390-virtio-ccw.c
|
||||
F: hw/s390x/css.[hc]
|
||||
F: hw/s390x/sclp*.[hc]
|
||||
F: hw/s390x/ipl*.[hc]
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
UniCore32 Machines
|
||||
@@ -529,23 +500,9 @@ X86 Machines
|
||||
------------
|
||||
PC
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/i386/
|
||||
F: hw/i386/
|
||||
F: hw/pci-host/piix.c
|
||||
F: hw/pci-host/q35.c
|
||||
F: hw/pci-host/pam.c
|
||||
F: include/hw/pci-host/q35.h
|
||||
F: include/hw/pci-host/pam.h
|
||||
F: hw/isa/piix4.c
|
||||
F: hw/isa/lpc_ich9.c
|
||||
F: hw/i2c/smbus_ich9.c
|
||||
F: hw/acpi/piix4.c
|
||||
F: hw/acpi/ich9.c
|
||||
F: include/hw/acpi/ich9.h
|
||||
F: include/hw/acpi/piix.h
|
||||
|
||||
F: hw/i386/pc.[ch]
|
||||
F: hw/i386/pc_piix.c
|
||||
|
||||
Xtensa Machines
|
||||
---------------
|
||||
@@ -563,7 +520,6 @@ 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/
|
||||
@@ -605,7 +561,7 @@ S: Orphan
|
||||
F: hw/scsi/lsi53c895a.c
|
||||
|
||||
SSI
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
S: Maintained
|
||||
F: hw/ssi/*
|
||||
F: hw/block/m25p80.c
|
||||
@@ -614,7 +570,6 @@ USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb/*
|
||||
F: tests/usb-hcd-ehci-test.c
|
||||
|
||||
VFIO
|
||||
M: Alex Williamson <alex.williamson@redhat.com>
|
||||
@@ -628,7 +583,6 @@ F: hw/*/*vhost*
|
||||
|
||||
virtio
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: hw/*/virtio*
|
||||
|
||||
@@ -637,7 +591,6 @@ M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
S: Supported
|
||||
F: hw/9pfs/
|
||||
F: fsdev/
|
||||
F: tests/virtio-9p-test.c
|
||||
T: git git://github.com/kvaneesh/QEMU.git
|
||||
|
||||
virtio-blk
|
||||
@@ -648,7 +601,6 @@ F: hw/block/virtio-blk.c
|
||||
|
||||
virtio-ccw
|
||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/virtio-ccw.[hc]
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
@@ -663,16 +615,9 @@ nvme
|
||||
M: Keith Busch <keith.busch@intel.com>
|
||||
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: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/*/xilinx_*
|
||||
@@ -686,9 +631,6 @@ 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>
|
||||
@@ -697,8 +639,6 @@ S: Supported
|
||||
F: block*
|
||||
F: block/
|
||||
F: hw/block/
|
||||
F: qemu-img*
|
||||
F: qemu-io*
|
||||
T: git git://repo.or.cz/qemu/kevin.git block
|
||||
T: git git://github.com/stefanha/qemu.git block
|
||||
|
||||
@@ -721,7 +661,7 @@ F: include/hw/cpu/icc_bus.h
|
||||
F: hw/cpu/icc_bus.c
|
||||
|
||||
Device Tree
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: device_tree.[ch]
|
||||
@@ -732,16 +672,6 @@ 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
|
||||
@@ -752,13 +682,11 @@ F: hw/display/qxl*
|
||||
|
||||
Graphics
|
||||
M: Anthony Liguori <aliguori@amazon.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: ui/
|
||||
|
||||
Cocoa graphics
|
||||
M: Andreas Färber <andreas.faerber@web.de>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Odd Fixes
|
||||
F: ui/cocoa.m
|
||||
|
||||
@@ -769,7 +697,7 @@ F: vl.c
|
||||
|
||||
Human Monitor (HMP)
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: monitor.c
|
||||
F: hmp.c
|
||||
F: hmp-commands.hx
|
||||
@@ -782,14 +710,6 @@ S: Maintained
|
||||
F: net/
|
||||
T: git git://github.com/stefanha/qemu.git net
|
||||
|
||||
Netmap network backend
|
||||
M: Luigi Rizzo <rizzo@iet.unipi.it>
|
||||
M: Giuseppe Lettieri <g.lettieri@iet.unipi.it>
|
||||
M: Vincenzo Maffione <v.maffione@gmail.com>
|
||||
W: http://info.iet.unipi.it/~luigi/netmap/
|
||||
S: Maintained
|
||||
F: net/netmap.c
|
||||
|
||||
Network Block Device (NBD)
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Odd Fixes
|
||||
@@ -801,7 +721,7 @@ T: git git://github.com/bonzini/qemu.git nbd-next
|
||||
QAPI
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: qapi/
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
@@ -813,20 +733,9 @@ S: Supported
|
||||
F: qapi-schema.json
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
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
|
||||
S: Supported
|
||||
F: qmp.c
|
||||
F: monitor.c
|
||||
F: qmp-commands.hx
|
||||
@@ -854,7 +763,7 @@ S: Odd Fixes
|
||||
F: scripts/checkpatch.pl
|
||||
|
||||
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
|
||||
@@ -970,7 +879,6 @@ F: block/rbd.c
|
||||
Sheepdog
|
||||
M: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
|
||||
M: Liu Yuan <namei.unix@gmail.com>
|
||||
L: sheepdog@lists.wpkg.org
|
||||
S: Supported
|
||||
F: block/sheepdog.c
|
||||
|
||||
@@ -991,11 +899,6 @@ M: Peter Lieven <pl@kamp.de>
|
||||
S: Supported
|
||||
F: block/iscsi.c
|
||||
|
||||
NFS
|
||||
M: Peter Lieven <pl@kamp.de>
|
||||
S: Maintained
|
||||
F: block/nfs.c
|
||||
|
||||
SSH
|
||||
M: Richard W.M. Jones <rjones@redhat.com>
|
||||
S: Supported
|
||||
|
||||
@@ -45,23 +45,18 @@ 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
|
||||
|
||||
ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust)
|
||||
GENERATED_HEADERS += trace/generated-ust-provider.h
|
||||
GENERATED_SOURCES += trace/generated-ust.c
|
||||
endif
|
||||
|
||||
# Don't try to regenerate Makefile or configure
|
||||
# We don't generate any of them
|
||||
Makefile: ;
|
||||
@@ -127,26 +122,13 @@ defconfig:
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
endif
|
||||
|
||||
dummy := $(call unnest-vars,, \
|
||||
stub-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/tests/Makefile
|
||||
endif
|
||||
ifeq ($(CONFIG_SMARTCARD_NSS),y)
|
||||
include $(SRC_PATH)/libcacard/Makefile
|
||||
endif
|
||||
|
||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
|
||||
|
||||
config-host.h: config-host.h-timestamp
|
||||
config-host.h-timestamp: config-host.mak
|
||||
@@ -156,7 +138,6 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||
|
||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||
|
||||
subdir-%:
|
||||
@@ -191,6 +172,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
|
||||
@@ -202,10 +185,7 @@ Makefile: $(version-obj-y) $(version-lobj-y)
|
||||
# Build libraries
|
||||
|
||||
libqemustub.a: $(stub-obj-y)
|
||||
libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o
|
||||
|
||||
block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL
|
||||
util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
|
||||
libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
|
||||
|
||||
######################################################################
|
||||
|
||||
@@ -232,44 +212,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)
|
||||
@@ -281,7 +240,8 @@ clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
find . \( -name '*.l[oa]' -o -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
|
||||
find . -name '*.[oda]' -type f -exec rm -f {} +
|
||||
find . -name '*.l[oa]' -type f -exec rm -f {} +
|
||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -f fsdev/*.pod
|
||||
rm -rf .libs */.libs
|
||||
@@ -330,10 +290,10 @@ common de-ch es fo fr-ca hu ja mk nl-be pt sl tr \
|
||||
bepo cz
|
||||
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
|
||||
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
||||
@@ -344,8 +304,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
|
||||
@@ -386,19 +345,13 @@ 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))
|
||||
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"; \
|
||||
done
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
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 \
|
||||
@@ -413,7 +366,7 @@ endif
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
|
||||
done
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
# various test targets
|
||||
|
||||
+23
-11
@@ -12,15 +12,18 @@ 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 qapi-event.o
|
||||
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
|
||||
block-obj-y += qemu-coroutine-sleep.o
|
||||
block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
|
||||
|
||||
block-obj-m = block/
|
||||
|
||||
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
||||
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
||||
# only pull in the actual virtio-9p device if we also enabled virtio.
|
||||
CONFIG_REALLY_VIRTFS=y
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# smartcard
|
||||
@@ -31,8 +34,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
|
||||
@@ -40,9 +41,9 @@ libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS)
|
||||
# single QEMU executable should support all CPUs and machines.
|
||||
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
common-obj-y = blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += iothread.o
|
||||
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += net/
|
||||
common-obj-y += readline.o
|
||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
@@ -50,8 +51,6 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration.o migration-tcp.o
|
||||
common-obj-y += vmstate.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
|
||||
@@ -66,11 +65,9 @@ common-obj-y += hw/
|
||||
|
||||
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/
|
||||
@@ -113,3 +110,18 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
|
||||
# by libqemuutil.a. These should be moved to a separate .json schema.
|
||||
qga-obj-y = qga/ qapi-types.o qapi-visit.o
|
||||
qga-vss-dll-obj-y = qga/
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||
|
||||
QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
||||
|
||||
nested-vars += \
|
||||
stub-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
common-obj-y
|
||||
dummy := $(call unnest-vars)
|
||||
|
||||
+36
-38
@@ -16,22 +16,19 @@ 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
|
||||
@@ -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,7 +55,7 @@ $(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) \
|
||||
@@ -85,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
|
||||
|
||||
@@ -108,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
|
||||
@@ -119,7 +109,7 @@ 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 += 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
|
||||
@@ -130,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)
|
||||
@@ -142,6 +130,8 @@ else
|
||||
obj-y += hw/$(TARGET_BASE_ARCH)/
|
||||
endif
|
||||
|
||||
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
@@ -149,23 +139,28 @@ endif # CONFIG_SOFTMMU
|
||||
# Workaround for http://gcc.gnu.org/PR55489, see configure.
|
||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
||||
|
||||
dummy := $(call unnest-vars,,obj-y)
|
||||
all-obj-y := $(obj-y)
|
||||
nested-vars += obj-y
|
||||
|
||||
block-obj-y :=
|
||||
common-obj-y :=
|
||||
# This resolves all nested paths, so it must come last
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
dummy := $(call unnest-vars,.., \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
all-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
|
||||
all-obj-y = $(obj-y)
|
||||
all-obj-y += $(addprefix ../, $(common-obj-y))
|
||||
|
||||
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)$@")
|
||||
@@ -186,7 +181,10 @@ 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"
|
||||
|
||||
+8
-35
@@ -125,7 +125,7 @@ static bool aio_dispatch(AioContext *ctx)
|
||||
bool progress = false;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
@@ -175,56 +175,27 @@ static 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 happens
|
||||
* in two cases:
|
||||
*
|
||||
* 1) when aio_poll is called with blocking == false
|
||||
*
|
||||
* 2) when we are called after poll(). If we are called before
|
||||
* poll(), bottom halves will not be re-evaluated and we need
|
||||
* aio_notify() if blocking == true.
|
||||
*
|
||||
* The first aio_dispatch() only does something when AioContext is
|
||||
* running as a GSource, and in that case aio_poll is used only
|
||||
* with blocking == false, so this optimization is already quite
|
||||
* effective. However, the code is ugly and should be restructured
|
||||
* to have a single aio_dispatch() call. To do this, we need to
|
||||
* reorganize aio_poll into a prepare/poll/dispatch model like
|
||||
* glib's.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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).
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/* Re-evaluate condition (1) above. */
|
||||
aio_set_dispatching(ctx, !blocking);
|
||||
if (aio_dispatch(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
if (progress && !blocking) {
|
||||
goto out;
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx->walking_handlers++;
|
||||
@@ -246,6 +217,11 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* early return if we only have the aio_notify() fd */
|
||||
if (ctx->pollfds->len == 1) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
ret = qemu_poll_ns((GPollFD *)ctx->pollfds->data,
|
||||
ctx->pollfds->len,
|
||||
@@ -263,12 +239,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;
|
||||
}
|
||||
|
||||
out:
|
||||
aio_set_dispatching(ctx, was_dispatching);
|
||||
return progress;
|
||||
}
|
||||
|
||||
+8
-3
@@ -102,7 +102,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
/*
|
||||
* 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 aio_poll loops).
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
@@ -115,7 +115,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
/*
|
||||
* Then dispatch any pending callbacks from the GSource.
|
||||
*
|
||||
* 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);
|
||||
@@ -161,6 +161,11 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* early return if we only have the aio_notify() fd */
|
||||
if (count == 1) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
while (count > 0) {
|
||||
int ret;
|
||||
@@ -177,7 +182,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
blocking = false;
|
||||
|
||||
/* we have to walk very carefully in case
|
||||
* aio_set_fd_handler is called while we're walking */
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
+132
-325
@@ -45,13 +45,10 @@
|
||||
#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"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
#ifdef DEBUG_ARCH_INIT
|
||||
#define DPRINTF(fmt, ...) \
|
||||
@@ -111,8 +108,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 */
|
||||
|
||||
@@ -125,6 +120,7 @@ static uint64_t bitmap_sync_count;
|
||||
#define RAM_SAVE_FLAG_XBZRLE 0x40
|
||||
/* 0x80 is reserved in migration.h start with 0x100 next */
|
||||
|
||||
|
||||
static struct defconfig_file {
|
||||
const char *filename;
|
||||
/* Indicates it is an user config file (disabled by -no-user-config) */
|
||||
@@ -135,7 +131,6 @@ static struct defconfig_file {
|
||||
{ NULL }, /* end of list */
|
||||
};
|
||||
|
||||
static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
|
||||
|
||||
int qemu_read_default_config_files(bool userconfig)
|
||||
{
|
||||
@@ -167,64 +162,25 @@ static struct {
|
||||
uint8_t *encoded_buf;
|
||||
/* buffer for storing page content */
|
||||
uint8_t *current_buf;
|
||||
/* Cache for XBZRLE, Protected by lock. */
|
||||
/* buffer used for XBZRLE decoding */
|
||||
uint8_t *decoded_buf;
|
||||
/* Cache for XBZRLE */
|
||||
PageCache *cache;
|
||||
QemuMutex lock;
|
||||
} XBZRLE;
|
||||
} XBZRLE = {
|
||||
.encoded_buf = NULL,
|
||||
.current_buf = NULL,
|
||||
.decoded_buf = NULL,
|
||||
.cache = NULL,
|
||||
};
|
||||
|
||||
/* buffer used for XBZRLE decoding */
|
||||
static uint8_t *xbzrle_decoded_buf;
|
||||
|
||||
static void XBZRLE_cache_lock(void)
|
||||
{
|
||||
if (migrate_use_xbzrle())
|
||||
qemu_mutex_lock(&XBZRLE.lock);
|
||||
}
|
||||
|
||||
static void XBZRLE_cache_unlock(void)
|
||||
{
|
||||
if (migrate_use_xbzrle())
|
||||
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;
|
||||
|
||||
if (new_size < TARGET_PAGE_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
|
||||
if (XBZRLE.cache != NULL) {
|
||||
if (pow2floor(new_size) == migrate_xbzrle_cache_size()) {
|
||||
goto out_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;
|
||||
}
|
||||
|
||||
cache_fini(XBZRLE.cache);
|
||||
XBZRLE.cache = new_cache;
|
||||
return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) *
|
||||
TARGET_PAGE_SIZE;
|
||||
}
|
||||
|
||||
out_new_size:
|
||||
ret = pow2floor(new_size);
|
||||
out:
|
||||
XBZRLE_cache_unlock();
|
||||
return ret;
|
||||
return pow2floor(new_size);
|
||||
}
|
||||
|
||||
/* accounting for migration statistics */
|
||||
@@ -236,7 +192,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;
|
||||
|
||||
@@ -292,11 +247,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;
|
||||
@@ -319,37 +269,9 @@ static size_t save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
|
||||
return size;
|
||||
}
|
||||
|
||||
/* This is the last block that we have visited serching for dirty pages
|
||||
*/
|
||||
static RAMBlock *last_seen_block;
|
||||
/* This is the last block from where we have sent data */
|
||||
static RAMBlock *last_sent_block;
|
||||
static ram_addr_t last_offset;
|
||||
static unsigned long *migration_bitmap;
|
||||
static uint64_t migration_dirty_pages;
|
||||
static uint32_t last_version;
|
||||
static bool ram_bulk_stage;
|
||||
|
||||
/* Update the xbzrle cache to reflect a page that's been sent as all 0.
|
||||
* The important thing is that a stale (not-yet-0'd) page be replaced
|
||||
* by the new data.
|
||||
* As a bonus, if the page wasn't in the cache it gets added so that
|
||||
* when a small write is made into the 0'd page it gets XBZRLE sent
|
||||
*/
|
||||
static void xbzrle_cache_zero_page(ram_addr_t current_addr)
|
||||
{
|
||||
if (ram_bulk_stage || !migrate_use_xbzrle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We don't care if this fails to allocate a new cache page
|
||||
* as long as it updated an old one */
|
||||
cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE);
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -357,23 +279,17 @@ 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) {
|
||||
return -1;
|
||||
} else {
|
||||
/* update *current_data when the page has been
|
||||
inserted into cache */
|
||||
*current_data = get_cached_data(XBZRLE.cache, current_addr);
|
||||
}
|
||||
cache_insert(XBZRLE.cache, current_addr, current_data);
|
||||
}
|
||||
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,
|
||||
@@ -386,10 +302,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;
|
||||
}
|
||||
|
||||
@@ -410,6 +323,18 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
|
||||
/* This is the last block that we have visited serching for dirty pages
|
||||
*/
|
||||
static RAMBlock *last_seen_block;
|
||||
/* This is the last block from where we have sent data */
|
||||
static RAMBlock *last_sent_block;
|
||||
static ram_addr_t last_offset;
|
||||
static unsigned long *migration_bitmap;
|
||||
static uint64_t migration_dirty_pages;
|
||||
static uint32_t last_version;
|
||||
static bool ram_bulk_stage;
|
||||
|
||||
static inline
|
||||
ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
|
||||
ram_addr_t start)
|
||||
@@ -434,10 +359,11 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
|
||||
return (next - base) << TARGET_PAGE_BITS;
|
||||
}
|
||||
|
||||
static inline bool migration_bitmap_set_dirty(ram_addr_t addr)
|
||||
static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
|
||||
ram_addr_t offset)
|
||||
{
|
||||
bool ret;
|
||||
int nr = addr >> TARGET_PAGE_BITS;
|
||||
int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
|
||||
|
||||
ret = test_and_set_bit(nr, migration_bitmap);
|
||||
|
||||
@@ -447,47 +373,12 @@ static inline bool migration_bitmap_set_dirty(ram_addr_t addr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS);
|
||||
|
||||
/* start address is aligned at the start of a word? */
|
||||
if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) {
|
||||
int k;
|
||||
int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS);
|
||||
unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION];
|
||||
|
||||
for (k = page; k < page + nr; k++) {
|
||||
if (src[k]) {
|
||||
unsigned long new_dirty;
|
||||
new_dirty = ~migration_bitmap[k];
|
||||
migration_bitmap[k] |= src[k];
|
||||
new_dirty &= src[k];
|
||||
migration_dirty_pages += ctpopl(new_dirty);
|
||||
src[k] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) {
|
||||
if (cpu_physical_memory_get_dirty(start + addr,
|
||||
TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
cpu_physical_memory_reset_dirty(start + addr,
|
||||
TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION);
|
||||
migration_bitmap_set_dirty(start + addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Needs iothread lock! */
|
||||
|
||||
static void migration_bitmap_sync(void)
|
||||
{
|
||||
RAMBlock *block;
|
||||
ram_addr_t addr;
|
||||
uint64_t num_dirty_pages_init = migration_dirty_pages;
|
||||
MigrationState *s = migrate_get_current();
|
||||
static int64_t start_time;
|
||||
@@ -495,10 +386,6 @@ static void migration_bitmap_sync(void)
|
||||
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();
|
||||
@@ -512,7 +399,13 @@ static void migration_bitmap_sync(void)
|
||||
address_space_sync_dirty_bitmap(&address_space_memory);
|
||||
|
||||
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
|
||||
migration_bitmap_sync_range(block->mr->ram_addr, block->length);
|
||||
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
|
||||
if (memory_region_test_and_clear_dirty(block->mr,
|
||||
addr, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
migration_bitmap_set_dirty(block->mr, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
trace_migration_bitmap_sync_end(migration_dirty_pages
|
||||
- num_dirty_pages_init);
|
||||
@@ -540,113 +433,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);
|
||||
@@ -667,7 +476,48 @@ 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;
|
||||
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);
|
||||
|
||||
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++;
|
||||
} else if (!ram_bulk_stage && migrate_use_xbzrle()) {
|
||||
current_addr = block->offset + offset;
|
||||
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
|
||||
offset, cont, last_stage);
|
||||
if (!last_stage) {
|
||||
p = get_cached_data(XBZRLE.cache, current_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* XBZRLE overflow or normal page */
|
||||
if (bytes_sent == -1) {
|
||||
bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
|
||||
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
|
||||
bytes_sent += TARGET_PAGE_SIZE;
|
||||
acct_info.norm_pages++;
|
||||
}
|
||||
|
||||
/* if page is unmodified, continue to the next */
|
||||
if (bytes_sent > 0) {
|
||||
@@ -722,12 +572,6 @@ uint64_t ram_bytes_total(void)
|
||||
return total;
|
||||
}
|
||||
|
||||
void free_xbzrle_decoded_buf(void)
|
||||
{
|
||||
g_free(xbzrle_decoded_buf);
|
||||
xbzrle_decoded_buf = NULL;
|
||||
}
|
||||
|
||||
static void migration_end(void)
|
||||
{
|
||||
if (migration_bitmap) {
|
||||
@@ -736,16 +580,14 @@ static void migration_end(void)
|
||||
migration_bitmap = NULL;
|
||||
}
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
if (XBZRLE.cache) {
|
||||
cache_fini(XBZRLE.cache);
|
||||
g_free(XBZRLE.cache);
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
g_free(XBZRLE.current_buf);
|
||||
g_free(XBZRLE.decoded_buf);
|
||||
XBZRLE.cache = NULL;
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
XBZRLE.current_buf = NULL;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
}
|
||||
|
||||
static void ram_migration_cancel(void *opaque)
|
||||
@@ -767,39 +609,24 @@ 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;
|
||||
|
||||
if (migrate_use_xbzrle()) {
|
||||
XBZRLE_cache_lock();
|
||||
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");
|
||||
DPRINTF("Error creating cache\n");
|
||||
return -1;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
|
||||
/* 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");
|
||||
return -1;
|
||||
}
|
||||
|
||||
XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.current_buf) {
|
||||
error_report("Error allocating current_buf");
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
|
||||
XBZRLE.current_buf = g_malloc(TARGET_PAGE_SIZE);
|
||||
acct_clear();
|
||||
}
|
||||
|
||||
@@ -808,22 +635,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();
|
||||
@@ -866,7 +677,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;
|
||||
@@ -928,7 +739,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;
|
||||
@@ -962,11 +773,12 @@ 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;
|
||||
|
||||
if (!xbzrle_decoded_buf) {
|
||||
xbzrle_decoded_buf = g_malloc(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.decoded_buf) {
|
||||
XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE);
|
||||
}
|
||||
|
||||
/* extract RLE header */
|
||||
@@ -974,25 +786,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);
|
||||
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,
|
||||
@@ -1005,7 +822,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
|
||||
if (flags & RAM_SAVE_FLAG_CONTINUE) {
|
||||
if (!block) {
|
||||
error_report("Ack, bad migration stream!");
|
||||
fprintf(stderr, "Ack, bad migration stream!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1021,7 +838,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1045,7 +862,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
seq_iter++;
|
||||
|
||||
if (version_id != 4) {
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (!ret) {
|
||||
@@ -1072,9 +889,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: " RAM_ADDR_FMT
|
||||
" in != " 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;
|
||||
@@ -1082,8 +900,8 @@ 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) {
|
||||
@@ -1098,9 +916,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ch = qemu_get_byte(f);
|
||||
@@ -1110,18 +926,14 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
|
||||
} 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;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (load_xbzrle(f, addr, host) < 0) {
|
||||
@@ -1148,7 +960,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,
|
||||
@@ -1157,12 +969,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;
|
||||
@@ -1245,11 +1051,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;
|
||||
}
|
||||
@@ -1272,13 +1079,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);
|
||||
|
||||
@@ -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) */
|
||||
@@ -221,7 +220,6 @@ aio_ctx_finalize(GSource *source)
|
||||
thread_pool_free(ctx->thread_pool);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, NULL);
|
||||
event_notifier_cleanup(&ctx->notifier);
|
||||
rfifolock_destroy(&ctx->lock);
|
||||
qemu_mutex_destroy(&ctx->bh_lock);
|
||||
g_array_free(ctx->pollfds, TRUE);
|
||||
timerlistgroup_deinit(&ctx->tlg);
|
||||
@@ -248,25 +246,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)
|
||||
@@ -274,12 +256,6 @@ static void aio_timerlist_notify(void *opaque)
|
||||
aio_notify(opaque);
|
||||
}
|
||||
|
||||
static void aio_rfifolock_cb(void *opaque)
|
||||
{
|
||||
/* Kick owner thread in case they are blocked in aio_poll() */
|
||||
aio_notify(opaque);
|
||||
}
|
||||
|
||||
AioContext *aio_context_new(void)
|
||||
{
|
||||
AioContext *ctx;
|
||||
@@ -287,7 +263,6 @@ AioContext *aio_context_new(void)
|
||||
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 *)
|
||||
@@ -306,13 +281,3 @@ void aio_context_unref(AioContext *ctx)
|
||||
{
|
||||
g_source_unref(&ctx->source);
|
||||
}
|
||||
|
||||
void aio_context_acquire(AioContext *ctx)
|
||||
{
|
||||
rfifolock_lock(&ctx->lock);
|
||||
}
|
||||
|
||||
void aio_context_release(AioContext *ctx)
|
||||
{
|
||||
rfifolock_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
+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)
|
||||
|
||||
+3
-2
@@ -95,7 +95,7 @@ static struct {
|
||||
}
|
||||
},
|
||||
|
||||
.period = { .hertz = 100 },
|
||||
.period = { .hertz = 250 },
|
||||
.plive = 0,
|
||||
.log_to_monitor = 0,
|
||||
.try_poll_in = 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)
|
||||
|
||||
+4
-4
@@ -547,11 +547,11 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
ss.rate = as->freq;
|
||||
|
||||
/*
|
||||
* qemu audio tick runs at 100 Hz (by default), so processing
|
||||
* data chunks worth 10 ms of sound should be a good fit.
|
||||
* qemu audio tick runs at 250 Hz (by default), so processing
|
||||
* data chunks worth 4 ms of sound should be a good fit.
|
||||
*/
|
||||
ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
|
||||
ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
|
||||
ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
|
||||
ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
|
||||
ba.maxlength = -1;
|
||||
ba.prebuf = -1;
|
||||
|
||||
|
||||
+3
-26
@@ -25,17 +25,8 @@
|
||||
#include "audio.h"
|
||||
#include "audio_int.h"
|
||||
|
||||
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||
#define LINE_OUT_SAMPLES (480 * 4)
|
||||
#else
|
||||
#define LINE_OUT_SAMPLES (256 * 4)
|
||||
#endif
|
||||
|
||||
#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
|
||||
#define LINE_IN_SAMPLES (480 * 4)
|
||||
#else
|
||||
#define LINE_IN_SAMPLES (256 * 4)
|
||||
#endif
|
||||
#define LINE_IN_SAMPLES 1024
|
||||
#define LINE_OUT_SAMPLES 1024
|
||||
|
||||
typedef struct SpiceRateCtl {
|
||||
int64_t start_ticks;
|
||||
@@ -105,7 +96,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;
|
||||
}
|
||||
@@ -120,11 +111,7 @@ static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
struct audsettings settings;
|
||||
|
||||
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||
settings.freq = spice_server_get_best_playback_rate(NULL);
|
||||
#else
|
||||
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||
#endif
|
||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||
settings.fmt = AUD_FMT_S16;
|
||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
@@ -135,9 +122,6 @@ static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
|
||||
|
||||
out->sin.base.sif = &playback_sif.base;
|
||||
qemu_spice_add_interface (&out->sin.base);
|
||||
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||
spice_server_set_playback_rate(&out->sin, settings.freq);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -248,11 +232,7 @@ static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
struct audsettings settings;
|
||||
|
||||
#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
|
||||
settings.freq = spice_server_get_best_record_rate(NULL);
|
||||
#else
|
||||
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
||||
#endif
|
||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||
settings.fmt = AUD_FMT_S16;
|
||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
@@ -263,9 +243,6 @@ static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
|
||||
|
||||
in->sin.base.sif = &record_sif.base;
|
||||
qemu_spice_add_interface (&in->sin.base);
|
||||
#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
|
||||
spice_server_set_record_rate(&in->sin, settings.freq);
|
||||
#endif
|
||||
return 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@ common-obj-$(CONFIG_POSIX) += rng-random.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
|
||||
|
||||
+3
-7
@@ -566,15 +566,13 @@ CharDriverState *chr_baum_init(void)
|
||||
BaumDriverState *baum;
|
||||
CharDriverState *chr;
|
||||
brlapi_handle_t *handle;
|
||||
#if defined(CONFIG_SDL)
|
||||
#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||
#ifdef CONFIG_SDL
|
||||
SDL_SysWMinfo info;
|
||||
#endif
|
||||
#endif
|
||||
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;
|
||||
@@ -597,14 +595,12 @@ CharDriverState *chr_baum_init(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SDL)
|
||||
#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||
#ifdef CONFIG_SDL
|
||||
memset(&info, 0, sizeof(info));
|
||||
SDL_VERSION(&info.version);
|
||||
if (SDL_GetWMInfo(&info))
|
||||
tty = info.info.x11.wmwindow;
|
||||
else
|
||||
#endif
|
||||
#endif
|
||||
tty = BRLAPI_TTY_DEFAULT;
|
||||
|
||||
|
||||
@@ -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);
|
||||
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,375 +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);
|
||||
}
|
||||
|
||||
static void host_memory_backend_finalize(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
if (memory_region_size(&backend->mr)) {
|
||||
memory_region_destroy(&backend->mr);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
.instance_finalize = host_memory_backend_finalize,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&host_memory_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -120,15 +123,15 @@ static void rng_random_init(Object *obj)
|
||||
NULL);
|
||||
|
||||
s->filename = g_strdup("/dev/random");
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
static void rng_random_finalize(Object *obj)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(obj);
|
||||
|
||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
|
||||
if (s->fd != -1) {
|
||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
qemu_close(s->fd);
|
||||
}
|
||||
|
||||
|
||||
+6
-22
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "sysemu/rng.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
void rng_backend_request_entropy(RngBackend *s, size_t size,
|
||||
EntropyReceiveFunc *receive_entropy,
|
||||
@@ -41,16 +40,15 @@ static bool rng_backend_prop_get_opened(Object *obj, Error **errp)
|
||||
return s->opened;
|
||||
}
|
||||
|
||||
static void rng_backend_complete(UserCreatable *uc, Error **errp)
|
||||
void rng_backend_open(RngBackend *s, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(uc), true, "opened", errp);
|
||||
object_property_set_bool(OBJECT(s), true, "opened", errp);
|
||||
}
|
||||
|
||||
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 +60,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)
|
||||
@@ -80,25 +76,13 @@ static void rng_backend_init(Object *obj)
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = rng_backend_complete;
|
||||
}
|
||||
|
||||
static const TypeInfo rng_backend_info = {
|
||||
.name = TYPE_RNG_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(RngBackend),
|
||||
.instance_init = rng_backend_init,
|
||||
.class_size = sizeof(RngBackendClass),
|
||||
.class_init = rng_backend_class_init,
|
||||
.abstract = true,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
|
||||
+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;
|
||||
|
||||
+13
-56
@@ -58,8 +58,6 @@ typedef struct BlkMigDevState {
|
||||
/* Protected by block migration lock. */
|
||||
unsigned long *aio_bitmap;
|
||||
int64_t completed_sectors;
|
||||
BdrvDirtyBitmap *dirty_bitmap;
|
||||
Error *blocker;
|
||||
} BlkMigDevState;
|
||||
|
||||
typedef struct BlkMigBlock {
|
||||
@@ -311,36 +309,12 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static int 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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
static void set_dirty_tracking(int enable)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
bdrv_release_dirty_bitmap(bmds->bs, bmds->dirty_bitmap);
|
||||
bdrv_set_dirty_tracking(bmds->bs, enable ? BLOCK_SIZE : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,8 +336,7 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
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_set_in_use(bs, 1);
|
||||
bdrv_ref(bs);
|
||||
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
@@ -459,7 +432,7 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
} else {
|
||||
blk_mig_unlock();
|
||||
}
|
||||
if (bdrv_get_dirty(bmds->bs, bmds->dirty_bitmap, sector)) {
|
||||
if (bdrv_get_dirty(bmds->bs, sector)) {
|
||||
|
||||
if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
|
||||
nr_sectors = total_sectors - sector;
|
||||
@@ -581,7 +554,7 @@ static int64_t get_remaining_dirty(void)
|
||||
int64_t dirty = 0;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
dirty += bdrv_get_dirty_count(bmds->bs, bmds->dirty_bitmap);
|
||||
dirty += bdrv_get_dirty_count(bmds->bs);
|
||||
}
|
||||
|
||||
return dirty << BDRV_SECTOR_BITS;
|
||||
@@ -596,13 +569,12 @@ static void blk_mig_cleanup(void)
|
||||
|
||||
bdrv_drain_all();
|
||||
|
||||
unset_dirty_tracking();
|
||||
set_dirty_tracking(0);
|
||||
|
||||
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);
|
||||
@@ -632,13 +604,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(1);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
ret = flush_blks(f);
|
||||
@@ -652,7 +618,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);
|
||||
@@ -702,14 +667,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. */
|
||||
@@ -764,8 +722,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();
|
||||
@@ -822,8 +780,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
|
||||
}
|
||||
|
||||
if (flags & BLK_MIG_FLAG_ZERO_BLOCK) {
|
||||
ret = bdrv_write_zeroes(bs, addr, nr_sectors,
|
||||
BDRV_REQ_MAY_UNMAP);
|
||||
ret = bdrv_write_zeroes(bs, addr, nr_sectors);
|
||||
} else {
|
||||
buf = g_malloc(BLOCK_SIZE);
|
||||
qemu_get_buffer(f, buf, BLOCK_SIZE);
|
||||
@@ -869,7 +826,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,
|
||||
|
||||
+2
-15
@@ -3,7 +3,6 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c
|
||||
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 += snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
@@ -11,9 +10,8 @@ block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
|
||||
ifeq ($(CONFIG_POSIX),y)
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-y += nbd.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
|
||||
@@ -25,15 +23,4 @@ common-obj-y += commit.o
|
||||
common-obj-y += mirror.o
|
||||
common-obj-y += backup.o
|
||||
|
||||
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||
iscsi.o-libs := $(LIBISCSI_LIBS)
|
||||
curl.o-cflags := $(CURL_CFLAGS)
|
||||
curl.o-libs := $(CURL_LIBS)
|
||||
rbd.o-cflags := $(RBD_CFLAGS)
|
||||
rbd.o-libs := $(RBD_LIBS)
|
||||
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
||||
gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||
qcow.o-libs := -lz
|
||||
linux-aio.o-libs := -laio
|
||||
$(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS)
|
||||
|
||||
+4
-10
@@ -138,8 +138,7 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
|
||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||
ret = bdrv_co_write_zeroes(job->target,
|
||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
||||
n, BDRV_REQ_MAY_UNMAP);
|
||||
start * BACKUP_SECTORS_PER_CLUSTER, n);
|
||||
} else {
|
||||
ret = bdrv_co_writev(job->target,
|
||||
start * BACKUP_SECTORS_PER_CLUSTER, n,
|
||||
@@ -181,13 +180,8 @@ static int coroutine_fn backup_before_write_notify(
|
||||
void *opaque)
|
||||
{
|
||||
BdrvTrackedRequest *req = opaque;
|
||||
int64_t sector_num = req->offset >> BDRV_SECTOR_BITS;
|
||||
int nb_sectors = req->bytes >> BDRV_SECTOR_BITS;
|
||||
|
||||
assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
|
||||
return backup_do_cow(req->bs, sector_num, nb_sectors, NULL);
|
||||
return backup_do_cow(req->bs, req->sector_num, req->nb_sectors, NULL);
|
||||
}
|
||||
|
||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -307,7 +301,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;
|
||||
}
|
||||
}
|
||||
@@ -325,7 +319,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--;
|
||||
|
||||
+33
-122
@@ -186,14 +186,6 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||
|
||||
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
|
||||
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
|
||||
|
||||
[BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head",
|
||||
[BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head",
|
||||
[BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail",
|
||||
[BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail",
|
||||
[BLKDBG_PWRITEV] = "pwritev",
|
||||
[BLKDBG_PWRITEV_ZERO] = "pwritev_zero",
|
||||
[BLKDBG_PWRITEV_DONE] = "pwritev_done",
|
||||
};
|
||||
|
||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||
@@ -279,33 +271,19 @@ static void remove_rule(BlkdebugRule *rule)
|
||||
g_free(rule);
|
||||
}
|
||||
|
||||
static int read_config(BDRVBlkdebugState *s, const char *filename,
|
||||
QDict *options, Error **errp)
|
||||
static int read_config(BDRVBlkdebugState *s, const char *filename)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
FILE *f;
|
||||
int ret;
|
||||
struct add_rule_data d;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (filename) {
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
error_setg_errno(errp, errno, "Could not read blkdebug config file");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = qemu_config_parse(f, config_groups, filename);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Could not parse blkdebug config file");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
qemu_config_parse_qdict(options, config_groups, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
ret = qemu_config_parse(f, config_groups, filename);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -320,9 +298,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
|
||||
fail:
|
||||
qemu_opts_reset(&inject_error_opts);
|
||||
qemu_opts_reset(&set_state_opts);
|
||||
if (f) {
|
||||
fclose(f);
|
||||
}
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -334,9 +310,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
||||
|
||||
/* Parse the blkdebug: prefix */
|
||||
if (!strstart(filename, "blkdebug:", &filename)) {
|
||||
/* There was no prefix; therefore, all options have to be already
|
||||
present in the QDict (except for the filename) */
|
||||
qdict_put(options, "x-image", qstring_from_str(filename));
|
||||
error_setg(errp, "File name string must start with 'blkdebug:'");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -372,11 +346,6 @@ static QemuOptsList runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "[internal use only, will be removed]",
|
||||
},
|
||||
{
|
||||
.name = "align",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Required alignment in bytes",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -387,53 +356,46 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *config;
|
||||
uint64_t align;
|
||||
const char *filename, *config;
|
||||
int ret;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
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;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Read rules from config file or command line options */
|
||||
/* Read rules from config file */
|
||||
config = qemu_opt_get(opts, "config");
|
||||
ret = read_config(s, config, options, errp);
|
||||
if (ret) {
|
||||
goto out;
|
||||
if (config) {
|
||||
ret = read_config(s, config);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read blkdebug config file");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set initial state */
|
||||
s->state = 1;
|
||||
|
||||
/* Open the backing file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
||||
flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
filename = qemu_opt_get(opts, "x-image");
|
||||
if (filename == NULL) {
|
||||
error_setg(errp, "Could not retrieve image file name");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set request alignment */
|
||||
align = qemu_opt_get_size(opts, "align", bs->request_alignment);
|
||||
if (align > 0 && align < INT_MAX && !(align & (align - 1))) {
|
||||
bs->request_alignment = align;
|
||||
} else {
|
||||
error_setg(errp, "Invalid alignment");
|
||||
ret = -EINVAL;
|
||||
goto fail_unref;
|
||||
ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
fail_unref:
|
||||
bdrv_unref(bs->file);
|
||||
out:
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
@@ -449,10 +411,6 @@ static void error_callback_bh(void *opaque)
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
|
||||
if (acb->bh) {
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
@@ -475,7 +433,7 @@ static BlockDriverAIOCB *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);
|
||||
|
||||
@@ -526,25 +484,6 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *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)
|
||||
{
|
||||
@@ -655,9 +594,9 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r, *next;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co, NULL);
|
||||
return 0;
|
||||
@@ -666,31 +605,6 @@ static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
|
||||
const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r, *r_next;
|
||||
BlkdebugRule *rule, *next;
|
||||
int i, ret = -ENOENT;
|
||||
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
if (rule->action == ACTION_SUSPEND &&
|
||||
!strcmp(rule->options.suspend.tag, tag)) {
|
||||
remove_rule(rule);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co, NULL);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
@@ -722,12 +636,9 @@ static BlockDriver bdrv_blkdebug = {
|
||||
|
||||
.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,
|
||||
.bdrv_debug_remove_breakpoint
|
||||
= blkdebug_debug_remove_breakpoint,
|
||||
.bdrv_debug_resume = blkdebug_debug_resume,
|
||||
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
|
||||
};
|
||||
|
||||
+142
-63
@@ -39,13 +39,12 @@ struct BlkverifyAIOCB {
|
||||
static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb;
|
||||
AioContext *aio_context = bdrv_get_aio_context(blockacb->bs);
|
||||
bool finished = false;
|
||||
|
||||
/* Wait until request completes, invokes its callback, and frees itself */
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
aio_poll(aio_context, true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +78,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
||||
|
||||
/* Parse the blkverify: prefix */
|
||||
if (!strstart(filename, "blkverify:", &filename)) {
|
||||
/* There was no prefix; therefore, all options have to be already
|
||||
present in the QDict (except for the filename) */
|
||||
qdict_put(options, "x-image", qstring_from_str(filename));
|
||||
error_setg(errp, "File name string must start with 'blkverify:'");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,31 +122,44 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *filename, *raw;
|
||||
int ret;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
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 fail;
|
||||
}
|
||||
|
||||
/* Open the raw file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
||||
"raw", flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||
/* Parse the raw image filename */
|
||||
raw = qemu_opt_get(opts, "x-raw");
|
||||
if (raw == NULL) {
|
||||
error_setg(errp, "Could not retrieve raw image filename");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the test file */
|
||||
assert(s->test_file == NULL);
|
||||
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
||||
"test", flags, false, &local_err);
|
||||
filename = qemu_opt_get(opts, "x-image");
|
||||
if (filename == NULL) {
|
||||
error_setg(errp, "Could not retrieve test image filename");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->test_file = bdrv_new("");
|
||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
bdrv_unref(s->test_file);
|
||||
s->test_file = NULL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -174,6 +184,110 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
|
||||
return bdrv_getlength(s->test_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that I/O vector contents are identical
|
||||
*
|
||||
* @a: I/O vector
|
||||
* @b: I/O vector
|
||||
* @ret: Offset to first mismatching byte or -1 if match
|
||||
*/
|
||||
static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
|
||||
{
|
||||
int i;
|
||||
ssize_t offset = 0;
|
||||
|
||||
assert(a->niov == b->niov);
|
||||
for (i = 0; i < a->niov; i++) {
|
||||
size_t len = 0;
|
||||
uint8_t *p = (uint8_t *)a->iov[i].iov_base;
|
||||
uint8_t *q = (uint8_t *)b->iov[i].iov_base;
|
||||
|
||||
assert(a->iov[i].iov_len == b->iov[i].iov_len);
|
||||
while (len < a->iov[i].iov_len && *p++ == *q++) {
|
||||
len++;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
|
||||
if (len != a->iov[i].iov_len) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int src_index;
|
||||
struct iovec *src_iov;
|
||||
void *dest_base;
|
||||
} IOVectorSortElem;
|
||||
|
||||
static int sortelem_cmp_src_base(const void *a, const void *b)
|
||||
{
|
||||
const IOVectorSortElem *elem_a = a;
|
||||
const IOVectorSortElem *elem_b = b;
|
||||
|
||||
/* Don't overflow */
|
||||
if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
|
||||
return -1;
|
||||
} else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int sortelem_cmp_src_index(const void *a, const void *b)
|
||||
{
|
||||
const IOVectorSortElem *elem_a = a;
|
||||
const IOVectorSortElem *elem_b = b;
|
||||
|
||||
return elem_a->src_index - elem_b->src_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contents of I/O vector
|
||||
*
|
||||
* The relative relationships of overlapping iovecs are preserved. This is
|
||||
* necessary to ensure identical semantics in the cloned I/O vector.
|
||||
*/
|
||||
static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
|
||||
void *buf)
|
||||
{
|
||||
IOVectorSortElem sortelems[src->niov];
|
||||
void *last_end;
|
||||
int i;
|
||||
|
||||
/* Sort by source iovecs by base address */
|
||||
for (i = 0; i < src->niov; i++) {
|
||||
sortelems[i].src_index = i;
|
||||
sortelems[i].src_iov = &src->iov[i];
|
||||
}
|
||||
qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
|
||||
|
||||
/* Allocate buffer space taking into account overlapping iovecs */
|
||||
last_end = NULL;
|
||||
for (i = 0; i < src->niov; i++) {
|
||||
struct iovec *cur = sortelems[i].src_iov;
|
||||
ptrdiff_t rewind = 0;
|
||||
|
||||
/* Detect overlap */
|
||||
if (last_end && last_end > cur->iov_base) {
|
||||
rewind = last_end - cur->iov_base;
|
||||
}
|
||||
|
||||
sortelems[i].dest_base = buf - rewind;
|
||||
buf += cur->iov_len - MIN(rewind, cur->iov_len);
|
||||
last_end = MAX(cur->iov_base + cur->iov_len, last_end);
|
||||
}
|
||||
|
||||
/* Sort by source iovec index and build destination iovec */
|
||||
qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
|
||||
for (i = 0; i < src->niov; i++) {
|
||||
qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
|
||||
}
|
||||
}
|
||||
|
||||
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
@@ -229,8 +343,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;
|
||||
}
|
||||
@@ -238,7 +351,7 @@ static void blkverify_aio_cb(void *opaque, int ret)
|
||||
|
||||
static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
||||
{
|
||||
ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov);
|
||||
ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
|
||||
if (offset != -1) {
|
||||
blkverify_err(acb, "contents mismatch in sector %" PRId64,
|
||||
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
|
||||
@@ -256,7 +369,7 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
acb->verify = blkverify_verify_readv;
|
||||
acb->buf = qemu_blockalign(bs->file, qiov->size);
|
||||
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
|
||||
blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
|
||||
|
||||
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
@@ -290,55 +403,21 @@ static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
|
||||
return bdrv_aio_flush(s->test_file, cb, opaque);
|
||||
}
|
||||
|
||||
static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bool perm = bdrv_recurse_is_first_non_filter(bs->file, candidate);
|
||||
|
||||
if (perm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 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_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,
|
||||
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
|
||||
.bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
|
||||
};
|
||||
|
||||
static void bdrv_blkverify_init(void)
|
||||
|
||||
+15
-31
@@ -113,8 +113,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||
error_setg(errp, "Image not in Bochs format");
|
||||
return -EINVAL;
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(bochs.version) == HEADER_V1) {
|
||||
@@ -148,26 +147,16 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512;
|
||||
|
||||
s->extent_size = le32_to_cpu(bochs.extent);
|
||||
if (s->extent_size < BDRV_SECTOR_SIZE) {
|
||||
/* bximage actually never creates extents smaller than 4k */
|
||||
error_setg(errp, "Extent size must be at least 512");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
} else if (!is_power_of_2(s->extent_size)) {
|
||||
error_setg(errp, "Extent size %" PRIu32 " is not a power of two",
|
||||
s->extent_size);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
if (s->extent_size == 0) {
|
||||
error_setg(errp, "Extent size may not be zero");
|
||||
return -EINVAL;
|
||||
} else if (s->extent_size > 0x800000) {
|
||||
error_setg(errp, "Extent size %" PRIu32 " is too large",
|
||||
s->extent_size);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (s->catalog_size < DIV_ROUND_UP(bs->total_sectors,
|
||||
s->extent_size / BDRV_SECTOR_SIZE))
|
||||
{
|
||||
if (s->catalog_size < bs->total_sectors / s->extent_size) {
|
||||
error_setg(errp, "Catalog size is too small for this disk size");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@@ -187,14 +176,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 +
|
||||
@@ -202,14 +190,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));
|
||||
@@ -222,16 +209,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;
|
||||
|
||||
+6
-6
@@ -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;
|
||||
@@ -133,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;
|
||||
}
|
||||
@@ -146,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;
|
||||
}
|
||||
|
||||
+11
-8
@@ -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,
|
||||
@@ -142,7 +141,7 @@ wait:
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
|
||||
ret = bdrv_drop_intermediate(active, top, base);
|
||||
}
|
||||
|
||||
exit_free_buf:
|
||||
@@ -159,7 +158,7 @@ exit_restore_reopen:
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ static const BlockJobDriver commit_job_driver = {
|
||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, const char *backing_file_str, Error **errp)
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
@@ -195,11 +194,17 @@ 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;
|
||||
}
|
||||
|
||||
/* Once we support top == active layer, remove this check */
|
||||
if (top == bs) {
|
||||
error_setg(errp,
|
||||
"Top image as the active layer is currently unsupported");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(top != bs);
|
||||
if (top == base) {
|
||||
error_setg(errp, "Invalid files for merge: top and base are the same");
|
||||
return;
|
||||
@@ -245,8 +250,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);
|
||||
|
||||
|
||||
+88
-117
@@ -74,16 +74,15 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
||||
error_setg(errp, "Image not in COW format");
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version),
|
||||
"COW version %" PRIu32, cow_header.version);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
"COW version %d", cow_header.version);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "cow", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
@@ -104,18 +103,40 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void cow_set_bits(uint8_t *bitmap, int start, int64_t nb_sectors)
|
||||
/*
|
||||
* XXX(hch): right now these functions are extremely inefficient.
|
||||
* We should just read the whole bitmap we'll need in one go instead.
|
||||
*/
|
||||
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum, bool *first)
|
||||
{
|
||||
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++;
|
||||
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
|
||||
uint8_t bitmap;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bitmap & (1 << (bitnum % 8))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*first) {
|
||||
ret = bdrv_flush(bs->file);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
*first = false;
|
||||
}
|
||||
|
||||
bitmap |= (1 << (bitnum % 8));
|
||||
|
||||
ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BITS_PER_BITMAP_SECTOR (512 * 8)
|
||||
@@ -153,34 +174,18 @@ static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
|
||||
{
|
||||
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;
|
||||
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||
int ret;
|
||||
int changed;
|
||||
|
||||
do {
|
||||
int ret;
|
||||
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
|
||||
changed = cow_test_bit(bitnum, bitmap);
|
||||
*num_same = cow_find_streak(bitmap, changed, bitnum, nb_sectors);
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -199,52 +204,18 @@ static int64_t coroutine_fn cow_co_get_block_status(BlockDriverState *bs,
|
||||
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;
|
||||
int error = 0;
|
||||
int i;
|
||||
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;
|
||||
for (i = 0; i < nb_sectors; i++) {
|
||||
error = cow_set_bit(bs, sector_num + i, &first);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -324,31 +295,39 @@ static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
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;
|
||||
char *image_filename = NULL;
|
||||
const char *image_filename = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *cow_bs = NULL;
|
||||
BlockDriverState *cow_bs;
|
||||
|
||||
/* Read out options */
|
||||
image_sectors = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512;
|
||||
image_filename = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
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_open(&cow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&cow_header, 0, sizeof(cow_header));
|
||||
@@ -381,29 +360,22 @@ static int cow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
exit:
|
||||
g_free(image_filename);
|
||||
if (cow_bs) {
|
||||
bdrv_unref(cow_bs);
|
||||
}
|
||||
bdrv_unref(cow_bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QemuOptsList cow_create_opts = {
|
||||
.name = "cow-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(cow_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"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
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 = {
|
||||
@@ -415,13 +387,12 @@ static BlockDriver bdrv_cow = {
|
||||
.bdrv_close = cow_close,
|
||||
.bdrv_create = cow_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.supports_backing = true,
|
||||
|
||||
.bdrv_read = cow_co_read,
|
||||
.bdrv_write = cow_co_write,
|
||||
.bdrv_co_get_block_status = cow_co_get_block_status,
|
||||
|
||||
.create_opts = &cow_create_opts,
|
||||
.create_options = cow_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_cow_init(void)
|
||||
|
||||
+198
-230
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
// #define DEBUG
|
||||
@@ -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,16 +46,12 @@ 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 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"
|
||||
|
||||
struct BDRVCURLState;
|
||||
|
||||
typedef struct CURLAIOCB {
|
||||
@@ -91,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;
|
||||
@@ -108,14 +87,11 @@ typedef struct BDRVCURLState {
|
||||
CURLState states[CURL_NUM_STATES];
|
||||
char *url;
|
||||
size_t readahead_size;
|
||||
bool sslverify;
|
||||
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)
|
||||
@@ -135,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;
|
||||
}
|
||||
|
||||
@@ -187,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 */
|
||||
@@ -212,6 +180,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
read_end:
|
||||
return realsize;
|
||||
}
|
||||
|
||||
@@ -246,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))
|
||||
@@ -269,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_release(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)
|
||||
@@ -346,7 +313,7 @@ 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
|
||||
@@ -370,44 +337,44 @@ static CURLState *curl_init_state(BDRVCURLState *s)
|
||||
break;
|
||||
}
|
||||
if (!state) {
|
||||
aio_poll(state->s->aio_context, 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);
|
||||
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);
|
||||
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;
|
||||
|
||||
@@ -424,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 = {
|
||||
@@ -475,20 +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"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -506,30 +461,30 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
static int inited = 0;
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
error_setg(errp, "curl block device does not support writes");
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR,
|
||||
"curl block device does not support writes");
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
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);
|
||||
fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
|
||||
s->readahead_size);
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
|
||||
|
||||
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");
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "curl block driver requires "
|
||||
"an 'url' option");
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
@@ -539,7 +494,6 @@ 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(s);
|
||||
if (!state)
|
||||
@@ -572,13 +526,27 @@ 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:
|
||||
@@ -601,7 +569,6 @@ static const AIOCBInfo curl_aiocb_info = {
|
||||
static void curl_readv_bh_cb(void *p)
|
||||
{
|
||||
CURLState *state;
|
||||
int running;
|
||||
|
||||
CURLAIOCB *acb = p;
|
||||
BDRVCURLState *s = acb->common.bs->opaque;
|
||||
@@ -636,7 +603,8 @@ 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;
|
||||
@@ -649,9 +617,8 @@ 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 BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
@@ -666,7 +633,7 @@ static BlockDriverAIOCB *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;
|
||||
}
|
||||
@@ -674,9 +641,25 @@ static BlockDriverAIOCB *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->url);
|
||||
}
|
||||
@@ -688,83 +671,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)
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
+212
-344
@@ -3,27 +3,42 @@
|
||||
*
|
||||
* Copyright (C) 2012 Bharata B Rao <bharata@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
* Pipe handling mechanism in AIO implementation is derived from
|
||||
* block/rbd.c. Hence,
|
||||
*
|
||||
* Copyright (C) 2010-2011 Christian Brunner <chb@muc.de>,
|
||||
* Josh Durgin <josh.durgin@dreamhost.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
#include <glusterfs/api/glfs.h>
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/uri.h"
|
||||
|
||||
typedef struct GlusterAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
int64_t size;
|
||||
int ret;
|
||||
bool *finished;
|
||||
QEMUBH *bh;
|
||||
Coroutine *coroutine;
|
||||
AioContext *aio_context;
|
||||
} GlusterAIOCB;
|
||||
|
||||
typedef struct BDRVGlusterState {
|
||||
struct glfs *glfs;
|
||||
int fds[2];
|
||||
struct glfs_fd *fd;
|
||||
int event_reader_pos;
|
||||
GlusterAIOCB *event_acb;
|
||||
} BDRVGlusterState;
|
||||
|
||||
#define GLUSTER_FD_READ 0
|
||||
#define GLUSTER_FD_WRITE 1
|
||||
|
||||
typedef struct GlusterConf {
|
||||
char *server;
|
||||
int port;
|
||||
@@ -34,13 +49,11 @@ typedef struct GlusterConf {
|
||||
|
||||
static void qemu_gluster_gconf_free(GlusterConf *gconf)
|
||||
{
|
||||
if (gconf) {
|
||||
g_free(gconf->server);
|
||||
g_free(gconf->volname);
|
||||
g_free(gconf->image);
|
||||
g_free(gconf->transport);
|
||||
g_free(gconf);
|
||||
}
|
||||
g_free(gconf->server);
|
||||
g_free(gconf->volname);
|
||||
g_free(gconf->image);
|
||||
g_free(gconf->transport);
|
||||
g_free(gconf);
|
||||
}
|
||||
|
||||
static int parse_volume_options(GlusterConf *gconf, char *path)
|
||||
@@ -81,7 +94,7 @@ static int parse_volume_options(GlusterConf *gconf, char *path)
|
||||
* 'server' specifies the server where the volume file specification for
|
||||
* the given volume resides. This can be either hostname, ipv4 address
|
||||
* or ipv6 address. ipv6 address needs to be within square brackets [ ].
|
||||
* If transport type is 'unix', then 'server' field should not be specified.
|
||||
* If transport type is 'unix', then 'server' field should not be specifed.
|
||||
* The 'socket' field needs to be populated with the path to unix domain
|
||||
* socket.
|
||||
*
|
||||
@@ -118,7 +131,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
|
||||
}
|
||||
|
||||
/* transport */
|
||||
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
|
||||
if (!strcmp(uri->scheme, "gluster")) {
|
||||
gconf->transport = g_strdup("tcp");
|
||||
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
|
||||
gconf->transport = g_strdup("tcp");
|
||||
@@ -154,7 +167,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
|
||||
}
|
||||
gconf->server = g_strdup(qp->p[0].value);
|
||||
} else {
|
||||
gconf->server = g_strdup(uri->server ? uri->server : "localhost");
|
||||
gconf->server = g_strdup(uri->server);
|
||||
gconf->port = uri->port;
|
||||
}
|
||||
|
||||
@@ -166,8 +179,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
|
||||
Error **errp)
|
||||
static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
|
||||
{
|
||||
struct glfs *glfs = NULL;
|
||||
int ret;
|
||||
@@ -175,8 +187,8 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
|
||||
|
||||
ret = qemu_gluster_parseuri(gconf, filename);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/"
|
||||
"volname/image[?socket=...]");
|
||||
error_report("Usage: file=gluster[+transport]://[server[:port]]/"
|
||||
"volname/image[?socket=...]");
|
||||
errno = -ret;
|
||||
goto out;
|
||||
}
|
||||
@@ -203,16 +215,9 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
|
||||
|
||||
ret = glfs_init(glfs);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, errno,
|
||||
"Gluster connection failed for server=%s port=%d "
|
||||
"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;
|
||||
|
||||
error_report("Gluster connection failed for server=%s port=%d "
|
||||
"volume=%s image=%s transport=%s", gconf->server, gconf->port,
|
||||
gconf->volname, gconf->image, gconf->transport);
|
||||
goto out;
|
||||
}
|
||||
return glfs;
|
||||
@@ -226,32 +231,46 @@ out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void qemu_gluster_complete_aio(void *opaque)
|
||||
static void qemu_gluster_complete_aio(GlusterAIOCB *acb, BDRVGlusterState *s)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
|
||||
int ret;
|
||||
bool *finished = acb->finished;
|
||||
BlockDriverCompletionFunc *cb = acb->common.cb;
|
||||
void *opaque = acb->common.opaque;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
qemu_coroutine_enter(acb->coroutine, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* AIO callback routine called from GlusterFS thread.
|
||||
*/
|
||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
||||
|
||||
if (!ret || ret == acb->size) {
|
||||
acb->ret = 0; /* Success */
|
||||
} else if (ret < 0) {
|
||||
acb->ret = ret; /* Read/Write failed */
|
||||
if (!acb->ret || acb->ret == acb->size) {
|
||||
ret = 0; /* Success */
|
||||
} else if (acb->ret < 0) {
|
||||
ret = acb->ret; /* Read/Write failed */
|
||||
} else {
|
||||
acb->ret = -EIO; /* Partial read/write - fail it */
|
||||
ret = -EIO; /* Partial read/write - fail it */
|
||||
}
|
||||
|
||||
acb->bh = aio_bh_new(acb->aio_context, qemu_gluster_complete_aio, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
qemu_aio_release(acb);
|
||||
cb(opaque, ret);
|
||||
if (finished) {
|
||||
*finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_gluster_aio_event_reader(void *opaque)
|
||||
{
|
||||
BDRVGlusterState *s = opaque;
|
||||
ssize_t ret;
|
||||
|
||||
do {
|
||||
char *p = (char *)&s->event_acb;
|
||||
|
||||
ret = read(s->fds[GLUSTER_FD_READ], p + s->event_reader_pos,
|
||||
sizeof(s->event_acb) - s->event_reader_pos);
|
||||
if (ret > 0) {
|
||||
s->event_reader_pos += ret;
|
||||
if (s->event_reader_pos == sizeof(s->event_acb)) {
|
||||
s->event_reader_pos = 0;
|
||||
qemu_gluster_complete_aio(s->event_acb, s);
|
||||
}
|
||||
}
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
@@ -268,57 +287,60 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
|
||||
{
|
||||
assert(open_flags != NULL);
|
||||
|
||||
*open_flags |= O_BINARY;
|
||||
|
||||
if (bdrv_flags & BDRV_O_RDWR) {
|
||||
*open_flags |= O_RDWR;
|
||||
} else {
|
||||
*open_flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
||||
*open_flags |= O_DIRECT;
|
||||
}
|
||||
}
|
||||
|
||||
static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||
int bdrv_flags, Error **errp)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int open_flags = 0;
|
||||
int open_flags = O_BINARY;
|
||||
int ret = 0;
|
||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
filename = qemu_opt_get(opts, "filename");
|
||||
|
||||
s->glfs = qemu_gluster_init(gconf, filename, errp);
|
||||
|
||||
s->glfs = qemu_gluster_init(gconf, filename);
|
||||
if (!s->glfs) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
||||
if (bdrv_flags & BDRV_O_RDWR) {
|
||||
open_flags |= O_RDWR;
|
||||
} else {
|
||||
open_flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
||||
open_flags |= O_DIRECT;
|
||||
}
|
||||
|
||||
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
|
||||
if (!s->fd) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_pipe(s->fds);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
fcntl(s->fds[GLUSTER_FD_READ], F_SETFL, O_NONBLOCK);
|
||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ],
|
||||
qemu_gluster_aio_event_reader, NULL, s);
|
||||
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
qemu_gluster_gconf_free(gconf);
|
||||
@@ -334,181 +356,26 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct BDRVGlusterReopenState {
|
||||
struct glfs *glfs;
|
||||
struct glfs_fd *fd;
|
||||
} BDRVGlusterReopenState;
|
||||
|
||||
|
||||
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
BDRVGlusterReopenState *reop_s;
|
||||
GlusterConf *gconf = NULL;
|
||||
int open_flags = 0;
|
||||
|
||||
assert(state != NULL);
|
||||
assert(state->bs != NULL);
|
||||
|
||||
state->opaque = g_malloc0(sizeof(BDRVGlusterReopenState));
|
||||
reop_s = state->opaque;
|
||||
|
||||
qemu_gluster_parse_flags(state->flags, &open_flags);
|
||||
|
||||
gconf = g_malloc0(sizeof(GlusterConf));
|
||||
|
||||
reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, errp);
|
||||
if (reop_s->glfs == NULL) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
reop_s->fd = glfs_open(reop_s->glfs, gconf->image, open_flags);
|
||||
if (reop_s->fd == NULL) {
|
||||
/* reops->glfs will be cleaned up in _abort */
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
/* state->opaque will be freed in either the _abort or _commit */
|
||||
qemu_gluster_gconf_free(gconf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qemu_gluster_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
BDRVGlusterReopenState *reop_s = state->opaque;
|
||||
BDRVGlusterState *s = state->bs->opaque;
|
||||
|
||||
|
||||
/* close the old */
|
||||
if (s->fd) {
|
||||
glfs_close(s->fd);
|
||||
}
|
||||
if (s->glfs) {
|
||||
glfs_fini(s->glfs);
|
||||
}
|
||||
|
||||
/* use the newly opened image / connection */
|
||||
s->fd = reop_s->fd;
|
||||
s->glfs = reop_s->glfs;
|
||||
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void qemu_gluster_reopen_abort(BDRVReopenState *state)
|
||||
{
|
||||
BDRVGlusterReopenState *reop_s = state->opaque;
|
||||
|
||||
if (reop_s == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reop_s->fd) {
|
||||
glfs_close(reop_s->fd);
|
||||
}
|
||||
|
||||
if (reop_s->glfs) {
|
||||
glfs_fini(reop_s->glfs);
|
||||
}
|
||||
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||
static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
off_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
|
||||
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) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
ret = acb->ret;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool gluster_supports_zerofill(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
||||
int64_t size)
|
||||
{
|
||||
return glfs_zerofill(fd, offset, size);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline bool gluster_supports_zerofill(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
||||
int64_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#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_malloc0(sizeof(GlusterConf));
|
||||
|
||||
glfs = qemu_gluster_init(gconf, filename, errp);
|
||||
glfs = qemu_gluster_init(gconf, filename);
|
||||
if (!glfs) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_size =
|
||||
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;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = glfs_creat(glfs, gconf->image,
|
||||
@@ -516,21 +383,14 @@ static int qemu_gluster_create(const char *filename,
|
||||
if (!fd) {
|
||||
ret = -errno;
|
||||
} else {
|
||||
if (!glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE)) {
|
||||
if (prealloc && qemu_gluster_zerofill(fd, 0,
|
||||
total_size * BDRV_SECTOR_SIZE)) {
|
||||
ret = -errno;
|
||||
}
|
||||
} else {
|
||||
if (glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
if (glfs_close(fd) != 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
}
|
||||
out:
|
||||
g_free(tmp);
|
||||
qemu_gluster_gconf_free(gconf);
|
||||
if (glfs) {
|
||||
glfs_fini(glfs);
|
||||
@@ -538,19 +398,58 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write)
|
||||
static void qemu_gluster_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)blockacb;
|
||||
bool finished = false;
|
||||
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo gluster_aiocb_info = {
|
||||
.aiocb_size = sizeof(GlusterAIOCB),
|
||||
.cancel = qemu_gluster_aio_cancel,
|
||||
};
|
||||
|
||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int retval;
|
||||
|
||||
acb->ret = ret;
|
||||
retval = qemu_write_full(s->fds[GLUSTER_FD_WRITE], &acb, sizeof(acb));
|
||||
if (retval != sizeof(acb)) {
|
||||
/*
|
||||
* Gluster AIO callback thread failed to notify the waiting
|
||||
* QEMU thread about IO completion.
|
||||
*/
|
||||
error_report("Gluster AIO completion failed: %s", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int write)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
GlusterAIOCB *acb;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
size_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
size_t size;
|
||||
off_t offset;
|
||||
|
||||
offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
|
||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
||||
acb->size = size;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
acb->finished = NULL;
|
||||
|
||||
if (write) {
|
||||
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||
@@ -561,16 +460,13 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
ret = acb->ret;
|
||||
return &acb->common;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
||||
@@ -586,70 +482,71 @@ static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0);
|
||||
return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
}
|
||||
|
||||
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
|
||||
return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
||||
}
|
||||
|
||||
static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
GlusterAIOCB *acb;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
|
||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
acb->finished = NULL;
|
||||
|
||||
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
ret = acb->ret;
|
||||
return &acb->common;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||
static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
GlusterAIOCB *acb;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
size_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
size_t size;
|
||||
off_t offset;
|
||||
|
||||
offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
|
||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
acb->finished = NULL;
|
||||
|
||||
ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
ret = acb->ret;
|
||||
return &acb->common;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -684,6 +581,10 @@ static void qemu_gluster_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
|
||||
close(s->fds[GLUSTER_FD_READ]);
|
||||
close(s->fds[GLUSTER_FD_WRITE]);
|
||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL);
|
||||
|
||||
if (s->fd) {
|
||||
glfs_close(s->fd);
|
||||
s->fd = NULL;
|
||||
@@ -697,22 +598,13 @@ 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"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster = {
|
||||
@@ -721,25 +613,19 @@ static BlockDriver bdrv_gluster = {
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_truncate = qemu_gluster_truncate,
|
||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
||||
#endif
|
||||
#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 = {
|
||||
@@ -748,25 +634,19 @@ static BlockDriver bdrv_gluster_tcp = {
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_truncate = qemu_gluster_truncate,
|
||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
||||
#endif
|
||||
#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 = {
|
||||
@@ -775,25 +655,19 @@ static BlockDriver bdrv_gluster_unix = {
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_truncate = qemu_gluster_truncate,
|
||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
||||
#endif
|
||||
#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 = {
|
||||
@@ -802,25 +676,19 @@ static BlockDriver bdrv_gluster_rdma = {
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_truncate = qemu_gluster_truncate,
|
||||
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
||||
#endif
|
||||
#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)
|
||||
|
||||
+673
-732
File diff suppressed because it is too large
Load Diff
+3
-120
@@ -25,8 +25,6 @@
|
||||
*/
|
||||
#define MAX_EVENTS 128
|
||||
|
||||
#define MAX_QUEUED_IO 128
|
||||
|
||||
struct qemu_laiocb {
|
||||
BlockDriverAIOCB common;
|
||||
struct qemu_laio_state *ctx;
|
||||
@@ -38,19 +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;
|
||||
};
|
||||
|
||||
static inline ssize_t io_event_ret(struct io_event *ev)
|
||||
@@ -147,79 +135,6 @@ static const AIOCBInfo laio_aiocb_info = {
|
||||
.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;
|
||||
}
|
||||
|
||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
@@ -253,13 +168,8 @@ BlockDriverAIOCB *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:
|
||||
@@ -267,20 +177,6 @@ out_free_aiocb:
|
||||
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);
|
||||
}
|
||||
|
||||
void laio_attach_aio_context(void *s_, AioContext *new_context)
|
||||
{
|
||||
struct qemu_laio_state *s = s_;
|
||||
|
||||
aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb);
|
||||
}
|
||||
|
||||
void *laio_init(void)
|
||||
{
|
||||
struct qemu_laio_state *s;
|
||||
@@ -294,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;
|
||||
|
||||
@@ -304,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);
|
||||
}
|
||||
|
||||
+39
-189
@@ -31,14 +31,7 @@ typedef struct MirrorBlockJob {
|
||||
BlockJob common;
|
||||
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;
|
||||
MirrorSyncMode mode;
|
||||
BlockdevOnError on_source_error, on_target_error;
|
||||
bool synced;
|
||||
bool should_complete;
|
||||
@@ -46,7 +39,6 @@ typedef struct MirrorBlockJob {
|
||||
int64_t granularity;
|
||||
size_t buf_size;
|
||||
unsigned long *cow_bitmap;
|
||||
BdrvDirtyBitmap *dirty_bitmap;
|
||||
HBitmapIter hbi;
|
||||
uint8_t *buf;
|
||||
QSIMPLEQ_HEAD(, MirrorBuffer) buf_free;
|
||||
@@ -102,7 +94,6 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||
}
|
||||
|
||||
qemu_iovec_destroy(&op->qiov);
|
||||
g_slice_free(MirrorOp, op);
|
||||
|
||||
/* Enter coroutine when it is not sleeping. The coroutine sleeps to
|
||||
@@ -124,7 +115,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;
|
||||
}
|
||||
}
|
||||
@@ -141,7 +132,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;
|
||||
}
|
||||
|
||||
@@ -162,10 +153,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
if (s->sector_num < 0) {
|
||||
bdrv_dirty_iter_init(source, s->dirty_bitmap, &s->hbi);
|
||||
bdrv_dirty_iter_init(source, &s->hbi);
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
trace_mirror_restart_iter(s,
|
||||
bdrv_get_dirty_count(source, s->dirty_bitmap));
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(source));
|
||||
assert(s->sector_num >= 0);
|
||||
}
|
||||
|
||||
@@ -201,7 +191,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
do {
|
||||
int added_sectors, added_chunks;
|
||||
|
||||
if (!bdrv_get_dirty(source, s->dirty_bitmap, next_sector) ||
|
||||
if (!bdrv_get_dirty(source, next_sector) ||
|
||||
test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
assert(nb_sectors > 0);
|
||||
break;
|
||||
@@ -265,17 +255,14 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
next_sector = sector_num;
|
||||
while (nb_chunks-- > 0) {
|
||||
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
||||
size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size;
|
||||
|
||||
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
||||
s->buf_free_count--;
|
||||
qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
|
||||
qemu_iovec_add(&op->qiov, buf, s->granularity);
|
||||
|
||||
/* Advance the HBitmapIter in parallel, so that we do not examine
|
||||
* the same sector twice.
|
||||
*/
|
||||
if (next_sector > hbitmap_next_sector
|
||||
&& bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
||||
if (next_sector > hbitmap_next_sector && bdrv_get_dirty(source, next_sector)) {
|
||||
hbitmap_next_sector = hbitmap_iter_next(&s->hbi);
|
||||
}
|
||||
|
||||
@@ -332,21 +319,12 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
ret = s->common.len;
|
||||
goto immediate_exit;
|
||||
} else if (s->common.len == 0) {
|
||||
/* Report BLOCK_JOB_READY and wait for complete. */
|
||||
block_job_event_ready(&s->common);
|
||||
s->synced = true;
|
||||
while (!block_job_is_cancelled(&s->common) && !s->should_complete) {
|
||||
block_job_yield(&s->common);
|
||||
}
|
||||
s->common.cancelled = false;
|
||||
goto immediate_exit;
|
||||
if (s->common.len <= 0) {
|
||||
block_job_completed(&s->common, s->common.len);
|
||||
return;
|
||||
}
|
||||
|
||||
length = DIV_ROUND_UP(s->common.len, 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
|
||||
@@ -356,10 +334,7 @@ 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);
|
||||
@@ -371,9 +346,10 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
mirror_free_init(s);
|
||||
|
||||
if (!s->is_none_mode) {
|
||||
if (s->mode != MIRROR_SYNC_MODE_NONE) {
|
||||
/* First part, loop on the sectors and initialize the dirty bitmap. */
|
||||
BlockDriverState *base = s->base;
|
||||
BlockDriverState *base;
|
||||
base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd;
|
||||
for (sector_num = 0; sector_num < end; ) {
|
||||
int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1;
|
||||
ret = bdrv_is_allocated_above(bs, base,
|
||||
@@ -393,7 +369,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_dirty_iter_init(bs, s->dirty_bitmap, &s->hbi);
|
||||
bdrv_dirty_iter_init(bs, &s->hbi);
|
||||
last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
for (;;) {
|
||||
uint64_t delay_ns = 0;
|
||||
@@ -405,7 +381,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
|
||||
/* 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.
|
||||
@@ -432,8 +408,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 {
|
||||
@@ -444,13 +419,13 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
*/
|
||||
s->common.offset = end * BDRV_SECTOR_SIZE;
|
||||
if (!s->synced) {
|
||||
block_job_event_ready(&s->common);
|
||||
block_job_ready(&s->common);
|
||||
s->synced = true;
|
||||
}
|
||||
|
||||
should_complete = s->should_complete ||
|
||||
block_job_is_cancelled(&s->common);
|
||||
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +440,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
*/
|
||||
trace_mirror_before_drain(s, cnt);
|
||||
bdrv_drain_all();
|
||||
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@@ -505,31 +480,15 @@ immediate_exit:
|
||||
qemu_vfree(s->buf);
|
||||
g_free(s->cow_bitmap);
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||
bdrv_set_dirty_tracking(bs, 0);
|
||||
bdrv_iostatus_disable(s->target);
|
||||
if (s->should_complete && 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 (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->to_replace) {
|
||||
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
|
||||
error_free(s->replace_blocker);
|
||||
bdrv_unref(s->to_replace);
|
||||
}
|
||||
g_free(s->replaces);
|
||||
bdrv_close(s->target);
|
||||
bdrv_unref(s->target);
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
@@ -560,6 +519,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
|
||||
ret = bdrv_open_backing_file(s->target, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
char backing_filename[PATH_MAX];
|
||||
bdrv_get_full_backing_filename(s->target, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
@@ -568,20 +530,6 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* check the target bs is not blocked and block all operations on it */
|
||||
if (s->replaces) {
|
||||
s->to_replace = check_to_replace_node(s->replaces, &local_err);
|
||||
if (!s->to_replace) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
s->should_complete = true;
|
||||
block_job_resume(job);
|
||||
}
|
||||
@@ -594,25 +542,12 @@ static const BlockJobDriver mirror_job_driver = {
|
||||
.complete = mirror_complete,
|
||||
};
|
||||
|
||||
static const BlockJobDriver commit_active_job_driver = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = mirror_set_speed,
|
||||
.iostatus_reset
|
||||
= mirror_iostatus_reset,
|
||||
.complete = mirror_complete,
|
||||
};
|
||||
|
||||
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,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp,
|
||||
const BlockJobDriver *driver,
|
||||
bool is_none_mode, BlockDriverState *base)
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, int64_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
|
||||
@@ -637,25 +572,19 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
s = block_job_create(driver, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(&mirror_job_driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->replaces = g_strdup(replaces);
|
||||
s->on_source_error = on_source_error;
|
||||
s->on_target_error = on_target_error;
|
||||
s->target = target;
|
||||
s->is_none_mode = is_none_mode;
|
||||
s->base = base;
|
||||
s->mode = mode;
|
||||
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;
|
||||
}
|
||||
bdrv_set_dirty_tracking(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);
|
||||
@@ -663,82 +592,3 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
trace_mirror_start(bs, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
}
|
||||
|
||||
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,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool is_none_mode;
|
||||
BlockDriverState *base;
|
||||
|
||||
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,
|
||||
on_source_error, on_target_error, cb, opaque, errp,
|
||||
&mirror_job_driver, is_none_mode, base);
|
||||
}
|
||||
|
||||
void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
int64_t length, base_length;
|
||||
int orig_base_flags;
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
orig_base_flags = bdrv_get_flags(base);
|
||||
|
||||
if (bdrv_reopen(base, bs->open_flags, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
length = bdrv_getlength(bs);
|
||||
if (length < 0) {
|
||||
error_setg_errno(errp, -length,
|
||||
"Unable to determine length of %s", bs->filename);
|
||||
goto error_restore_flags;
|
||||
}
|
||||
|
||||
base_length = bdrv_getlength(base);
|
||||
if (base_length < 0) {
|
||||
error_setg_errno(errp, -base_length,
|
||||
"Unable to determine length of %s", base->filename);
|
||||
goto error_restore_flags;
|
||||
}
|
||||
|
||||
if (length > base_length) {
|
||||
ret = bdrv_truncate(base, length);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Top image %s is larger than base image %s, and "
|
||||
"resize of base image failed",
|
||||
bs->filename, base->filename);
|
||||
goto error_restore_flags;
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_ref(base);
|
||||
mirror_start_job(bs, base, NULL, speed, 0, 0,
|
||||
on_error, on_error, cb, opaque, &local_err,
|
||||
&commit_active_job_driver, false, base);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto error_restore_flags;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error_restore_flags:
|
||||
/* ignore error and errp for bdrv_reopen, because we want to propagate
|
||||
* the original error */
|
||||
bdrv_reopen(base, orig_base_flags, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* QEMU Block driver for NBD
|
||||
*
|
||||
* Copyright (C) 2008 Bull S.A.S.
|
||||
* Author: Laurent Vivier <Laurent.Vivier@bull.net>
|
||||
*
|
||||
* Some parts:
|
||||
* Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
|
||||
*
|
||||
* 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 "nbd-client.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
||||
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
|
||||
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
|
||||
|
||||
static void nbd_recv_coroutines_enter_all(NbdClientSession *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nbd_teardown_connection(NbdClientSession *client)
|
||||
{
|
||||
/* finish any pending coroutines */
|
||||
shutdown(client->sock, 2);
|
||||
nbd_recv_coroutines_enter_all(client);
|
||||
|
||||
nbd_client_session_detach_aio_context(client);
|
||||
closesocket(client->sock);
|
||||
client->sock = -1;
|
||||
}
|
||||
|
||||
static void nbd_reply_ready(void *opaque)
|
||||
{
|
||||
NbdClientSession *s = opaque;
|
||||
uint64_t i;
|
||||
int ret;
|
||||
|
||||
if (s->reply.handle == 0) {
|
||||
/* No reply already in flight. Fetch a header. It is possible
|
||||
* that another thread has done the same thing in parallel, so
|
||||
* the socket is not readable anymore.
|
||||
*/
|
||||
ret = nbd_receive_reply(s->sock, &s->reply);
|
||||
if (ret == -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
if (ret < 0) {
|
||||
s->reply.handle = 0;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* There's no need for a mutex on the receive side, because the
|
||||
* handler acts as a synchronization point and ensures that only
|
||||
* one coroutine is called until the reply finishes. */
|
||||
i = HANDLE_TO_INDEX(s, s->reply.handle);
|
||||
if (i >= MAX_NBD_REQUESTS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
fail:
|
||||
nbd_teardown_connection(s);
|
||||
}
|
||||
|
||||
static void nbd_restart_write(void *opaque)
|
||||
{
|
||||
NbdClientSession *s = opaque;
|
||||
|
||||
qemu_coroutine_enter(s->send_coroutine, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
if (qiov) {
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 1);
|
||||
}
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
if (rc >= 0) {
|
||||
ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (ret != request->len) {
|
||||
rc = -EIO;
|
||||
}
|
||||
}
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 0);
|
||||
}
|
||||
} else {
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
}
|
||||
aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, s);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void nbd_co_receive_reply(NbdClientSession *s,
|
||||
struct nbd_request *request, struct nbd_reply *reply,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Wait until we're woken up by the read handler. TODO: perhaps
|
||||
* peek at the next reply and avoid yielding if it's ours? */
|
||||
qemu_coroutine_yield();
|
||||
*reply = s->reply;
|
||||
if (reply->handle != request->handle) {
|
||||
reply->error = EIO;
|
||||
} else {
|
||||
if (qiov && reply->error == 0) {
|
||||
ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (ret != request->len) {
|
||||
reply->error = EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the read handler to read another header. */
|
||||
s->reply.handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void nbd_coroutine_start(NbdClientSession *s,
|
||||
struct nbd_request *request)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Poor man semaphore. The free_sema is locked when no other request
|
||||
* can be accepted, and unlocked after receiving one reply. */
|
||||
if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
|
||||
qemu_co_mutex_lock(&s->free_sema);
|
||||
assert(s->in_flight < MAX_NBD_REQUESTS);
|
||||
}
|
||||
s->in_flight++;
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i] == NULL) {
|
||||
s->recv_coroutine[i] = qemu_coroutine_self();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < MAX_NBD_REQUESTS);
|
||||
request->handle = INDEX_TO_HANDLE(s, i);
|
||||
}
|
||||
|
||||
static void nbd_coroutine_end(NbdClientSession *s,
|
||||
struct nbd_request *request)
|
||||
{
|
||||
int i = HANDLE_TO_INDEX(s, request->handle);
|
||||
s->recv_coroutine[i] = NULL;
|
||||
if (s->in_flight-- == MAX_NBD_REQUESTS) {
|
||||
qemu_co_mutex_unlock(&s->free_sema);
|
||||
}
|
||||
}
|
||||
|
||||
static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
struct nbd_request request = { .type = NBD_CMD_READ };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(client, &request, &reply, qiov, offset);
|
||||
}
|
||||
nbd_coroutine_end(client, &request);
|
||||
return -reply.error;
|
||||
|
||||
}
|
||||
|
||||
static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
struct nbd_request request = { .type = NBD_CMD_WRITE };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
if (!bdrv_enable_write_cache(client->bs) &&
|
||||
(client->nbdflags & NBD_FLAG_SEND_FUA)) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, qiov, offset);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(client, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(client, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
/* qemu-nbd has a limit of slightly less than 1M per request. Try to
|
||||
* remain aligned to 4K. */
|
||||
#define NBD_MAX_SECTORS 2040
|
||||
|
||||
int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_readv_1(client, sector_num,
|
||||
NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += NBD_MAX_SECTORS * 512;
|
||||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_readv_1(client, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_writev_1(client, sector_num,
|
||||
NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += NBD_MAX_SECTORS * 512;
|
||||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_writev_1(client, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
int nbd_client_session_co_flush(NbdClientSession *client)
|
||||
{
|
||||
struct nbd_request request = { .type = NBD_CMD_FLUSH };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
if (!(client->nbdflags & NBD_FLAG_SEND_FLUSH)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (client->nbdflags & NBD_FLAG_SEND_FUA) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
||||
request.from = 0;
|
||||
request.len = 0;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(client, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(client, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
struct nbd_request request = { .type = NBD_CMD_TRIM };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) {
|
||||
return 0;
|
||||
}
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(client, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(client, &request);
|
||||
return -reply.error;
|
||||
|
||||
}
|
||||
|
||||
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 = {
|
||||
.type = NBD_CMD_DISC,
|
||||
.from = 0,
|
||||
.len = 0
|
||||
};
|
||||
|
||||
if (!client->bs) {
|
||||
return;
|
||||
}
|
||||
if (client->sock == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nbd_send_request(client->sock, &request);
|
||||
|
||||
nbd_teardown_connection(client);
|
||||
client->bs = NULL;
|
||||
}
|
||||
|
||||
int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
|
||||
int sock, const char *export)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* NBD handshake */
|
||||
logout("session init %s\n", export);
|
||||
qemu_set_block(sock);
|
||||
ret = nbd_receive_negotiate(sock, export,
|
||||
&client->nbdflags, &client->size,
|
||||
&client->blocksize);
|
||||
if (ret < 0) {
|
||||
logout("Failed to negotiate with the NBD server\n");
|
||||
closesocket(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&client->send_mutex);
|
||||
qemu_co_mutex_init(&client->free_sema);
|
||||
client->bs = bs;
|
||||
client->sock = sock;
|
||||
|
||||
/* 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));
|
||||
|
||||
logout("Established connection with NBD server\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifndef NBD_CLIENT_H
|
||||
#define NBD_CLIENT_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block/nbd.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
/* #define DEBUG_NBD */
|
||||
|
||||
#if defined(DEBUG_NBD)
|
||||
#define logout(fmt, ...) \
|
||||
fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define logout(fmt, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define MAX_NBD_REQUESTS 16
|
||||
|
||||
typedef struct NbdClientSession {
|
||||
int sock;
|
||||
uint32_t nbdflags;
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
|
||||
CoMutex send_mutex;
|
||||
CoMutex free_sema;
|
||||
Coroutine *send_coroutine;
|
||||
int in_flight;
|
||||
|
||||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
||||
struct nbd_reply reply;
|
||||
|
||||
bool is_unix;
|
||||
|
||||
BlockDriverState *bs;
|
||||
} NbdClientSession;
|
||||
|
||||
int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
|
||||
int sock, const char *export_name);
|
||||
void nbd_client_session_close(NbdClientSession *client);
|
||||
|
||||
int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors);
|
||||
int nbd_client_session_co_flush(NbdClientSession *client);
|
||||
int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
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 */
|
||||
+401
-106
@@ -26,7 +26,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "block/nbd-client.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/nbd.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
@@ -39,9 +40,37 @@
|
||||
|
||||
#define EN_OPTSTR ":exportname="
|
||||
|
||||
/* #define DEBUG_NBD */
|
||||
|
||||
#if defined(DEBUG_NBD)
|
||||
#define logout(fmt, ...) \
|
||||
fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define logout(fmt, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define MAX_NBD_REQUESTS 16
|
||||
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
|
||||
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
|
||||
|
||||
typedef struct BDRVNBDState {
|
||||
NbdClientSession client;
|
||||
int sock;
|
||||
uint32_t nbdflags;
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
|
||||
CoMutex send_mutex;
|
||||
CoMutex free_sema;
|
||||
Coroutine *send_coroutine;
|
||||
int in_flight;
|
||||
|
||||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
||||
struct nbd_reply reply;
|
||||
|
||||
bool is_unix;
|
||||
QemuOpts *socket_opts;
|
||||
|
||||
char *export_name; /* An NBD server may export several devices */
|
||||
} BDRVNBDState;
|
||||
|
||||
static int nbd_parse_uri(const char *filename, QDict *options)
|
||||
@@ -175,7 +204,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;
|
||||
}
|
||||
|
||||
@@ -188,49 +217,195 @@ out:
|
||||
g_free(file);
|
||||
}
|
||||
|
||||
static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
||||
Error **errp)
|
||||
static int nbd_config(BDRVNBDState *s, QDict *options)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
|
||||
if (qdict_haskey(options, "path")) {
|
||||
error_setg(errp, "path and host may not be used at the same time.");
|
||||
} else {
|
||||
error_setg(errp, "one of path and host must be specified.");
|
||||
if (qdict_haskey(options, "path")) {
|
||||
if (qdict_haskey(options, "host")) {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "path and host may not "
|
||||
"be used at the same time.");
|
||||
return -EINVAL;
|
||||
}
|
||||
return;
|
||||
s->is_unix = true;
|
||||
} else if (qdict_haskey(options, "host")) {
|
||||
s->is_unix = false;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s->client.is_unix = qdict_haskey(options, "path");
|
||||
s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
|
||||
&error_abort);
|
||||
s->socket_opts = qemu_opts_create_nofail(&socket_optslist);
|
||||
|
||||
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!qemu_opt_get(s->socket_opts, "port")) {
|
||||
qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT);
|
||||
}
|
||||
|
||||
*export = g_strdup(qdict_get_try_str(options, "export"));
|
||||
if (*export) {
|
||||
s->export_name = g_strdup(qdict_get_try_str(options, "export"));
|
||||
if (s->export_name) {
|
||||
qdict_del(options, "export");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Poor man semaphore. The free_sema is locked when no other request
|
||||
* can be accepted, and unlocked after receiving one reply. */
|
||||
if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
|
||||
qemu_co_mutex_lock(&s->free_sema);
|
||||
assert(s->in_flight < MAX_NBD_REQUESTS);
|
||||
}
|
||||
s->in_flight++;
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i] == NULL) {
|
||||
s->recv_coroutine[i] = qemu_coroutine_self();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < MAX_NBD_REQUESTS);
|
||||
request->handle = INDEX_TO_HANDLE(s, i);
|
||||
}
|
||||
|
||||
static void nbd_reply_ready(void *opaque)
|
||||
{
|
||||
BDRVNBDState *s = opaque;
|
||||
uint64_t i;
|
||||
int ret;
|
||||
|
||||
if (s->reply.handle == 0) {
|
||||
/* No reply already in flight. Fetch a header. It is possible
|
||||
* that another thread has done the same thing in parallel, so
|
||||
* the socket is not readable anymore.
|
||||
*/
|
||||
ret = nbd_receive_reply(s->sock, &s->reply);
|
||||
if (ret == -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
if (ret < 0) {
|
||||
s->reply.handle = 0;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* There's no need for a mutex on the receive side, because the
|
||||
* handler acts as a synchronization point and ensures that only
|
||||
* one coroutine is called until the reply finishes. */
|
||||
i = HANDLE_TO_INDEX(s, s->reply.handle);
|
||||
if (i >= MAX_NBD_REQUESTS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
fail:
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
|
||||
static void nbd_restart_write(void *opaque)
|
||||
{
|
||||
BDRVNBDState *s = opaque;
|
||||
qemu_coroutine_enter(s->send_coroutine, NULL);
|
||||
}
|
||||
|
||||
static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
{
|
||||
int rc, ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->send_mutex);
|
||||
s->send_coroutine = qemu_coroutine_self();
|
||||
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);
|
||||
}
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
if (rc >= 0) {
|
||||
ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (ret != request->len) {
|
||||
rc = -EIO;
|
||||
}
|
||||
}
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 0);
|
||||
}
|
||||
} else {
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request,
|
||||
struct nbd_reply *reply,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Wait until we're woken up by the read handler. TODO: perhaps
|
||||
* peek at the next reply and avoid yielding if it's ours? */
|
||||
qemu_coroutine_yield();
|
||||
*reply = s->reply;
|
||||
if (reply->handle != request->handle) {
|
||||
reply->error = EIO;
|
||||
} else {
|
||||
if (qiov && reply->error == 0) {
|
||||
ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (ret != request->len) {
|
||||
reply->error = EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the read handler to read another header. */
|
||||
s->reply.handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void nbd_coroutine_end(BDRVNBDState *s, struct nbd_request *request)
|
||||
{
|
||||
int i = HANDLE_TO_INDEX(s, request->handle);
|
||||
s->recv_coroutine[i] = NULL;
|
||||
if (s->in_flight-- == MAX_NBD_REQUESTS) {
|
||||
qemu_co_mutex_unlock(&s->free_sema);
|
||||
}
|
||||
}
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
int sock;
|
||||
int ret;
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
|
||||
if (s->client.is_unix) {
|
||||
sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL);
|
||||
if (s->is_unix) {
|
||||
sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
|
||||
} else {
|
||||
sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL);
|
||||
sock = tcp_socket_outgoing_opts(s->socket_opts);
|
||||
if (sock >= 0) {
|
||||
socket_set_nodelay(sock);
|
||||
}
|
||||
@@ -242,148 +417,268 @@ static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return sock;
|
||||
/* NBD handshake */
|
||||
ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
|
||||
&blocksize);
|
||||
if (ret < 0) {
|
||||
logout("Failed to negotiate with the NBD server\n");
|
||||
closesocket(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Now that we're connected, set the socket to be non-blocking and
|
||||
* kick the reply mechanism. */
|
||||
qemu_set_nonblock(sock);
|
||||
qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, s);
|
||||
|
||||
s->sock = sock;
|
||||
s->size = size;
|
||||
s->blocksize = blocksize;
|
||||
|
||||
logout("Established connection with NBD server\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
|
||||
request.type = NBD_CMD_DISC;
|
||||
request.from = 0;
|
||||
request.len = 0;
|
||||
nbd_send_request(s->sock, &request);
|
||||
|
||||
qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL);
|
||||
closesocket(s->sock);
|
||||
}
|
||||
|
||||
static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
char *export = NULL;
|
||||
int result, sock;
|
||||
Error *local_err = NULL;
|
||||
int result;
|
||||
|
||||
qemu_co_mutex_init(&s->send_mutex);
|
||||
qemu_co_mutex_init(&s->free_sema);
|
||||
|
||||
/* Pop the config into our state object. Exit if invalid. */
|
||||
nbd_config(s, options, &export, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
result = nbd_config(s, options);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* establish TCP connection, return error if it fails
|
||||
* TODO: Configurable retry-until-timeout behaviour.
|
||||
*/
|
||||
sock = nbd_establish_connection(bs, errp);
|
||||
if (sock < 0) {
|
||||
return sock;
|
||||
}
|
||||
result = nbd_establish_connection(bs);
|
||||
|
||||
/* NBD handshake */
|
||||
result = nbd_client_session_init(&s->client, bs, sock, export);
|
||||
g_free(export);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
request.type = NBD_CMD_READ;
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, qiov, offset);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
|
||||
}
|
||||
|
||||
static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
request.type = NBD_CMD_WRITE;
|
||||
if (!bdrv_enable_write_cache(bs) && (s->nbdflags & NBD_FLAG_SEND_FUA)) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, qiov, offset);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
/* qemu-nbd has a limit of slightly less than 1M per request. Try to
|
||||
* remain aligned to 4K. */
|
||||
#define NBD_MAX_SECTORS 2040
|
||||
|
||||
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
return nbd_client_session_co_readv(&s->client, sector_num,
|
||||
nb_sectors, qiov);
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += NBD_MAX_SECTORS * 512;
|
||||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
return nbd_client_session_co_writev(&s->client, sector_num,
|
||||
nb_sectors, qiov);
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += NBD_MAX_SECTORS * 512;
|
||||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
static int nbd_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
return nbd_client_session_co_flush(&s->client);
|
||||
if (!(s->nbdflags & NBD_FLAG_SEND_FLUSH)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
request.type = NBD_CMD_FLUSH;
|
||||
if (s->nbdflags & NBD_FLAG_SEND_FUA) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
||||
request.from = 0;
|
||||
request.len = 0;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
return nbd_client_session_co_discard(&s->client, sector_num,
|
||||
nb_sectors);
|
||||
if (!(s->nbdflags & NBD_FLAG_SEND_TRIM)) {
|
||||
return 0;
|
||||
}
|
||||
request.type = NBD_CMD_TRIM;
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
static void nbd_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
g_free(s->export_name);
|
||||
qemu_opts_del(s->socket_opts);
|
||||
nbd_client_session_close(&s->client);
|
||||
|
||||
nbd_teardown_connection(bs);
|
||||
}
|
||||
|
||||
static int64_t nbd_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
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);
|
||||
return s->size;
|
||||
}
|
||||
|
||||
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,
|
||||
.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,
|
||||
.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,
|
||||
.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)
|
||||
|
||||
-500
@@ -1,500 +0,0 @@
|
||||
/*
|
||||
* QEMU Block driver for native access to files on NFS shares
|
||||
*
|
||||
* Copyright (c) 2014 Peter Lieven <pl@kamp.de>
|
||||
*
|
||||
* 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 "config-host.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "block/block_int.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include <nfsc/libnfs.h>
|
||||
|
||||
typedef struct NFSClient {
|
||||
struct nfs_context *context;
|
||||
struct nfsfh *fh;
|
||||
int events;
|
||||
bool has_zero_init;
|
||||
AioContext *aio_context;
|
||||
} NFSClient;
|
||||
|
||||
typedef struct NFSRPC {
|
||||
int ret;
|
||||
int complete;
|
||||
QEMUIOVector *iov;
|
||||
struct stat *st;
|
||||
Coroutine *co;
|
||||
QEMUBH *bh;
|
||||
NFSClient *client;
|
||||
} NFSRPC;
|
||||
|
||||
static void nfs_process_read(void *arg);
|
||||
static void nfs_process_write(void *arg);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
client->events = ev;
|
||||
}
|
||||
|
||||
static void nfs_process_read(void *arg)
|
||||
{
|
||||
NFSClient *client = arg;
|
||||
nfs_service(client->context, POLLIN);
|
||||
nfs_set_events(client);
|
||||
}
|
||||
|
||||
static void nfs_process_write(void *arg)
|
||||
{
|
||||
NFSClient *client = arg;
|
||||
nfs_service(client->context, POLLOUT);
|
||||
nfs_set_events(client);
|
||||
}
|
||||
|
||||
static void nfs_co_init_task(NFSClient *client, NFSRPC *task)
|
||||
{
|
||||
*task = (NFSRPC) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.client = client,
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
|
||||
void *private_data)
|
||||
{
|
||||
NFSRPC *task = private_data;
|
||||
task->ret = ret;
|
||||
if (task->ret > 0 && task->iov) {
|
||||
if (task->ret <= task->iov->size) {
|
||||
qemu_iovec_from_buf(task->iov, 0, data, task->ret);
|
||||
} else {
|
||||
task->ret = -EIO;
|
||||
}
|
||||
}
|
||||
if (task->ret == 0 && task->st) {
|
||||
memcpy(task->st, data, sizeof(struct stat));
|
||||
}
|
||||
if (task->ret < 0) {
|
||||
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);
|
||||
qemu_bh_schedule(task->bh);
|
||||
} else {
|
||||
task->complete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn nfs_co_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *iov)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
NFSRPC task;
|
||||
|
||||
nfs_co_init_task(client, &task);
|
||||
task.iov = iov;
|
||||
|
||||
if (nfs_pread_async(client->context, client->fh,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE,
|
||||
nfs_co_generic_cb, &task) != 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while (!task.complete) {
|
||||
nfs_set_events(client);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
if (task.ret < 0) {
|
||||
return task.ret;
|
||||
}
|
||||
|
||||
/* zero pad short reads */
|
||||
if (task.ret < iov->size) {
|
||||
qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn nfs_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *iov)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
NFSRPC task;
|
||||
char *buf = NULL;
|
||||
|
||||
nfs_co_init_task(client, &task);
|
||||
|
||||
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,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE,
|
||||
buf, nfs_co_generic_cb, &task) != 0) {
|
||||
g_free(buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while (!task.complete) {
|
||||
nfs_set_events(client);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
g_free(buf);
|
||||
|
||||
if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) {
|
||||
return task.ret < 0 ? task.ret : -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
NFSRPC task;
|
||||
|
||||
nfs_co_init_task(client, &task);
|
||||
|
||||
if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
|
||||
&task) != 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while (!task.complete) {
|
||||
nfs_set_events(client);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
return task.ret;
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "nfs",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "URL to the NFS file",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
nfs_destroy_context(client->context);
|
||||
}
|
||||
memset(client, 0, sizeof(NFSClient));
|
||||
}
|
||||
|
||||
static void nfs_file_close(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
nfs_client_close(client);
|
||||
}
|
||||
|
||||
static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
int ret = -EINVAL, i;
|
||||
struct stat st;
|
||||
URI *uri;
|
||||
QueryParams *qp = NULL;
|
||||
char *file = NULL, *strp = NULL;
|
||||
|
||||
uri = uri_parse(filename);
|
||||
if (!uri) {
|
||||
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");
|
||||
goto fail;
|
||||
}
|
||||
file = g_strdup(strp);
|
||||
*strp = 0;
|
||||
|
||||
client->context = nfs_init_context();
|
||||
if (client->context == NULL) {
|
||||
error_setg(errp, "Failed to init NFS context");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||
qp->p[i].name);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nfs_mount(client->context, uri->server, uri->path);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to mount nfs share: %s",
|
||||
nfs_get_error(client->context));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (flags & O_CREAT) {
|
||||
ret = nfs_creat(client->context, file, 0600, &client->fh);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to create file: %s",
|
||||
nfs_get_error(client->context));
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
ret = nfs_open(client->context, file, flags, &client->fh);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to open file : %s",
|
||||
nfs_get_error(client->context));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nfs_fstat(client->context, client->fh, &st);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to fstat file: %s",
|
||||
nfs_get_error(client->context));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
|
||||
client->has_zero_init = S_ISREG(st.st_mode);
|
||||
goto out;
|
||||
fail:
|
||||
nfs_client_close(client);
|
||||
out:
|
||||
if (qp) {
|
||||
query_params_free(qp);
|
||||
}
|
||||
uri_free(uri);
|
||||
g_free(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp) {
|
||||
NFSClient *client = bs->opaque;
|
||||
int64_t ret;
|
||||
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) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = nfs_client_open(client, qemu_opt_get(opts, "filename"),
|
||||
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
bs->total_sectors = 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)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t total_size = 0;
|
||||
NFSClient *client = g_malloc0(sizeof(NFSClient));
|
||||
|
||||
client->aio_context = qemu_get_aio_context();
|
||||
|
||||
/* Read out options */
|
||||
total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
|
||||
ret = nfs_client_open(client, url, O_CREAT, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = nfs_ftruncate(client->context, client->fh, total_size);
|
||||
nfs_client_close(client);
|
||||
out:
|
||||
g_free(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
return client->has_zero_init;
|
||||
}
|
||||
|
||||
static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
NFSRPC task = {0};
|
||||
struct stat st;
|
||||
|
||||
task.st = &st;
|
||||
if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb,
|
||||
&task) != 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while (!task.complete) {
|
||||
nfs_set_events(client);
|
||||
aio_poll(client->aio_context, true);
|
||||
}
|
||||
|
||||
return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize);
|
||||
}
|
||||
|
||||
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
return nfs_ftruncate(client->context, client->fh, offset);
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_nfs = {
|
||||
.format_name = "nfs",
|
||||
.protocol_name = "nfs",
|
||||
|
||||
.instance_size = sizeof(NFSClient),
|
||||
.bdrv_needs_filename = true,
|
||||
.create_opts = &nfs_create_opts,
|
||||
|
||||
.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_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,
|
||||
};
|
||||
|
||||
static void nfs_block_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_nfs);
|
||||
}
|
||||
|
||||
block_init(nfs_block_init);
|
||||
+1
-2
@@ -85,8 +85,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
|
||||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
|
||||
error_setg(errp, "Image not in Parallels format");
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
+65
-72
@@ -29,61 +29,6 @@
|
||||
#include "qapi/qmp-output-visitor.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
|
||||
BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
|
||||
{
|
||||
BlockDeviceInfo *info = g_malloc0(sizeof(*info));
|
||||
|
||||
info->file = g_strdup(bs->filename);
|
||||
info->ro = bs->read_only;
|
||||
info->drv = g_strdup(bs->drv->format_name);
|
||||
info->encrypted = bs->encrypted;
|
||||
info->encryption_key_missing = bdrv_key_required(bs);
|
||||
|
||||
if (bs->node_name[0]) {
|
||||
info->has_node_name = true;
|
||||
info->node_name = g_strdup(bs->node_name);
|
||||
}
|
||||
|
||||
if (bs->backing_file[0]) {
|
||||
info->has_backing_file = true;
|
||||
info->backing_file = g_strdup(bs->backing_file);
|
||||
}
|
||||
|
||||
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||
info->detect_zeroes = bs->detect_zeroes;
|
||||
|
||||
if (bs->io_limits_enabled) {
|
||||
ThrottleConfig cfg;
|
||||
throttle_get_config(&bs->throttle_state, &cfg);
|
||||
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||
info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
|
||||
|
||||
info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
|
||||
info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
|
||||
info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
|
||||
|
||||
info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||
info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||
info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max;
|
||||
info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max;
|
||||
info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||
info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||
|
||||
info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||
info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||
info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max;
|
||||
info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max;
|
||||
info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||
info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||
|
||||
info->has_iops_size = cfg.op_size;
|
||||
info->iops_size = cfg.op_size;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 on success, with *p_list either set to describe snapshot
|
||||
* information, or NULL because there are no snapshots. Returns -errno on
|
||||
@@ -259,20 +204,76 @@ void bdrv_query_info(BlockDriverState *bs,
|
||||
info->io_status = bs->iostatus;
|
||||
}
|
||||
|
||||
if (!QLIST_EMPTY(&bs->dirty_bitmaps)) {
|
||||
info->has_dirty_bitmaps = true;
|
||||
info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs);
|
||||
if (bs->dirty_bitmap) {
|
||||
info->has_dirty = true;
|
||||
info->dirty = g_malloc0(sizeof(*info->dirty));
|
||||
info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE;
|
||||
info->dirty->granularity =
|
||||
((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap));
|
||||
}
|
||||
|
||||
if (bs->drv) {
|
||||
info->has_inserted = true;
|
||||
info->inserted = bdrv_block_device_info(bs);
|
||||
info->inserted = g_malloc0(sizeof(*info->inserted));
|
||||
info->inserted->file = g_strdup(bs->filename);
|
||||
info->inserted->ro = bs->read_only;
|
||||
info->inserted->drv = g_strdup(bs->drv->format_name);
|
||||
info->inserted->encrypted = bs->encrypted;
|
||||
info->inserted->encryption_key_missing = bdrv_key_required(bs);
|
||||
|
||||
if (bs->backing_file[0]) {
|
||||
info->inserted->has_backing_file = true;
|
||||
info->inserted->backing_file = g_strdup(bs->backing_file);
|
||||
}
|
||||
|
||||
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||
|
||||
if (bs->io_limits_enabled) {
|
||||
ThrottleConfig cfg;
|
||||
throttle_get_config(&bs->throttle_state, &cfg);
|
||||
info->inserted->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||
info->inserted->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||
info->inserted->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
|
||||
|
||||
info->inserted->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
|
||||
info->inserted->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
|
||||
info->inserted->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
|
||||
|
||||
info->inserted->has_bps_max =
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||
info->inserted->bps_max =
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||
info->inserted->has_bps_rd_max =
|
||||
cfg.buckets[THROTTLE_BPS_READ].max;
|
||||
info->inserted->bps_rd_max =
|
||||
cfg.buckets[THROTTLE_BPS_READ].max;
|
||||
info->inserted->has_bps_wr_max =
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||
info->inserted->bps_wr_max =
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||
|
||||
info->inserted->has_iops_max =
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||
info->inserted->iops_max =
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||
info->inserted->has_iops_rd_max =
|
||||
cfg.buckets[THROTTLE_OPS_READ].max;
|
||||
info->inserted->iops_rd_max =
|
||||
cfg.buckets[THROTTLE_OPS_READ].max;
|
||||
info->inserted->has_iops_wr_max =
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||
info->inserted->iops_wr_max =
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||
|
||||
info->inserted->has_iops_size = cfg.op_size;
|
||||
info->inserted->iops_size = cfg.op_size;
|
||||
}
|
||||
|
||||
bs0 = bs;
|
||||
p_image_info = &info->inserted->image;
|
||||
while (1) {
|
||||
bdrv_query_image_info(bs0, p_image_info, &local_err);
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto err;
|
||||
}
|
||||
@@ -293,7 +294,7 @@ void bdrv_query_info(BlockDriverState *bs,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||
{
|
||||
BlockStats *s;
|
||||
|
||||
@@ -320,11 +321,6 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||
s->parent = bdrv_query_stats(bs->file);
|
||||
}
|
||||
|
||||
if (bs->backing_hd) {
|
||||
s->has_backing = true;
|
||||
s->backing = bdrv_query_stats(bs->backing_hd);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -337,7 +333,7 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
BlockInfoList *info = g_malloc0(sizeof(*info));
|
||||
bdrv_query_info(bs, &info->value, &local_err);
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto err;
|
||||
}
|
||||
@@ -360,11 +356,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;
|
||||
@@ -538,11 +530,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");
|
||||
|
||||
+46
-50
@@ -115,16 +115,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
error_setg(errp, "Image not in qcow format");
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
if (header.version != QCOW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version), "QCOW version %" PRIu32,
|
||||
header.version);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "qcow", version);
|
||||
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "qcow", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -149,7 +147,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
if (header.crypt_method > QCOW_CRYPT_AES) {
|
||||
error_setg(errp, "invalid encryption method in qcow header");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -693,37 +690,43 @@ 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 = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512;
|
||||
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;
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcow_bs = NULL;
|
||||
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(qcow_bs, 0);
|
||||
@@ -748,7 +751,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
backing_file = NULL;
|
||||
}
|
||||
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||
unmodified sectors */
|
||||
unmodifyed sectors */
|
||||
header.l2_bits = 12; /* 32 KB L2 tables */
|
||||
} else {
|
||||
header.cluster_bits = 12; /* 4 KB clusters */
|
||||
@@ -794,8 +797,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;
|
||||
}
|
||||
|
||||
@@ -908,28 +909,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 = {
|
||||
@@ -938,10 +935,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,
|
||||
@@ -952,7 +948,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)
|
||||
|
||||
+18
-59
@@ -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 {
|
||||
@@ -158,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,11 +371,6 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
|
||||
|
||||
if (!bs->drv) {
|
||||
ret = -ENOMEDIUM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Call .bdrv_co_readv() directly instead of using the public block-layer
|
||||
* interface. This avoids double I/O throttling and request tracking,
|
||||
* which can lead to deadlock when block layer copy-on-read is enabled.
|
||||
@@ -501,7 +487,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
return -EIO;
|
||||
}
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
@@ -1188,7 +1173,7 @@ fail:
|
||||
* Return 0 on success and -errno in error cases
|
||||
*/
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t start, remaining;
|
||||
@@ -1196,13 +1181,15 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t cur_bytes;
|
||||
int ret;
|
||||
|
||||
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *num);
|
||||
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset,
|
||||
n_start, n_end);
|
||||
|
||||
assert((offset & ~BDRV_SECTOR_MASK) == 0);
|
||||
assert(n_start * BDRV_SECTOR_SIZE == offset_into_cluster(s, offset));
|
||||
offset = start_of_cluster(s, offset);
|
||||
|
||||
again:
|
||||
start = offset;
|
||||
remaining = (uint64_t)*num << BDRV_SECTOR_BITS;
|
||||
start = offset + (n_start << BDRV_SECTOR_BITS);
|
||||
remaining = (n_end - n_start) << BDRV_SECTOR_BITS;
|
||||
cluster_offset = 0;
|
||||
*host_offset = 0;
|
||||
cur_bytes = 0;
|
||||
@@ -1288,7 +1275,7 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
*num -= remaining >> BDRV_SECTOR_BITS;
|
||||
*num = (n_end - n_start) - (remaining >> BDRV_SECTOR_BITS);
|
||||
assert(*num > 0);
|
||||
assert(*host_offset != 0);
|
||||
|
||||
@@ -1370,47 +1357,19 @@ 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]);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
switch (qcow2_get_cluster_type(old_l2_entry)) {
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (!bs->backing_hd) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
continue;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
if ((old_offset & L2E_OFFSET_MASK) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* First remove L2 entries */
|
||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
@@ -1433,7 +1392,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
/* Round start up and end down */
|
||||
offset = align_offset(offset, s->cluster_size);
|
||||
end_offset = start_of_cluster(s, end_offset);
|
||||
end_offset &= ~(s->cluster_size - 1);
|
||||
|
||||
if (offset > end_offset) {
|
||||
return 0;
|
||||
@@ -1645,7 +1604,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
}
|
||||
|
||||
ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
s->cluster_sectors, 0);
|
||||
s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
if (!preallocated) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
|
||||
+35
-54
@@ -27,7 +27,6 @@
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
|
||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
@@ -99,8 +98,7 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
|
||||
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
|
||||
if (refcount_table_index >= s->refcount_table_size)
|
||||
return 0;
|
||||
refcount_block_offset =
|
||||
s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK;
|
||||
refcount_block_offset = s->refcount_table[refcount_table_index];
|
||||
if (!refcount_block_offset)
|
||||
return 0;
|
||||
|
||||
@@ -525,8 +523,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
s->l2_table_cache);
|
||||
}
|
||||
|
||||
start = start_of_cluster(s, offset);
|
||||
last = start_of_cluster(s, offset + length - 1);
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + length - 1) & ~(s->cluster_size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size)
|
||||
{
|
||||
@@ -654,15 +652,6 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure that all offsets in the "allocated" range are representable
|
||||
* in an int64_t */
|
||||
if (s->free_cluster_index > 0 &&
|
||||
s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits))
|
||||
{
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC2
|
||||
fprintf(stderr, "alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n",
|
||||
size,
|
||||
@@ -750,7 +739,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
}
|
||||
redo:
|
||||
free_in_cluster = s->cluster_size -
|
||||
offset_into_cluster(s, s->free_byte_offset);
|
||||
(s->free_byte_offset & (s->cluster_size - 1));
|
||||
if (size <= free_in_cluster) {
|
||||
/* enough space in current cluster */
|
||||
offset = s->free_byte_offset;
|
||||
@@ -758,7 +747,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
free_in_cluster -= size;
|
||||
if (free_in_cluster == 0)
|
||||
s->free_byte_offset = 0;
|
||||
if (offset_into_cluster(s, offset) != 0)
|
||||
if ((offset & (s->cluster_size - 1)) != 0)
|
||||
qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
} else {
|
||||
@@ -766,7 +755,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
cluster_offset = start_of_cluster(s, s->free_byte_offset);
|
||||
cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1);
|
||||
if ((cluster_offset + s->cluster_size) == offset) {
|
||||
/* we are lucky: contiguous data */
|
||||
offset = s->free_byte_offset;
|
||||
@@ -1035,8 +1024,8 @@ static void inc_refcounts(BlockDriverState *bs,
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
start = start_of_cluster(s, offset);
|
||||
last = start_of_cluster(s, offset + size - 1);
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + size - 1) & ~(s->cluster_size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size) {
|
||||
k = cluster_offset >> s->cluster_bits;
|
||||
@@ -1143,7 +1132,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
offset, s->cluster_size);
|
||||
|
||||
/* Correct offsets are cluster aligned */
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
|
||||
"properly aligned; L2 entry corrupted.\n", offset);
|
||||
res->corruptions++;
|
||||
@@ -1215,7 +1204,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
l2_offset, s->cluster_size);
|
||||
|
||||
/* L2 tables are cluster aligned */
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
if (l2_offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not "
|
||||
"cluster aligned; L1 entry corrupted\n", l2_offset);
|
||||
res->corruptions++;
|
||||
@@ -1397,7 +1386,7 @@ static int write_reftable_entry(BlockDriverState *bs, int rt_index)
|
||||
* does _not_ decrement the reference count for the currently occupied cluster.
|
||||
*
|
||||
* This function prints an informative message to stderr on error (and returns
|
||||
* -errno); on success, the offset of the newly allocated cluster is returned.
|
||||
* -errno); on success, 0 is returned.
|
||||
*/
|
||||
static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
||||
uint64_t offset)
|
||||
@@ -1413,14 +1402,14 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
||||
fprintf(stderr, "Could not allocate new cluster: %s\n",
|
||||
strerror(-new_offset));
|
||||
ret = new_offset;
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* fetch current refcount block content */
|
||||
ret = qcow2_cache_get(bs, s->refcount_block_cache, offset, &refcount_block);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not fetch refcount block: %s\n", strerror(-ret));
|
||||
goto fail_free_cluster;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* new block has not yet been entered into refcount table, therefore it is
|
||||
@@ -1431,7 +1420,8 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
||||
"check failed: %s\n", strerror(-ret));
|
||||
/* the image will be marked corrupt, so don't even attempt on freeing
|
||||
* the cluster */
|
||||
goto done;
|
||||
new_offset = 0;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* write to new block */
|
||||
@@ -1439,37 +1429,34 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
||||
s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not write refcount block: %s\n", strerror(-ret));
|
||||
goto fail_free_cluster;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* update refcount table */
|
||||
assert(!offset_into_cluster(s, new_offset));
|
||||
assert(!(new_offset & (s->cluster_size - 1)));
|
||||
s->refcount_table[reftable_index] = new_offset;
|
||||
ret = write_reftable_entry(bs, reftable_index);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not update refcount table: %s\n",
|
||||
strerror(-ret));
|
||||
goto fail_free_cluster;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
||||
fail_free_cluster:
|
||||
qcow2_free_clusters(bs, new_offset, s->cluster_size, QCOW2_DISCARD_OTHER);
|
||||
|
||||
done:
|
||||
fail:
|
||||
if (new_offset && (ret < 0)) {
|
||||
qcow2_free_clusters(bs, new_offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
}
|
||||
if (refcount_block) {
|
||||
/* This should never fail, as it would only do so if the given refcount
|
||||
* block cannot be found in the cache. As this is impossible as long as
|
||||
* there are no bugs, assert the success. */
|
||||
int tmp = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
|
||||
assert(tmp == 0);
|
||||
if (ret < 0) {
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
|
||||
} else {
|
||||
ret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return new_offset;
|
||||
}
|
||||
|
||||
@@ -1490,11 +1477,6 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
int ret;
|
||||
|
||||
size = bdrv_getlength(bs->file);
|
||||
if (size < 0) {
|
||||
res->check_errors++;
|
||||
return size;
|
||||
}
|
||||
|
||||
nb_clusters = size_to_clusters(s, size);
|
||||
if (nb_clusters > INT_MAX) {
|
||||
res->check_errors++;
|
||||
@@ -1540,7 +1522,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
cluster = offset >> s->cluster_bits;
|
||||
|
||||
/* Refcount blocks are cluster aligned */
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
|
||||
"cluster aligned; refcount table entry corrupted\n", i);
|
||||
res->corruptions++;
|
||||
@@ -1808,6 +1790,7 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
} else if (ret > 0) {
|
||||
int metadata_ol_bitnr = ffs(ret) - 1;
|
||||
char *message;
|
||||
QObject *data;
|
||||
|
||||
assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR);
|
||||
|
||||
@@ -1816,14 +1799,12 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
metadata_ol_names[metadata_ol_bitnr]);
|
||||
message = g_strdup_printf("Prevented %s overwrite",
|
||||
metadata_ol_names[metadata_ol_bitnr]);
|
||||
qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs),
|
||||
message,
|
||||
true,
|
||||
offset,
|
||||
true,
|
||||
size,
|
||||
&error_abort);
|
||||
data = qobject_from_jsonf("{ 'device': %s, 'msg': %s, 'offset': %"
|
||||
PRId64 ", 'size': %" PRId64 " }", bs->device_name, message,
|
||||
offset, size);
|
||||
monitor_protocol_event(QEVENT_BLOCK_IMAGE_CORRUPTED, data);
|
||||
g_free(message);
|
||||
qobject_decref(data);
|
||||
|
||||
qcow2_mark_corrupt(bs);
|
||||
bs->drv = NULL; /* make BDS unusable */
|
||||
|
||||
+7
-28
@@ -116,14 +116,8 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
}
|
||||
offset += name_size;
|
||||
sn->name[name_size] = '\0';
|
||||
|
||||
if (offset - s->snapshots_offset > QCOW_MAX_SNAPSHOTS_SIZE) {
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
assert(offset - s->snapshots_offset <= INT_MAX);
|
||||
s->snapshots_size = offset - s->snapshots_offset;
|
||||
return 0;
|
||||
|
||||
@@ -144,7 +138,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
uint32_t nb_snapshots;
|
||||
uint64_t snapshots_offset;
|
||||
} QEMU_PACKED header_data;
|
||||
int64_t offset, snapshots_offset = 0;
|
||||
int64_t offset, snapshots_offset;
|
||||
int ret;
|
||||
|
||||
/* compute the size of the snapshots */
|
||||
@@ -156,14 +150,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
offset += sizeof(extra);
|
||||
offset += strlen(sn->id_str);
|
||||
offset += strlen(sn->name);
|
||||
|
||||
if (offset > QCOW_MAX_SNAPSHOTS_SIZE) {
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
assert(offset <= INT_MAX);
|
||||
snapshots_size = offset;
|
||||
|
||||
/* Allocate space for the new snapshot list */
|
||||
@@ -598,8 +585,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
s->nb_snapshots--;
|
||||
ret = qcow2_write_snapshots(bs);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to remove snapshot from snapshot list");
|
||||
error_setg(errp, "Failed to remove snapshot from snapshot list");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -617,7 +603,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
|
||||
sn.l1_size, -1);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table");
|
||||
error_setg(errp, "Failed to free the cluster and L1 table");
|
||||
return ret;
|
||||
}
|
||||
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
|
||||
@@ -626,8 +612,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
/* must update the copied flag on the current cluster offsets */
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to update snapshot status in disk");
|
||||
error_setg(errp, "Failed to update snapshot status in disk");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -669,10 +654,7 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
return s->nb_snapshots;
|
||||
}
|
||||
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||
{
|
||||
int i, snapshot_index;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -684,17 +666,15 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
assert(bs->read_only);
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
|
||||
if (snapshot_index < 0) {
|
||||
error_setg(errp,
|
||||
"Can't find snapshot");
|
||||
return -ENOENT;
|
||||
}
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
|
||||
/* Allocate and read in the snapshot's L1 table */
|
||||
if (sn->l1_size > QCOW_MAX_L1_SIZE) {
|
||||
error_setg(errp, "Snapshot L1 table too large");
|
||||
error_report("Snapshot L1 table too large");
|
||||
return -EFBIG;
|
||||
}
|
||||
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||
@@ -702,7 +682,6 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
|
||||
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");
|
||||
g_free(new_l1_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+204
-277
@@ -31,7 +31,6 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/option_int.h"
|
||||
|
||||
/*
|
||||
Differences with QCOW:
|
||||
@@ -114,7 +113,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
#ifdef DEBUG_EXT
|
||||
printf("ext.magic = 0x%x\n", ext.magic);
|
||||
#endif
|
||||
if (offset > end_offset || ext.len > end_offset - offset) {
|
||||
if (ext.len > end_offset - offset) {
|
||||
error_setg(errp, "Header extension too large");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -125,9 +124,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
|
||||
case QCOW2_EXT_MAGIC_BACKING_FORMAT:
|
||||
if (ext.len >= sizeof(bs->backing_format)) {
|
||||
error_setg(errp, "ERROR: ext_backing_format: len=%" PRIu32
|
||||
" too large (>=%zu)", ext.len,
|
||||
sizeof(bs->backing_format));
|
||||
error_setg(errp, "ERROR: ext_backing_format: len=%u too large"
|
||||
" (>=%zu)", ext.len, sizeof(bs->backing_format));
|
||||
return 2;
|
||||
}
|
||||
ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len);
|
||||
@@ -210,31 +208,20 @@ static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs,
|
||||
static void report_unsupported_feature(BlockDriverState *bs,
|
||||
Error **errp, Qcow2Feature *table, uint64_t mask)
|
||||
{
|
||||
char *features = g_strdup("");
|
||||
char *old;
|
||||
|
||||
while (table && table->name[0] != '\0') {
|
||||
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
|
||||
if (mask & (1ULL << table->bit)) {
|
||||
old = features;
|
||||
features = g_strdup_printf("%s%s%.46s", old, *old ? ", " : "",
|
||||
table->name);
|
||||
g_free(old);
|
||||
mask &= ~(1ULL << table->bit);
|
||||
if (mask & (1 << table->bit)) {
|
||||
report_unsupported(bs, errp, "%.46s", table->name);
|
||||
mask &= ~(1 << table->bit);
|
||||
}
|
||||
}
|
||||
table++;
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
old = features;
|
||||
features = g_strdup_printf("%s%sUnknown incompatible feature: %" PRIx64,
|
||||
old, *old ? ", " : "", mask);
|
||||
g_free(old);
|
||||
report_unsupported(bs, errp, "Unknown incompatible feature: %" PRIx64,
|
||||
mask);
|
||||
}
|
||||
|
||||
report_unsupported(bs, errp, "%s", features);
|
||||
g_free(features);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -492,11 +479,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
error_setg(errp, "Image is not in qcow2 format");
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
if (header.version < 2 || header.version > 3) {
|
||||
report_unsupported(bs, errp, "QCOW version %" PRIu32, header.version);
|
||||
report_unsupported(bs, errp, "QCOW version %d", header.version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -506,8 +493,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Initialise cluster size */
|
||||
if (header.cluster_bits < MIN_CLUSTER_BITS ||
|
||||
header.cluster_bits > MAX_CLUSTER_BITS) {
|
||||
error_setg(errp, "Unsupported cluster size: 2^%" PRIu32,
|
||||
header.cluster_bits);
|
||||
error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -580,7 +566,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->incompatible_features &
|
||||
~QCOW2_INCOMPAT_MASK);
|
||||
ret = -ENOTSUP;
|
||||
g_free(feature_table);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -605,7 +590,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->refcount_order = header.refcount_order;
|
||||
|
||||
if (header.crypt_method > QCOW_CRYPT_AES) {
|
||||
error_setg(errp, "Unsupported encryption method: %" PRIu32,
|
||||
error_setg(errp, "Unsupported encryption method: %i",
|
||||
header.crypt_method);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@@ -757,7 +742,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Clear unknown autoclear feature bits */
|
||||
if (!bs->read_only && !(flags & BDRV_O_INCOMING) && s->autoclear_features) {
|
||||
if (!bs->read_only && s->autoclear_features != 0) {
|
||||
s->autoclear_features = 0;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
@@ -770,7 +755,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
/* Repair image if dirty */
|
||||
if (!(flags & (BDRV_O_CHECK | BDRV_O_INCOMING)) && !bs->read_only &&
|
||||
if (!(flags & BDRV_O_CHECK) && !bs->read_only &&
|
||||
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
@@ -782,9 +767,9 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Enable lazy_refcounts according to image and command line options */
|
||||
opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&qcow2_runtime_opts);
|
||||
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 fail;
|
||||
@@ -858,21 +843,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (s->l2_table_cache) {
|
||||
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||
}
|
||||
if (s->refcount_block_cache) {
|
||||
qcow2_cache_destroy(bs, s->refcount_block_cache);
|
||||
}
|
||||
g_free(s->cluster_cache);
|
||||
qemu_vfree(s->cluster_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
bs->bl.write_zeroes_alignment = s->cluster_sectors;
|
||||
}
|
||||
|
||||
static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -1029,20 +1004,11 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
n1 = qcow2_backing_read1(bs->backing_hd, &hd_qiov,
|
||||
sector_num, cur_nr_sectors);
|
||||
if (n1 > 0) {
|
||||
QEMUIOVector local_qiov;
|
||||
|
||||
qemu_iovec_init(&local_qiov, hd_qiov.niov);
|
||||
qemu_iovec_concat(&local_qiov, &hd_qiov, 0,
|
||||
n1 * BDRV_SECTOR_SIZE);
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_readv(bs->backing_hd, sector_num,
|
||||
n1, &local_qiov);
|
||||
n1, &hd_qiov);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
qemu_iovec_destroy(&local_qiov);
|
||||
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1137,6 +1103,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster;
|
||||
int n_end;
|
||||
int ret;
|
||||
int cur_nr_sectors; /* number of sectors in current iteration */
|
||||
uint64_t cluster_offset;
|
||||
@@ -1160,16 +1127,14 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
|
||||
trace_qcow2_writev_start_part(qemu_coroutine_self());
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
cur_nr_sectors = remaining_sectors;
|
||||
n_end = index_in_cluster + remaining_sectors;
|
||||
if (s->crypt_method &&
|
||||
cur_nr_sectors >
|
||||
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) {
|
||||
cur_nr_sectors =
|
||||
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster;
|
||||
n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) {
|
||||
n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9,
|
||||
&cur_nr_sectors, &cluster_offset, &l2meta);
|
||||
index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1274,25 +1239,10 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
/* else pre-write overlap checks in cache_destroy may crash */
|
||||
s->l1_table = NULL;
|
||||
|
||||
if (!(bs->open_flags & BDRV_O_INCOMING)) {
|
||||
int ret1, ret2;
|
||||
qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
|
||||
ret1 = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
ret2 = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
|
||||
if (ret1) {
|
||||
error_report("Failed to flush the L2 table cache: %s",
|
||||
strerror(-ret1));
|
||||
}
|
||||
if (ret2) {
|
||||
error_report("Failed to flush the refcount block cache: %s",
|
||||
strerror(-ret2));
|
||||
}
|
||||
|
||||
if (!ret1 && !ret2) {
|
||||
qcow2_mark_clean(bs);
|
||||
}
|
||||
}
|
||||
qcow2_mark_clean(bs);
|
||||
|
||||
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||
qcow2_cache_destroy(bs, s->refcount_block_cache);
|
||||
@@ -1306,7 +1256,7 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
qcow2_free_snapshots(bs);
|
||||
}
|
||||
|
||||
static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
static void qcow2_invalidate_cache(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int flags = s->flags;
|
||||
@@ -1314,8 +1264,6 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
AES_KEY aes_decrypt_key;
|
||||
uint32_t crypt_method = 0;
|
||||
QDict *options;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Backing files are read-only which makes all of their metadata immutable,
|
||||
@@ -1330,26 +1278,14 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
|
||||
qcow2_close(bs);
|
||||
|
||||
bdrv_invalidate_cache(bs->file, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
options = qdict_new();
|
||||
qdict_put(options, QCOW2_OPT_LAZY_REFCOUNTS,
|
||||
qbool_from_int(s->use_lazy_refcounts));
|
||||
|
||||
memset(s, 0, sizeof(BDRVQcowState));
|
||||
options = qdict_clone_shallow(bs->options);
|
||||
qcow2_open(bs, options, flags, NULL);
|
||||
|
||||
ret = qcow2_open(bs, options, flags, &local_err);
|
||||
QDECREF(options);
|
||||
if (local_err) {
|
||||
error_setg(errp, "Could not reopen qcow2 layer: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qcow2 layer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (crypt_method) {
|
||||
s->crypt_method = crypt_method;
|
||||
@@ -1570,39 +1506,34 @@ static int preallocate(BlockDriverState *bs)
|
||||
int ret;
|
||||
QCowL2Meta *meta;
|
||||
|
||||
nb_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
|
||||
nb_sectors = bdrv_getlength(bs) >> 9;
|
||||
offset = 0;
|
||||
|
||||
while (nb_sectors) {
|
||||
num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, &num,
|
||||
num = MIN(nb_sectors, INT_MAX >> 9);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
|
||||
&host_offset, &meta);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (meta) {
|
||||
QCowL2Meta *next = meta->next;
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
||||
if (ret < 0) {
|
||||
qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
||||
if (ret < 0) {
|
||||
qcow2_free_any_clusters(bs, meta->alloc_offset,
|
||||
meta->nb_clusters, QCOW2_DISCARD_NEVER);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* There are no dependent requests, but we need to remove our
|
||||
* request from the list of in-flight requests */
|
||||
/* There are no dependent requests, but we need to remove our request
|
||||
* from the list of in-flight requests */
|
||||
if (meta != NULL) {
|
||||
QLIST_REMOVE(meta, next_in_flight);
|
||||
|
||||
g_free(meta);
|
||||
meta = next;
|
||||
}
|
||||
|
||||
/* TODO Preallocate data if requested */
|
||||
|
||||
nb_sectors -= num;
|
||||
offset += num << BDRV_SECTOR_BITS;
|
||||
offset += num << 9;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1611,10 +1542,9 @@ static int preallocate(BlockDriverState *bs)
|
||||
* EOF). Extend the image to the last allocated sector.
|
||||
*/
|
||||
if (host_offset != 0) {
|
||||
uint8_t buf[BDRV_SECTOR_SIZE];
|
||||
memset(buf, 0, BDRV_SECTOR_SIZE);
|
||||
ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1,
|
||||
buf, 1);
|
||||
uint8_t buf[512];
|
||||
memset(buf, 0, 512);
|
||||
ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -1626,7 +1556,7 @@ static int preallocate(BlockDriverState *bs)
|
||||
static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
const char *backing_file, const char *backing_format,
|
||||
int flags, size_t cluster_size, int prealloc,
|
||||
QemuOpts *opts, int version,
|
||||
QEMUOptionParameter *options, int version,
|
||||
Error **errp)
|
||||
{
|
||||
/* Calculate cluster_bits */
|
||||
@@ -1658,15 +1588,13 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
NULL, &local_err);
|
||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
@@ -1717,17 +1645,17 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_unref(bs);
|
||||
bs = NULL;
|
||||
bdrv_close(bs);
|
||||
|
||||
/*
|
||||
* And now open the image and make it consistent first (i.e. increase the
|
||||
* refcount of the cluster that is occupied by the header and the refcount
|
||||
* table)
|
||||
*/
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
&bdrv_qcow2, &local_err);
|
||||
BlockDriver* drv = bdrv_find_format("qcow2");
|
||||
assert(drv != NULL);
|
||||
ret = bdrv_open(bs, filename, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
@@ -1773,31 +1701,28 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_unref(bs);
|
||||
bs = NULL;
|
||||
bdrv_close(bs);
|
||||
|
||||
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||
ret = bdrv_open(bs, filename, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||
&bdrv_qcow2, &local_err);
|
||||
if (local_err) {
|
||||
drv, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int qcow2_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
char *backing_file = NULL;
|
||||
char *backing_fmt = NULL;
|
||||
char *buf = NULL;
|
||||
const char *backing_file = NULL;
|
||||
const char *backing_fmt = NULL;
|
||||
uint64_t sectors = 0;
|
||||
int flags = 0;
|
||||
size_t cluster_size = DEFAULT_CLUSTER_SIZE;
|
||||
@@ -1807,71 +1732,89 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int ret;
|
||||
|
||||
/* Read out options */
|
||||
sectors = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512;
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
|
||||
flags |= BLOCK_FLAG_ENCRYPT;
|
||||
}
|
||||
cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
if (!buf || !strcmp(buf, "off")) {
|
||||
prealloc = 0;
|
||||
} else if (!strcmp(buf, "metadata")) {
|
||||
prealloc = 1;
|
||||
} else {
|
||||
error_setg(errp, "Invalid preallocation mode: '%s'", buf);
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
g_free(buf);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_COMPAT_LEVEL);
|
||||
if (!buf) {
|
||||
/* keep the default */
|
||||
} else if (!strcmp(buf, "0.10")) {
|
||||
version = 2;
|
||||
} else if (!strcmp(buf, "1.1")) {
|
||||
version = 3;
|
||||
} else {
|
||||
error_setg(errp, "Invalid compatibility level: '%s'", buf);
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) {
|
||||
flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
sectors = 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_BACKING_FMT)) {
|
||||
backing_fmt = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
|
||||
flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
|
||||
} 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_PREALLOC)) {
|
||||
if (!options->value.s || !strcmp(options->value.s, "off")) {
|
||||
prealloc = 0;
|
||||
} else if (!strcmp(options->value.s, "metadata")) {
|
||||
prealloc = 1;
|
||||
} else {
|
||||
error_setg(errp, "Invalid preallocation mode: '%s'",
|
||||
options->value.s);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
|
||||
if (!options->value.s) {
|
||||
/* keep the default */
|
||||
} else if (!strcmp(options->value.s, "0.10")) {
|
||||
version = 2;
|
||||
} else if (!strcmp(options->value.s, "1.1")) {
|
||||
version = 3;
|
||||
} else {
|
||||
error_setg(errp, "Invalid compatibility level: '%s'",
|
||||
options->value.s);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
|
||||
flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
if (backing_file && prealloc) {
|
||||
error_setg(errp, "Backing file and preallocation cannot be used at "
|
||||
"the same time");
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
|
||||
error_setg(errp, "Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)");
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
|
||||
cluster_size, prealloc, opts, version, &local_err);
|
||||
if (local_err) {
|
||||
cluster_size, prealloc, options, version, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
finish:
|
||||
g_free(backing_file);
|
||||
g_free(backing_fmt);
|
||||
g_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_make_empty(BlockDriverState *bs)
|
||||
{
|
||||
#if 0
|
||||
/* XXX: not correct */
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint32_t l1_length = s->l1_size * sizeof(uint64_t);
|
||||
int ret;
|
||||
|
||||
memset(s->l1_table, 0, l1_length);
|
||||
if (bdrv_pwrite(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0)
|
||||
return -1;
|
||||
ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
l2_cache_reset(bs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -1960,7 +1903,8 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
sector based I/Os */
|
||||
cluster_offset = bdrv_getlength(bs->file);
|
||||
cluster_offset = (cluster_offset + 511) & ~511;
|
||||
return bdrv_truncate(bs->file, cluster_offset);
|
||||
bdrv_truncate(bs->file, cluster_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nb_sectors != s->cluster_sectors) {
|
||||
@@ -2066,8 +2010,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3);
|
||||
bdi->cluster_size = s->cluster_size;
|
||||
bdi->vm_state_offset = qcow2_vm_state_offset(s);
|
||||
return 0;
|
||||
@@ -2230,72 +2172,64 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts)
|
||||
static int qcow2_amend_options(BlockDriverState *bs,
|
||||
QEMUOptionParameter *options)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int old_version = s->qcow_version, new_version = old_version;
|
||||
uint64_t new_size = 0;
|
||||
const char *backing_file = NULL, *backing_format = NULL;
|
||||
bool lazy_refcounts = s->use_lazy_refcounts;
|
||||
const char *compat = NULL;
|
||||
uint64_t cluster_size = s->cluster_size;
|
||||
bool encrypt;
|
||||
int ret;
|
||||
QemuOptDesc *desc = opts->list->desc;
|
||||
int i;
|
||||
|
||||
while (desc && desc->name) {
|
||||
if (!qemu_opt_find(opts, desc->name)) {
|
||||
for (i = 0; options[i].name; i++)
|
||||
{
|
||||
if (!options[i].assigned) {
|
||||
/* only change explicitly defined options */
|
||||
desc++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(desc->name, "compat")) {
|
||||
compat = qemu_opt_get(opts, "compat");
|
||||
if (!compat) {
|
||||
if (!strcmp(options[i].name, "compat")) {
|
||||
if (!options[i].value.s) {
|
||||
/* preserve default */
|
||||
} else if (!strcmp(compat, "0.10")) {
|
||||
} else if (!strcmp(options[i].value.s, "0.10")) {
|
||||
new_version = 2;
|
||||
} else if (!strcmp(compat, "1.1")) {
|
||||
} else if (!strcmp(options[i].value.s, "1.1")) {
|
||||
new_version = 3;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown compatibility level %s.\n", compat);
|
||||
fprintf(stderr, "Unknown compatibility level %s.\n",
|
||||
options[i].value.s);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(desc->name, "preallocation")) {
|
||||
} else if (!strcmp(options[i].name, "preallocation")) {
|
||||
fprintf(stderr, "Cannot change preallocation mode.\n");
|
||||
return -ENOTSUP;
|
||||
} else if (!strcmp(desc->name, "size")) {
|
||||
new_size = qemu_opt_get_size(opts, "size", 0);
|
||||
} else if (!strcmp(desc->name, "backing_file")) {
|
||||
backing_file = qemu_opt_get(opts, "backing_file");
|
||||
} else if (!strcmp(desc->name, "backing_fmt")) {
|
||||
backing_format = qemu_opt_get(opts, "backing_fmt");
|
||||
} else if (!strcmp(desc->name, "encryption")) {
|
||||
encrypt = qemu_opt_get_bool(opts, "encryption", s->crypt_method);
|
||||
if (encrypt != !!s->crypt_method) {
|
||||
} else if (!strcmp(options[i].name, "size")) {
|
||||
new_size = options[i].value.n;
|
||||
} else if (!strcmp(options[i].name, "backing_file")) {
|
||||
backing_file = options[i].value.s;
|
||||
} else if (!strcmp(options[i].name, "backing_fmt")) {
|
||||
backing_format = options[i].value.s;
|
||||
} else if (!strcmp(options[i].name, "encryption")) {
|
||||
if ((options[i].value.n != !!s->crypt_method)) {
|
||||
fprintf(stderr, "Changing the encryption flag is not "
|
||||
"supported.\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, "cluster_size")) {
|
||||
cluster_size = qemu_opt_get_size(opts, "cluster_size",
|
||||
cluster_size);
|
||||
if (cluster_size != s->cluster_size) {
|
||||
} else if (!strcmp(options[i].name, "cluster_size")) {
|
||||
if (options[i].value.n != s->cluster_size) {
|
||||
fprintf(stderr, "Changing the cluster size is not "
|
||||
"supported.\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, "lazy_refcounts")) {
|
||||
lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts",
|
||||
lazy_refcounts);
|
||||
} else if (!strcmp(options[i].name, "lazy_refcounts")) {
|
||||
lazy_refcounts = options[i].value.n;
|
||||
} else {
|
||||
/* if this assertion fails, this probably means a new option was
|
||||
* added without having it covered here */
|
||||
assert(false);
|
||||
}
|
||||
|
||||
desc++;
|
||||
}
|
||||
|
||||
if (new_version != old_version) {
|
||||
@@ -2364,58 +2298,52 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList qcow2_create_opts = {
|
||||
.name = "qcow2-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_COMPAT_LEVEL,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Compatibility level (0.10 or 1.1)"
|
||||
},
|
||||
{
|
||||
.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_ENCRYPT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Encrypt the image",
|
||||
.def_value_str = "off"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "qcow2 cluster size",
|
||||
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE)
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_PREALLOC,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Preallocation mode (allowed values: off, metadata)"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_LAZY_REFCOUNTS,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Postpone refcount updates",
|
||||
.def_value_str = "off"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
static QEMUOptionParameter qcow2_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_COMPAT_LEVEL,
|
||||
.type = OPT_STRING,
|
||||
.help = "Compatibility level (0.10 or 1.1)"
|
||||
},
|
||||
{
|
||||
.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_ENCRYPT,
|
||||
.type = OPT_FLAG,
|
||||
.help = "Encrypt the image"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "qcow2 cluster size",
|
||||
.value = { .n = DEFAULT_CLUSTER_SIZE },
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_PREALLOC,
|
||||
.type = OPT_STRING,
|
||||
.help = "Preallocation mode (allowed values: off, metadata)"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_LAZY_REFCOUNTS,
|
||||
.type = OPT_FLAG,
|
||||
.help = "Postpone refcount updates",
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
BlockDriver bdrv_qcow2 = {
|
||||
static BlockDriver bdrv_qcow2 = {
|
||||
.format_name = "qcow2",
|
||||
.instance_size = sizeof(BDRVQcowState),
|
||||
.bdrv_probe = qcow2_probe,
|
||||
@@ -2426,6 +2354,7 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
||||
.bdrv_set_key = qcow2_set_key,
|
||||
.bdrv_make_empty = qcow2_make_empty,
|
||||
|
||||
.bdrv_co_readv = qcow2_co_readv,
|
||||
.bdrv_co_writev = qcow2_co_writev,
|
||||
@@ -2440,22 +2369,20 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_snapshot_goto = qcow2_snapshot_goto,
|
||||
.bdrv_snapshot_delete = qcow2_snapshot_delete,
|
||||
.bdrv_snapshot_list = qcow2_snapshot_list,
|
||||
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
|
||||
.bdrv_get_info = qcow2_get_info,
|
||||
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
|
||||
.bdrv_get_info = qcow2_get_info,
|
||||
.bdrv_get_specific_info = qcow2_get_specific_info,
|
||||
|
||||
.bdrv_save_vmstate = qcow2_save_vmstate,
|
||||
.bdrv_load_vmstate = qcow2_load_vmstate,
|
||||
|
||||
.supports_backing = true,
|
||||
.bdrv_change_backing_file = qcow2_change_backing_file,
|
||||
|
||||
.bdrv_refresh_limits = qcow2_refresh_limits,
|
||||
.bdrv_invalidate_cache = qcow2_invalidate_cache,
|
||||
|
||||
.create_opts = &qcow2_create_opts,
|
||||
.bdrv_check = qcow2_check,
|
||||
.bdrv_amend_options = qcow2_amend_options,
|
||||
.create_options = qcow2_create_options,
|
||||
.bdrv_check = qcow2_check,
|
||||
.bdrv_amend_options = qcow2_amend_options,
|
||||
};
|
||||
|
||||
static void bdrv_qcow2_init(void)
|
||||
|
||||
+5
-12
@@ -48,10 +48,6 @@
|
||||
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||
#define QCOW_MAX_L1_SIZE 0x2000000
|
||||
|
||||
/* Allow for an average of 1k per snapshot table entry, should be plenty of
|
||||
* space for snapshot names and IDs */
|
||||
#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS)
|
||||
|
||||
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||
#define QCOW_OFLAG_COPIED (1ULL << 63)
|
||||
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||
@@ -379,11 +375,11 @@ typedef enum QCow2MetadataOverlap {
|
||||
#define QCOW2_OL_ALL \
|
||||
(QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
|
||||
|
||||
#define L1E_OFFSET_MASK 0x00fffffffffffe00ULL
|
||||
#define L2E_OFFSET_MASK 0x00fffffffffffe00ULL
|
||||
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
|
||||
|
||||
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
|
||||
#define REFT_OFFSET_MASK 0xffffffffffffff00ULL
|
||||
|
||||
static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset)
|
||||
{
|
||||
@@ -512,7 +508,7 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *cluster_offset);
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *host_offset, QCowL2Meta **m);
|
||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m);
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
int compressed_size);
|
||||
@@ -532,10 +528,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
|
||||
|
||||
void qcow2_free_snapshots(BlockDriverState *bs);
|
||||
int qcow2_read_snapshots(BlockDriverState *bs);
|
||||
|
||||
+4
-4
@@ -173,7 +173,7 @@ 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
+93
-172
@@ -21,13 +21,12 @@
|
||||
static void qed_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QEDAIOCB *acb = (QEDAIOCB *)blockacb;
|
||||
AioContext *aio_context = bdrv_get_aio_context(blockacb->bs);
|
||||
bool finished = false;
|
||||
|
||||
/* Wait for the request to finish */
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
aio_poll(aio_context, true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,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)
|
||||
{
|
||||
@@ -413,15 +391,14 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
qed_header_le_to_cpu(&le_header, &s->header);
|
||||
|
||||
if (s->header.magic != QED_MAGIC) {
|
||||
error_setg(errp, "Image not in QED format");
|
||||
return -EINVAL;
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
if (s->header.features & ~QED_FEATURE_MASK) {
|
||||
/* image uses unsupported feature bits */
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%" PRIx64,
|
||||
s->header.features & ~QED_FEATURE_MASK);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "QED", buf);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -518,7 +495,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) {
|
||||
@@ -528,13 +506,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
|
||||
}
|
||||
|
||||
/* We have nothing to do for QED reopen, stubs just return
|
||||
* success */
|
||||
static int bdrv_qed_reopen_prepare(BDRVReopenState *state,
|
||||
@@ -547,7 +518,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);
|
||||
@@ -564,8 +536,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)
|
||||
const char *backing_file, const char *backing_fmt)
|
||||
{
|
||||
QEDHeader header = {
|
||||
.magic = QED_MAGIC,
|
||||
@@ -582,20 +553,20 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
size_t l1_size = header.cluster_size * header.table_size;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
ret = bdrv_create_file(filename, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL,
|
||||
&local_err);
|
||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -639,53 +610,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 = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -751,6 +722,11 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
|
||||
return cb.status;
|
||||
}
|
||||
|
||||
static int bdrv_qed_make_empty(BlockDriverState *bs)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
|
||||
{
|
||||
return acb->common.bs->opaque;
|
||||
@@ -759,19 +735,17 @@ 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,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
uint64_t backing_length = 0;
|
||||
@@ -804,21 +778,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;
|
||||
@@ -835,12 +803,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;
|
||||
@@ -878,12 +840,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);
|
||||
}
|
||||
|
||||
@@ -950,8 +911,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
|
||||
@@ -1326,7 +1286,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;
|
||||
}
|
||||
|
||||
@@ -1352,12 +1312,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);
|
||||
@@ -1397,7 +1351,6 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
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);
|
||||
|
||||
@@ -1444,8 +1397,7 @@ static void coroutine_fn qed_co_write_zeroes_cb(void *opaque, int ret)
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
BdrvRequestFlags flags)
|
||||
int nb_sectors)
|
||||
{
|
||||
BlockDriverAIOCB *blockacb;
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
@@ -1522,8 +1474,6 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
memset(bdi, 0, sizeof(*bdi));
|
||||
bdi->cluster_size = s->header.cluster_size;
|
||||
bdi->is_dirty = s->header.features & QED_F_NEED_CHECK;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
bdi->can_write_zeroes_with_unmap = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1599,31 +1549,13 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
bdrv_qed_close(bs);
|
||||
|
||||
bdrv_invalidate_cache(bs->file, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(s, 0, sizeof(BDRVQEDState));
|
||||
ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_setg(errp, "Could not reopen qed layer: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
||||
return;
|
||||
}
|
||||
bdrv_qed_open(bs, NULL, bs->open_flags, NULL);
|
||||
}
|
||||
|
||||
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
@@ -1634,45 +1566,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,
|
||||
@@ -1682,18 +1605,16 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_create = bdrv_qed_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
|
||||
.bdrv_make_empty = bdrv_qed_make_empty,
|
||||
.bdrv_aio_readv = bdrv_qed_aio_readv,
|
||||
.bdrv_aio_writev = bdrv_qed_aio_writev,
|
||||
.bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes,
|
||||
.bdrv_truncate = bdrv_qed_truncate,
|
||||
.bdrv_getlength = bdrv_qed_getlength,
|
||||
.bdrv_get_info = bdrv_qed_get_info,
|
||||
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
|
||||
.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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
-977
@@ -1,977 +0,0 @@
|
||||
/*
|
||||
* Quorum Block filter
|
||||
*
|
||||
* Copyright (C) 2012-2014 Nodalink, EURL.
|
||||
*
|
||||
* Author:
|
||||
* Benoît Canet <benoit.canet@irqsave.net>
|
||||
*
|
||||
* Based on the design and code of blkverify.c (Copyright (C) 2010 IBM, Corp)
|
||||
* and blkmirror.c (Copyright (C) 2011 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 <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qjson.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"
|
||||
|
||||
/* This union holds a vote hash value */
|
||||
typedef union QuorumVoteValue {
|
||||
char h[HASH_LENGTH]; /* SHA-256 hash */
|
||||
int64_t l; /* simpler 64 bits hash */
|
||||
} QuorumVoteValue;
|
||||
|
||||
/* A vote item */
|
||||
typedef struct QuorumVoteItem {
|
||||
int index;
|
||||
QLIST_ENTRY(QuorumVoteItem) next;
|
||||
} QuorumVoteItem;
|
||||
|
||||
/* this structure is a vote version. A version is the set of votes sharing the
|
||||
* same vote value.
|
||||
* The set of votes will be tracked with the items field and its cardinality is
|
||||
* vote_count.
|
||||
*/
|
||||
typedef struct QuorumVoteVersion {
|
||||
QuorumVoteValue value;
|
||||
int index;
|
||||
int vote_count;
|
||||
QLIST_HEAD(, QuorumVoteItem) items;
|
||||
QLIST_ENTRY(QuorumVoteVersion) next;
|
||||
} QuorumVoteVersion;
|
||||
|
||||
/* this structure holds a group of vote versions together */
|
||||
typedef struct QuorumVotes {
|
||||
QLIST_HEAD(, QuorumVoteVersion) vote_list;
|
||||
bool (*compare)(QuorumVoteValue *a, QuorumVoteValue *b);
|
||||
} QuorumVotes;
|
||||
|
||||
/* the following structure holds the state of one quorum instance */
|
||||
typedef struct BDRVQuorumState {
|
||||
BlockDriverState **bs; /* children BlockDriverStates */
|
||||
int num_children; /* children count */
|
||||
int threshold; /* if less than threshold children reads gave the
|
||||
* same result a quorum error occurs.
|
||||
*/
|
||||
bool is_blkverify; /* true if the driver is in blkverify mode
|
||||
* Writes are mirrored on two children devices.
|
||||
* On reads the two children devices' contents are
|
||||
* compared and if a difference is spotted its
|
||||
* location is printed and the code aborts.
|
||||
* 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.
|
||||
*/
|
||||
} BDRVQuorumState;
|
||||
|
||||
typedef struct QuorumAIOCB QuorumAIOCB;
|
||||
|
||||
/* Quorum will create one instance of the following structure per operation it
|
||||
* performs on its children.
|
||||
* So for each read/write operation coming from the upper layer there will be
|
||||
* $children_count QuorumChildRequest.
|
||||
*/
|
||||
typedef struct QuorumChildRequest {
|
||||
BlockDriverAIOCB *aiocb;
|
||||
QEMUIOVector qiov;
|
||||
uint8_t *buf;
|
||||
int ret;
|
||||
QuorumAIOCB *parent;
|
||||
} QuorumChildRequest;
|
||||
|
||||
/* Quorum will use the following structure to track progress of each read/write
|
||||
* operation received by the upper layer.
|
||||
* This structure hold pointers to the QuorumChildRequest structures instances
|
||||
* used to do operations on each children and track overall progress.
|
||||
*/
|
||||
struct QuorumAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
|
||||
/* Request metadata */
|
||||
uint64_t sector_num;
|
||||
int nb_sectors;
|
||||
|
||||
QEMUIOVector *qiov; /* calling IOV */
|
||||
|
||||
QuorumChildRequest *qcrs; /* individual child requests */
|
||||
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;
|
||||
};
|
||||
|
||||
static bool quorum_vote(QuorumAIOCB *acb);
|
||||
|
||||
static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int i;
|
||||
|
||||
/* cancel all callbacks */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
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 = quorum_aio_cancel,
|
||||
};
|
||||
|
||||
static void quorum_aio_finalize(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int i, ret = 0;
|
||||
|
||||
if (acb->vote_ret) {
|
||||
ret = acb->vote_ret;
|
||||
}
|
||||
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
|
||||
if (acb->is_read) {
|
||||
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_release(acb);
|
||||
}
|
||||
|
||||
static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
{
|
||||
return !memcmp(a->h, b->h, HASH_LENGTH);
|
||||
}
|
||||
|
||||
static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
{
|
||||
return a->l == b->l;
|
||||
}
|
||||
|
||||
static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
BlockDriverState *bs,
|
||||
QEMUIOVector *qiov,
|
||||
uint64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
|
||||
int i;
|
||||
|
||||
acb->common.bs->opaque = s;
|
||||
acb->sector_num = sector_num;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->qiov = qiov;
|
||||
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;
|
||||
acb->vote_ret = 0;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].buf = NULL;
|
||||
acb->qcrs[i].ret = 0;
|
||||
acb->qcrs[i].parent = acb;
|
||||
}
|
||||
|
||||
return acb;
|
||||
}
|
||||
|
||||
static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
|
||||
{
|
||||
const char *msg = NULL;
|
||||
if (ret < 0) {
|
||||
msg = strerror(-ret);
|
||||
}
|
||||
qapi_event_send_quorum_report_bad(!!msg, msg, node_name,
|
||||
acb->sector_num, acb->nb_sectors, &error_abort);
|
||||
}
|
||||
|
||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static int quorum_vote_error(QuorumAIOCB *acb);
|
||||
|
||||
static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
|
||||
if (acb->success_count < s->threshold) {
|
||||
acb->vote_ret = quorum_vote_error(acb);
|
||||
quorum_report_failure(acb);
|
||||
return true;
|
||||
}
|
||||
|
||||
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 void quorum_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
QuorumChildRequest *sacb = opaque;
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
bool rewrite = false;
|
||||
|
||||
sacb->ret = ret;
|
||||
acb->count++;
|
||||
if (ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
|
||||
}
|
||||
assert(acb->count <= s->num_children);
|
||||
assert(acb->success_count <= s->num_children);
|
||||
if (acb->count < s->num_children) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do the vote on read */
|
||||
if (acb->is_read) {
|
||||
rewrite = 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);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
QuorumAIOCB *acb,
|
||||
QuorumVoteValue *value)
|
||||
{
|
||||
QuorumVoteVersion *version;
|
||||
QuorumVoteItem *item;
|
||||
|
||||
QLIST_FOREACH(version, &acb->votes.vote_list, next) {
|
||||
if (acb->votes.compare(&version->value, value)) {
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
quorum_report_bad(acb, s->bs[item->index]->node_name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb,
|
||||
QuorumVoteValue *value)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_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_count_vote(QuorumVotes *votes,
|
||||
QuorumVoteValue *value,
|
||||
int index)
|
||||
{
|
||||
QuorumVoteVersion *v = NULL, *version = NULL;
|
||||
QuorumVoteItem *item;
|
||||
|
||||
/* look if we have something with this hash */
|
||||
QLIST_FOREACH(v, &votes->vote_list, next) {
|
||||
if (votes->compare(&v->value, value)) {
|
||||
version = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* It's a version not yet in the list add it */
|
||||
if (!version) {
|
||||
version = g_new0(QuorumVoteVersion, 1);
|
||||
QLIST_INIT(&version->items);
|
||||
memcpy(&version->value, value, sizeof(version->value));
|
||||
version->index = index;
|
||||
version->vote_count = 0;
|
||||
QLIST_INSERT_HEAD(&votes->vote_list, version, next);
|
||||
}
|
||||
|
||||
version->vote_count++;
|
||||
|
||||
item = g_new0(QuorumVoteItem, 1);
|
||||
item->index = index;
|
||||
QLIST_INSERT_HEAD(&version->items, item, next);
|
||||
}
|
||||
|
||||
static void quorum_free_vote_list(QuorumVotes *votes)
|
||||
{
|
||||
QuorumVoteVersion *version, *next_version;
|
||||
QuorumVoteItem *item, *next_item;
|
||||
|
||||
QLIST_FOREACH_SAFE(version, &votes->vote_list, next, next_version) {
|
||||
QLIST_REMOVE(version, next);
|
||||
QLIST_FOREACH_SAFE(item, &version->items, next, next_item) {
|
||||
QLIST_REMOVE(item, next);
|
||||
g_free(item);
|
||||
}
|
||||
g_free(version);
|
||||
}
|
||||
}
|
||||
|
||||
static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash)
|
||||
{
|
||||
int j, ret;
|
||||
gnutls_hash_hd_t dig;
|
||||
QEMUIOVector *qiov = &acb->qcrs[i].qiov;
|
||||
|
||||
ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (j = 0; j < qiov->niov; j++) {
|
||||
ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_hash_deinit(dig, (void *) hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
|
||||
{
|
||||
int max = 0;
|
||||
QuorumVoteVersion *candidate, *winner = NULL;
|
||||
|
||||
QLIST_FOREACH(candidate, &votes->vote_list, next) {
|
||||
if (candidate->vote_count > max) {
|
||||
max = candidate->vote_count;
|
||||
winner = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return winner;
|
||||
}
|
||||
|
||||
/* qemu_iovec_compare is handy for blkverify mode because it returns the first
|
||||
* differing byte location. Yet it is handcoded to compare vectors one byte
|
||||
* after another so it does not benefit from the libc SIMD optimizations.
|
||||
* quorum_iovec_compare is written for speed and should be used in the non
|
||||
* blkverify mode of quorum.
|
||||
*/
|
||||
static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
|
||||
assert(a->niov == b->niov);
|
||||
for (i = 0; i < a->niov; i++) {
|
||||
assert(a->iov[i].iov_len == b->iov[i].iov_len);
|
||||
result = memcmp(a->iov[i].iov_base,
|
||||
b->iov[i].iov_base,
|
||||
a->iov[i].iov_len);
|
||||
if (result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ",
|
||||
acb->sector_num, acb->nb_sectors);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool quorum_compare(QuorumAIOCB *acb,
|
||||
QEMUIOVector *a,
|
||||
QEMUIOVector *b)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
ssize_t offset;
|
||||
|
||||
/* This driver will replace blkverify in this particular case */
|
||||
if (s->is_blkverify) {
|
||||
offset = qemu_iovec_compare(a, b);
|
||||
if (offset != -1) {
|
||||
quorum_err(acb, "contents mismatch in sector %" PRId64,
|
||||
acb->sector_num +
|
||||
(uint64_t)(offset / BDRV_SECTOR_SIZE));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return quorum_iovec_compare(a, b);
|
||||
}
|
||||
|
||||
/* Do a vote to get the error code */
|
||||
static int quorum_vote_error(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
QuorumVoteVersion *winner = NULL;
|
||||
QuorumVotes error_votes;
|
||||
QuorumVoteValue result_value;
|
||||
int i, ret = 0;
|
||||
bool error = false;
|
||||
|
||||
QLIST_INIT(&error_votes.vote_list);
|
||||
error_votes.compare = quorum_64bits_compare;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
ret = acb->qcrs[i].ret;
|
||||
if (ret) {
|
||||
error = true;
|
||||
result_value.l = ret;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
winner = quorum_get_vote_winner(&error_votes);
|
||||
ret = winner->value.l;
|
||||
}
|
||||
|
||||
quorum_free_vote_list(&error_votes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool 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;
|
||||
}
|
||||
|
||||
/* get the index of the first successful read */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
if (!acb->qcrs[i].ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < s->num_children);
|
||||
|
||||
/* compare this read with all other successful reads stopping at quorum
|
||||
* failure
|
||||
*/
|
||||
for (j = i + 1; j < s->num_children; j++) {
|
||||
if (acb->qcrs[j].ret) {
|
||||
continue;
|
||||
}
|
||||
quorum = quorum_compare(acb, &acb->qcrs[i].qiov, &acb->qcrs[j].qiov);
|
||||
if (!quorum) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Every successful read agrees */
|
||||
if (quorum) {
|
||||
quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* compute hashes for each successful read, also store indexes */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
if (acb->qcrs[i].ret) {
|
||||
continue;
|
||||
}
|
||||
ret = quorum_compute_hash(acb, i, &hash);
|
||||
/* if ever the hash computation failed */
|
||||
if (ret < 0) {
|
||||
acb->vote_ret = ret;
|
||||
goto free_exit;
|
||||
}
|
||||
quorum_count_vote(&acb->votes, &hash, i);
|
||||
}
|
||||
|
||||
/* vote to select the most represented version */
|
||||
winner = quorum_get_vote_winner(&acb->votes);
|
||||
|
||||
/* if the winner count is smaller than threshold the read fails */
|
||||
if (winner->vote_count < s->threshold) {
|
||||
quorum_report_failure(acb);
|
||||
acb->vote_ret = -EIO;
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
/* we have a winner: copy it */
|
||||
quorum_copy_qiov(acb->qiov, &acb->qcrs[winner->index].qiov);
|
||||
|
||||
/* 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 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 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,
|
||||
cb, opaque);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov,
|
||||
nb_sectors, &quorum_aio_cb,
|
||||
&acb->qcrs[i]);
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int64_t quorum_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int64_t result;
|
||||
int i;
|
||||
|
||||
/* check that all file have the same length */
|
||||
result = bdrv_getlength(s->bs[0]);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
for (i = 1; i < s->num_children; i++) {
|
||||
int64_t value = bdrv_getlength(s->bs[i]);
|
||||
if (value < 0) {
|
||||
return value;
|
||||
}
|
||||
if (value != result) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void quorum_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_invalidate_cache(s->bs[i], &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumVoteVersion *winner = NULL;
|
||||
QuorumVotes error_votes;
|
||||
QuorumVoteValue result_value;
|
||||
int i;
|
||||
int result = 0;
|
||||
|
||||
QLIST_INIT(&error_votes.vote_list);
|
||||
error_votes.compare = quorum_64bits_compare;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
result = bdrv_co_flush(s->bs[i]);
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
}
|
||||
|
||||
winner = quorum_get_vote_winner(&error_votes);
|
||||
result = winner->value.l;
|
||||
|
||||
quorum_free_vote_list(&error_votes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bool perm = bdrv_recurse_is_first_non_filter(s->bs[i],
|
||||
candidate);
|
||||
if (perm) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
|
||||
{
|
||||
|
||||
if (threshold < 1) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"vote-threshold", "value >= 1");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (threshold > num_children) {
|
||||
error_setg(errp, "threshold may not exceed children count");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList quorum_runtime_opts = {
|
||||
.name = "quorum",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = QUORUM_OPT_VOTE_THRESHOLD,
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "The number of vote needed for reaching quorum",
|
||||
},
|
||||
{
|
||||
.name = QUORUM_OPT_BLKVERIFY,
|
||||
.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",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts;
|
||||
bool *opened;
|
||||
QDict *sub = NULL;
|
||||
QList *list = NULL;
|
||||
const QListEntry *lentry;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
qdict_flatten(options);
|
||||
qdict_extract_subqdict(options, &sub, "children.");
|
||||
qdict_array_split(sub, &list);
|
||||
|
||||
if (qdict_size(sub)) {
|
||||
error_setg(&local_err, "Invalid option children.%s",
|
||||
qdict_first(sub)->key);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* count how many different children are present */
|
||||
s->num_children = qlist_size(list);
|
||||
if (s->num_children < 2) {
|
||||
error_setg(&local_err,
|
||||
"Number of provided children must be greater than 1");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* allocate the children BlockDriverState array */
|
||||
s->bs = g_new0(BlockDriverState *, s->num_children);
|
||||
opened = g_new0(bool, s->num_children);
|
||||
|
||||
for (i = 0, lentry = qlist_first(list); lentry;
|
||||
lentry = qlist_next(lentry), i++) {
|
||||
QDict *d;
|
||||
QString *string;
|
||||
|
||||
switch (qobject_type(lentry->value))
|
||||
{
|
||||
/* List of options */
|
||||
case QTYPE_QDICT:
|
||||
d = qobject_to_qdict(lentry->value);
|
||||
QINCREF(d);
|
||||
ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
|
||||
&local_err);
|
||||
break;
|
||||
|
||||
/* QMP reference */
|
||||
case QTYPE_QSTRING:
|
||||
string = qobject_to_qstring(lentry->value);
|
||||
ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
|
||||
flags, NULL, &local_err);
|
||||
break;
|
||||
|
||||
default:
|
||||
error_setg(&local_err, "Specification of child block device %i "
|
||||
"is invalid", i);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto close_exit;
|
||||
}
|
||||
opened[i] = true;
|
||||
}
|
||||
|
||||
g_free(opened);
|
||||
goto exit;
|
||||
|
||||
close_exit:
|
||||
/* cleanup on error */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
if (!opened[i]) {
|
||||
continue;
|
||||
}
|
||||
bdrv_unref(s->bs[i]);
|
||||
}
|
||||
g_free(s->bs);
|
||||
g_free(opened);
|
||||
exit:
|
||||
/* propagate error */
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
QDECREF(list);
|
||||
QDECREF(sub);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void quorum_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_unref(s->bs[i]);
|
||||
}
|
||||
|
||||
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 BlockDriver bdrv_quorum = {
|
||||
.format_name = "quorum",
|
||||
.protocol_name = "quorum",
|
||||
|
||||
.instance_size = sizeof(BDRVQuorumState),
|
||||
|
||||
.bdrv_file_open = quorum_open,
|
||||
.bdrv_close = quorum_close,
|
||||
|
||||
.bdrv_co_flush_to_disk = quorum_co_flush,
|
||||
|
||||
.bdrv_getlength = quorum_getlength,
|
||||
|
||||
.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,
|
||||
};
|
||||
|
||||
static void bdrv_quorum_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_quorum);
|
||||
}
|
||||
|
||||
block_init(bdrv_quorum_init);
|
||||
+1
-12
@@ -21,10 +21,9 @@
|
||||
#define QEMU_AIO_IOCTL 0x0004
|
||||
#define QEMU_AIO_FLUSH 0x0008
|
||||
#define QEMU_AIO_DISCARD 0x0010
|
||||
#define QEMU_AIO_WRITE_ZEROES 0x0020
|
||||
#define QEMU_AIO_TYPE_MASK \
|
||||
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \
|
||||
QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES)
|
||||
QEMU_AIO_DISCARD)
|
||||
|
||||
/* AIO flags */
|
||||
#define QEMU_AIO_MISALIGNED 0x1000
|
||||
@@ -34,29 +33,19 @@
|
||||
/* linux-aio.c - Linux native implementation */
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
void *laio_init(void);
|
||||
void laio_cleanup(void *s);
|
||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *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);
|
||||
#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);
|
||||
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
QEMUWin32AIOState *aio, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *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);
|
||||
#endif
|
||||
|
||||
#endif /* QEMU_RAW_AIO_H */
|
||||
|
||||
+171
-618
File diff suppressed because it is too large
Load Diff
+35
-132
@@ -36,6 +36,8 @@
|
||||
#define FTYPE_CD 1
|
||||
#define FTYPE_HARDDISK 2
|
||||
|
||||
static QEMUWin32AIOState *aio;
|
||||
|
||||
typedef struct RawWin32AIOData {
|
||||
BlockDriverState *bs;
|
||||
HANDLE hfile;
|
||||
@@ -200,54 +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;
|
||||
DWORD sectorsPerCluster, freeClusters, totalClusters, count;
|
||||
DISK_GEOMETRY_EX dg;
|
||||
BOOL status;
|
||||
|
||||
if (s->type == FTYPE_CD) {
|
||||
bs->request_alignment = 2048;
|
||||
return;
|
||||
}
|
||||
if (s->type == FTYPE_HARDDISK) {
|
||||
status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
||||
NULL, 0, &dg, sizeof(dg), &count, NULL);
|
||||
if (status != 0) {
|
||||
bs->request_alignment = dg.Geometry.BytesPerSector;
|
||||
return;
|
||||
}
|
||||
/* try GetDiskFreeSpace too */
|
||||
}
|
||||
|
||||
if (s->drive_path[0]) {
|
||||
GetDiskFreeSpace(s->drive_path, §orsPerCluster,
|
||||
&dg.Geometry.BytesPerSector,
|
||||
&freeClusters, &totalClusters);
|
||||
bs->request_alignment = dg.Geometry.BytesPerSector;
|
||||
}
|
||||
}
|
||||
|
||||
static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
|
||||
{
|
||||
assert(access_flags != NULL);
|
||||
@@ -268,17 +222,6 @@ static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
|
||||
}
|
||||
}
|
||||
|
||||
static void raw_parse_filename(const char *filename, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
/* The filename does not have to be prefixed by the protocol name, since
|
||||
* "file" is the default protocol; therefore, the return value of this
|
||||
* function call can be ignored. */
|
||||
strstart(filename, "file:", &filename);
|
||||
|
||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
||||
}
|
||||
|
||||
static QemuOptsList raw_runtime_opts = {
|
||||
.name = "raw",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
|
||||
@@ -305,9 +248,9 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
s->type = FTYPE_FILE;
|
||||
|
||||
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||
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 fail;
|
||||
@@ -317,15 +260,13 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
raw_parse_flags(flags, &access_flags, &overlapped);
|
||||
|
||||
if (filename[0] && filename[1] == ':') {
|
||||
snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", filename[0]);
|
||||
} else if (filename[0] == '\\' && filename[1] == '\\') {
|
||||
s->drive_path[0] = 0;
|
||||
} else {
|
||||
/* Relative path. */
|
||||
char buf[MAX_PATH];
|
||||
GetCurrentDirectory(MAX_PATH, buf);
|
||||
snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", buf[0]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
s->hfile = CreateFile(filename, access_flags,
|
||||
@@ -343,26 +284,15 @@ 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);
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
@@ -407,17 +337,7 @@ static BlockDriverAIOCB *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,16 +423,19 @@ 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;
|
||||
|
||||
strstart(filename, "file:", &filename);
|
||||
|
||||
/* Read out options */
|
||||
total_size =
|
||||
qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512;
|
||||
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);
|
||||
@@ -526,29 +449,23 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
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 +477,7 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
|
||||
.create_opts = &raw_create_opts,
|
||||
.create_options = raw_create_options,
|
||||
};
|
||||
|
||||
/***********************************************/
|
||||
@@ -621,15 +538,6 @@ static int hdev_probe_device(const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdev_parse_filename(const char *filename, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
/* The prefix is optional, just as for "file". */
|
||||
strstart(filename, "host_device:", &filename);
|
||||
|
||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
||||
}
|
||||
|
||||
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -642,10 +550,9 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
|
||||
QemuOpts *opts = qemu_opts_create(&raw_runtime_opts, NULL, 0,
|
||||
&error_abort);
|
||||
QemuOpts *opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||
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 done;
|
||||
@@ -700,7 +607,6 @@ static BlockDriver bdrv_host_device = {
|
||||
.protocol_name = "host_device",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_parse_filename = hdev_parse_filename,
|
||||
.bdrv_probe_device = hdev_probe_device,
|
||||
.bdrv_file_open = hdev_open,
|
||||
.bdrv_close = raw_close,
|
||||
@@ -709,9 +615,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,
|
||||
|
||||
|
||||
+15
-25
@@ -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,
|
||||
@@ -72,10 +68,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BdrvRequestFlags flags)
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors, flags);
|
||||
return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_discard(BlockDriverState *bs,
|
||||
@@ -94,11 +89,6 @@ 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)
|
||||
{
|
||||
bs->bl = bs->file->bl;
|
||||
}
|
||||
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
return bdrv_truncate(bs->file, offset);
|
||||
@@ -142,13 +132,14 @@ 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);
|
||||
if (local_err) {
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
@@ -173,7 +164,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,
|
||||
@@ -189,14 +180,13 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_getlength = &raw_getlength,
|
||||
.has_variable_length = true,
|
||||
.bdrv_get_info = &raw_get_info,
|
||||
.bdrv_refresh_limits = &raw_refresh_limits,
|
||||
.bdrv_is_inserted = &raw_is_inserted,
|
||||
.bdrv_media_changed = &raw_media_changed,
|
||||
.bdrv_eject = &raw_eject,
|
||||
.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
|
||||
};
|
||||
|
||||
|
||||
+174
-96
@@ -95,17 +95,22 @@ typedef struct RADOSCB {
|
||||
#define RBD_FD_WRITE 1
|
||||
|
||||
typedef struct BDRVRBDState {
|
||||
int fds[2];
|
||||
rados_t cluster;
|
||||
rados_ioctx_t io_ctx;
|
||||
rbd_image_t image;
|
||||
char name[RBD_MAX_IMAGE_NAME_SIZE];
|
||||
char *snap;
|
||||
int event_reader_pos;
|
||||
RADOSCB *event_rcb;
|
||||
} BDRVRBDState;
|
||||
|
||||
static void rbd_aio_bh_cb(void *opaque);
|
||||
|
||||
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;
|
||||
@@ -128,10 +133,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;
|
||||
}
|
||||
|
||||
@@ -157,15 +162,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;
|
||||
}
|
||||
|
||||
@@ -174,8 +177,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;
|
||||
@@ -183,25 +185,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);
|
||||
@@ -236,7 +234,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];
|
||||
@@ -248,20 +246,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;
|
||||
}
|
||||
@@ -270,7 +268,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) {
|
||||
@@ -278,7 +276,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;
|
||||
}
|
||||
@@ -289,9 +287,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;
|
||||
@@ -308,29 +306,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 = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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 +343,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;
|
||||
}
|
||||
@@ -366,8 +369,9 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
/*
|
||||
* This aio completion is being called from rbd_finish_bh() and runs in qemu
|
||||
* BH context.
|
||||
* This aio completion is being called from qemu_rbd_aio_event_reader()
|
||||
* and runs in qemu context. It schedules a bh, but just in case the aio
|
||||
* was not cancelled before.
|
||||
*/
|
||||
static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
{
|
||||
@@ -397,19 +401,36 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
acb->ret = r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note that acb->bh can be NULL in case where the aio was cancelled */
|
||||
acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
g_free(rcb);
|
||||
}
|
||||
|
||||
if (acb->cmd == RBD_AIO_READ) {
|
||||
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
|
||||
}
|
||||
qemu_vfree(acb->bounce);
|
||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
acb->status = 0;
|
||||
/*
|
||||
* aio fd read handler. It runs in the qemu context and calls the
|
||||
* completion handling of completed rados aio operations.
|
||||
*/
|
||||
static void qemu_rbd_aio_event_reader(void *opaque)
|
||||
{
|
||||
BDRVRBDState *s = opaque;
|
||||
|
||||
if (!acb->cancelled) {
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
ssize_t ret;
|
||||
|
||||
do {
|
||||
char *p = (char *)&s->event_rcb;
|
||||
|
||||
/* now read the rcb pointer that was sent from a non qemu thread */
|
||||
ret = read(s->fds[RBD_FD_READ], p + s->event_reader_pos,
|
||||
sizeof(s->event_rcb) - s->event_reader_pos);
|
||||
if (ret > 0) {
|
||||
s->event_reader_pos += ret;
|
||||
if (s->event_reader_pos == sizeof(s->event_rcb)) {
|
||||
s->event_reader_pos = 0;
|
||||
qemu_rbd_complete_aio(s->event_rcb);
|
||||
}
|
||||
}
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
@@ -440,10 +461,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
const char *filename;
|
||||
int r;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
qemu_opts_del(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -453,7 +475,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;
|
||||
}
|
||||
@@ -461,7 +483,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;
|
||||
}
|
||||
|
||||
@@ -489,35 +511,50 @@ 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;
|
||||
}
|
||||
|
||||
bs->read_only = (s->snap != NULL);
|
||||
|
||||
s->event_reader_pos = 0;
|
||||
r = qemu_pipe(s->fds);
|
||||
if (r < 0) {
|
||||
error_report("error opening eventfd");
|
||||
goto failed;
|
||||
}
|
||||
fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
|
||||
fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
|
||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader,
|
||||
NULL, s);
|
||||
|
||||
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
rbd_close(s->image);
|
||||
failed_open:
|
||||
rados_ioctx_destroy(s->io_ctx);
|
||||
failed_shutdown:
|
||||
@@ -532,6 +569,10 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
|
||||
close(s->fds[0]);
|
||||
close(s->fds[1]);
|
||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL);
|
||||
|
||||
rbd_close(s->image);
|
||||
rados_ioctx_destroy(s->io_ctx);
|
||||
g_free(s->snap);
|
||||
@@ -548,7 +589,7 @@ static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
acb->cancelled = 1;
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(acb->common.bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
@@ -559,11 +600,34 @@ static const AIOCBInfo rbd_aiocb_info = {
|
||||
.cancel = qemu_rbd_aio_cancel,
|
||||
};
|
||||
|
||||
static void rbd_finish_bh(void *opaque)
|
||||
static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb)
|
||||
{
|
||||
RADOSCB *rcb = opaque;
|
||||
qemu_bh_delete(rcb->acb->bh);
|
||||
qemu_rbd_complete_aio(rcb);
|
||||
int ret = 0;
|
||||
while (1) {
|
||||
fd_set wfd;
|
||||
int fd = s->fds[RBD_FD_WRITE];
|
||||
|
||||
/* send the op pointer to the qemu thread that is responsible
|
||||
for the aio/op completion. Must do it in a qemu thread context */
|
||||
ret = write(fd, (void *)&rcb, sizeof(rcb));
|
||||
if (ret >= 0) {
|
||||
break;
|
||||
}
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (errno != EAGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
FD_ZERO(&wfd);
|
||||
FD_SET(fd, &wfd);
|
||||
do {
|
||||
ret = select(fd + 1, NULL, &wfd, NULL, NULL);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -571,19 +635,40 @@ static void rbd_finish_bh(void *opaque)
|
||||
*
|
||||
* Note: this function is being called from a non qemu thread so
|
||||
* we need to be careful about what we do here. Generally we only
|
||||
* schedule a BH, and do the rest of the io completion handling
|
||||
* from rbd_finish_bh() which runs in a qemu context.
|
||||
* write to the block notification pipe, and do the rest of the
|
||||
* io completion handling from qemu_rbd_aio_event_reader() which
|
||||
* runs in a qemu context.
|
||||
*/
|
||||
static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
|
||||
{
|
||||
RBDAIOCB *acb = rcb->acb;
|
||||
|
||||
int ret;
|
||||
rcb->ret = rbd_aio_get_return_value(c);
|
||||
rbd_aio_release(c);
|
||||
ret = qemu_rbd_send_pipe(rcb->s, rcb);
|
||||
if (ret < 0) {
|
||||
error_report("failed writing to acb->s->fds");
|
||||
g_free(rcb);
|
||||
}
|
||||
}
|
||||
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs),
|
||||
rbd_finish_bh, rcb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
/* Callback when all queued rbd_aio requests are complete */
|
||||
|
||||
static void rbd_aio_bh_cb(void *opaque)
|
||||
{
|
||||
RBDAIOCB *acb = opaque;
|
||||
|
||||
if (acb->cmd == RBD_AIO_READ) {
|
||||
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
|
||||
}
|
||||
qemu_vfree(acb->bounce);
|
||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
acb->status = 0;
|
||||
|
||||
if (!acb->cancelled) {
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
}
|
||||
|
||||
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
||||
@@ -678,16 +763,13 @@ static BlockDriverAIOCB *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_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
@@ -904,22 +986,18 @@ static BlockDriverAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
|
||||
}
|
||||
#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 = {
|
||||
@@ -931,7 +1009,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",
|
||||
|
||||
+186
-369
File diff suppressed because it is too large
Load Diff
+3
-78
@@ -25,24 +25,6 @@
|
||||
#include "block/snapshot.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
QemuOptsList internal_snapshot_opts = {
|
||||
.name = "snapshot",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = SNAPSHOT_OPT_ID,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "snapshot id"
|
||||
},{
|
||||
.name = SNAPSHOT_OPT_NAME,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "snapshot name"
|
||||
},{
|
||||
/* end of list */
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
||||
const char *name)
|
||||
{
|
||||
@@ -212,7 +194,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
* If only @snapshot_id is specified, delete the first one with id
|
||||
* @snapshot_id.
|
||||
* If only @name is specified, delete the first one with name @name.
|
||||
* if none is specified, return -EINVAL.
|
||||
* if none is specified, return -ENINVAL.
|
||||
*
|
||||
* Returns: 0 on success, -errno on failure. If @bs is not inserted, return
|
||||
* -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
|
||||
@@ -236,10 +218,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);
|
||||
}
|
||||
@@ -287,71 +265,18 @@ int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily load an internal snapshot by @snapshot_id and @name.
|
||||
* @bs: block device used in the operation
|
||||
* @snapshot_id: unique snapshot ID, or NULL
|
||||
* @name: snapshot name, or NULL
|
||||
* @errp: location to store error
|
||||
*
|
||||
* If both @snapshot_id and @name are specified, load the first one with
|
||||
* id @snapshot_id and name @name.
|
||||
* If only @snapshot_id is specified, load the first one with id
|
||||
* @snapshot_id.
|
||||
* If only @name is specified, load the first one with name @name.
|
||||
* if none is specified, return -EINVAL.
|
||||
*
|
||||
* Returns: 0 on success, -errno on fail. If @bs is not inserted, return
|
||||
* -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support
|
||||
* internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and
|
||||
* @name, return -ENOENT. If @errp != NULL, it will always be filled on
|
||||
* failure.
|
||||
*/
|
||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
const char *snapshot_name)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (!drv) {
|
||||
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (!snapshot_id && !name) {
|
||||
error_setg(errp, "snapshot_id and name are both NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bs->read_only) {
|
||||
error_setg(errp, "Device is not readonly");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (drv->bdrv_snapshot_load_tmp) {
|
||||
return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp);
|
||||
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
|
||||
}
|
||||
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
drv->format_name, bdrv_get_device_name(bs),
|
||||
"temporarily load internal snapshot");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
||||
const char *id_or_name,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err);
|
||||
if (ret == -ENOENT || ret == -EINVAL) {
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
|
||||
}
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+93
-124
@@ -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,9 +476,10 @@ 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;
|
||||
|
||||
@@ -532,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;
|
||||
}
|
||||
@@ -549,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;
|
||||
}
|
||||
|
||||
@@ -559,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;
|
||||
}
|
||||
|
||||
@@ -570,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;
|
||||
}
|
||||
@@ -589,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;
|
||||
}
|
||||
@@ -599,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;
|
||||
}
|
||||
|
||||
@@ -656,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;
|
||||
}
|
||||
@@ -675,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;
|
||||
@@ -700,20 +665,26 @@ static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ssh_state_init(&s);
|
||||
|
||||
/* Get desired file size. */
|
||||
total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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;
|
||||
@@ -723,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;
|
||||
}
|
||||
@@ -771,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;
|
||||
@@ -789,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
|
||||
@@ -838,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)
|
||||
{
|
||||
@@ -871,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) {
|
||||
@@ -906,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)
|
||||
{
|
||||
@@ -941,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) {
|
||||
@@ -960,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;
|
||||
}
|
||||
|
||||
@@ -988,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);
|
||||
|
||||
@@ -1009,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;
|
||||
|
||||
@@ -1017,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 &&
|
||||
@@ -1039,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;
|
||||
@@ -1082,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)
|
||||
|
||||
+10
-16
@@ -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,11 +72,9 @@ 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);
|
||||
}
|
||||
|
||||
static void coroutine_fn stream_run(void *opaque)
|
||||
@@ -90,11 +88,6 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
int n = 0;
|
||||
void *buf;
|
||||
|
||||
if (!bs->backing_hd) {
|
||||
block_job_completed(&s->common, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
block_job_completed(&s->common, s->common.len);
|
||||
@@ -159,14 +152,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;
|
||||
}
|
||||
}
|
||||
@@ -186,7 +179,7 @@ wait:
|
||||
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_str;
|
||||
base_id = s->backing_file_id;
|
||||
if (base->drv) {
|
||||
base_fmt = base->drv->format_name;
|
||||
}
|
||||
@@ -196,7 +189,6 @@ wait:
|
||||
}
|
||||
|
||||
qemu_vfree(buf);
|
||||
g_free(s->backing_file_str);
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
|
||||
@@ -218,7 +210,7 @@ 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,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
@@ -238,7 +230,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);
|
||||
|
||||
+58
-99
@@ -31,7 +31,7 @@
|
||||
* Allocation of blocks could be optimized (less writes to block map and
|
||||
* header).
|
||||
*
|
||||
* Read and write of adjacent blocks could be done in one operation
|
||||
* Read and write of adjacents blocks could be done in one operation
|
||||
* (current code uses one operation per block (1 MiB).
|
||||
*
|
||||
* The code is not thread safe (missing locks for changes in header and
|
||||
@@ -53,13 +53,6 @@
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/migration.h"
|
||||
#ifdef __linux__
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifndef FS_NOCOW_FL
|
||||
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_UUID)
|
||||
#include <uuid/uuid.h>
|
||||
@@ -246,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);
|
||||
@@ -342,7 +336,6 @@ static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
logout("\n");
|
||||
bdi->cluster_size = s->block_size;
|
||||
bdi->vm_state_offset = 0;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -397,9 +390,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
#endif
|
||||
|
||||
if (header.disk_size > VDI_DISK_SIZE_MAX) {
|
||||
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||
", max supported is 0x%" PRIx64 ")",
|
||||
header.disk_size, VDI_DISK_SIZE_MAX);
|
||||
logout("disk size is 0x%" PRIx64 ", max supported is 0x%" PRIx64,
|
||||
header.disk_size, VDI_DISK_SIZE_MAX);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -414,57 +406,48 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
if (header.signature != VDI_SIGNATURE) {
|
||||
error_setg(errp, "Image not in VDI format (bad signature %08" PRIx32
|
||||
")", header.signature);
|
||||
ret = -EINVAL;
|
||||
logout("bad vdi signature %08x\n", header.signature);
|
||||
ret = -EMEDIUMTYPE;
|
||||
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);
|
||||
logout("unsupported version %u.%u\n",
|
||||
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);
|
||||
logout("unsupported block map offset 0x%x B\n", 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);
|
||||
logout("unsupported data offset 0x%x B\n", 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);
|
||||
logout("unsupported sector size %u B\n", header.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);
|
||||
logout("unsupported block size %u B\n", header.block_size);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.disk_size >
|
||||
(uint64_t)header.blocks_in_image * header.block_size) {
|
||||
error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", "
|
||||
"image bitmap has room for %" PRIu64 ")",
|
||||
header.disk_size,
|
||||
(uint64_t)header.blocks_in_image * header.block_size);
|
||||
logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (!uuid_is_null(header.uuid_link)) {
|
||||
error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
|
||||
logout("link uuid != 0, unsupported\n");
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (!uuid_is_null(header.uuid_parent)) {
|
||||
error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
|
||||
logout("parent uuid != 0, unsupported\n");
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) {
|
||||
error_setg(errp, "unsupported VDI image "
|
||||
"(too many blocks %u, max is %u)",
|
||||
header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX);
|
||||
logout("too many blocks %u, max is %u)",
|
||||
header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -679,7 +662,8 @@ 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 fd;
|
||||
int result = 0;
|
||||
@@ -690,30 +674,35 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
VdiHeader header;
|
||||
size_t i;
|
||||
size_t bmap_size;
|
||||
bool nocow = false;
|
||||
|
||||
logout("\n");
|
||||
|
||||
/* Read out options. */
|
||||
bytes = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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
|
||||
nocow = qemu_opt_get_bool_del(opts, BLOCK_OPT_NOCOW, false);
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
if (bytes > VDI_DISK_SIZE_MAX) {
|
||||
result = -ENOTSUP;
|
||||
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||
", max supported is 0x%" PRIx64 ")",
|
||||
bytes, VDI_DISK_SIZE_MAX);
|
||||
logout("image size (size is 0x%" PRIx64
|
||||
", max supported is 0x%" PRIx64 ")",
|
||||
bytes, VDI_DISK_SIZE_MAX);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -725,21 +714,6 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nocow) {
|
||||
#ifdef __linux__
|
||||
/* Set NOCOW flag to solve performance issue on fs like btrfs.
|
||||
* This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
|
||||
* be ignored since any failure of this operation should not block the
|
||||
* left work.
|
||||
*/
|
||||
int attr;
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
|
||||
attr |= FS_NOCOW_FL;
|
||||
ioctl(fd, FS_IOC_SETFLAGS, &attr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* We need enough blocks to store the given disk size,
|
||||
so always round up. */
|
||||
blocks = (bytes + block_size - 1) / block_size;
|
||||
@@ -771,7 +745,6 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
vdi_header_to_le(&header);
|
||||
if (write(fd, &header, sizeof(header)) < 0) {
|
||||
result = -errno;
|
||||
goto close_and_exit;
|
||||
}
|
||||
|
||||
if (bmap_size > 0) {
|
||||
@@ -785,8 +758,6 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
if (write(fd, bmap, bmap_size) < 0) {
|
||||
result = -errno;
|
||||
g_free(bmap);
|
||||
goto close_and_exit;
|
||||
}
|
||||
g_free(bmap);
|
||||
}
|
||||
@@ -794,12 +765,10 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) {
|
||||
result = -errno;
|
||||
goto close_and_exit;
|
||||
}
|
||||
}
|
||||
|
||||
close_and_exit:
|
||||
if ((close(fd) < 0) && !result) {
|
||||
if (close(fd) < 0) {
|
||||
result = -errno;
|
||||
}
|
||||
|
||||
@@ -817,39 +786,29 @@ static void vdi_close(BlockDriverState *bs)
|
||||
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 = {
|
||||
@@ -871,7 +830,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,
|
||||
};
|
||||
|
||||
|
||||
+4
-15
@@ -578,7 +578,7 @@ static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
total_sectors = hdr.entry_length / VHDX_LOG_SECTOR_SIZE;
|
||||
|
||||
|
||||
/* read_desc() will increment the read idx */
|
||||
/* read_desc() will incrememnt the read idx */
|
||||
ret = vhdx_log_read_desc(bs, s, log, &desc_buffer);
|
||||
if (ret < 0) {
|
||||
goto free_and_exit;
|
||||
@@ -706,8 +706,7 @@ exit:
|
||||
*
|
||||
* If read-only, we must replay the log in RAM (or refuse to open
|
||||
* a dirty VHDX file read-only) */
|
||||
int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
|
||||
Error **errp)
|
||||
int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed)
|
||||
{
|
||||
int ret = 0;
|
||||
VHDXHeader *hdr;
|
||||
@@ -762,16 +761,6 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
|
||||
}
|
||||
|
||||
if (logs.valid) {
|
||||
if (bs->read_only) {
|
||||
ret = -EPERM;
|
||||
error_setg_errno(errp, EPERM,
|
||||
"VHDX image file '%s' opened read-only, but "
|
||||
"contains a log that needs to be replayed. To "
|
||||
"replay the log, execute:\n qemu-img check -r "
|
||||
"all '%s'",
|
||||
bs->filename, bs->filename);
|
||||
goto exit;
|
||||
}
|
||||
/* now flush the log */
|
||||
ret = vhdx_log_flush(bs, s, &logs);
|
||||
if (ret < 0) {
|
||||
@@ -965,8 +954,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
cpu_to_le32s((uint32_t *)(buffer + 4));
|
||||
|
||||
/* now write to the log */
|
||||
ret = vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer,
|
||||
desc_sectors + sectors);
|
||||
vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer,
|
||||
desc_sectors + sectors);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
+71
-103
@@ -374,7 +374,7 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
inactive_header->log_guid = *log_guid;
|
||||
}
|
||||
|
||||
ret = vhdx_write_header(bs->file, inactive_header, header_offset, true);
|
||||
vhdx_write_header(bs->file, inactive_header, header_offset, true);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -402,10 +402,9 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
|
||||
/* opens the specified header block from the VHDX file header section */
|
||||
static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
Error **errp)
|
||||
static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
VHDXHeader *header1;
|
||||
VHDXHeader *header2;
|
||||
bool h1_valid = false;
|
||||
@@ -463,6 +462,7 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
} else if (!h1_valid && h2_valid) {
|
||||
s->curr_header = 1;
|
||||
} else if (!h1_valid && !h2_valid) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
} else {
|
||||
/* If both headers are valid, then we choose the active one by the
|
||||
@@ -473,29 +473,27 @@ 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;
|
||||
}
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
|
||||
s->headers[s->curr_header]->log_length);
|
||||
|
||||
ret = 0;
|
||||
|
||||
goto exit;
|
||||
|
||||
fail:
|
||||
error_setg_errno(errp, -ret, "No valid VHDX header found");
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "No valid VHDX header found");
|
||||
qemu_vfree(header1);
|
||||
qemu_vfree(header2);
|
||||
s->headers[0] = NULL;
|
||||
s->headers[1] = NULL;
|
||||
exit:
|
||||
qemu_vfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -888,7 +886,8 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret = 0;
|
||||
uint32_t i;
|
||||
uint64_t signature;
|
||||
Error *local_err = NULL;
|
||||
bool log_flushed = false;
|
||||
|
||||
|
||||
s->bat = NULL;
|
||||
s->first_visible_write = true;
|
||||
@@ -911,14 +910,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* header update */
|
||||
vhdx_guid_generate(&s->session_guid);
|
||||
|
||||
vhdx_parse_header(bs, s, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
ret = vhdx_parse_header(bs, s);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = vhdx_parse_log(bs, s, &s->log_replayed_on_open, errp);
|
||||
ret = vhdx_parse_log(bs, s, &log_flushed);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1054,18 +1051,6 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num,
|
||||
}
|
||||
|
||||
|
||||
static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
|
||||
bdi->cluster_size = s->block_size;
|
||||
|
||||
bdi->unallocated_blocks_are_zero =
|
||||
(s->params.data_bits & VHDX_PARAMS_HAS_PARENT) == 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
@@ -1723,7 +1708,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;
|
||||
@@ -1736,15 +1722,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 = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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");
|
||||
@@ -1753,7 +1748,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")) {
|
||||
@@ -1793,15 +1788,13 @@ 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;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
NULL, &local_err);
|
||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
@@ -1814,13 +1807,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
|
||||
&creator_items, NULL);
|
||||
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
|
||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||
bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
if (creator) {
|
||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||
creator, creator_items * sizeof(gunichar2));
|
||||
bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), creator,
|
||||
creator_items * sizeof(gunichar2));
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
@@ -1853,64 +1846,41 @@ 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;
|
||||
}
|
||||
|
||||
/* If opened r/w, the VHDX driver will automatically replay the log,
|
||||
* if one is present, inside the vhdx_open() call.
|
||||
*
|
||||
* If qemu-img check -r all is called, the image is automatically opened
|
||||
* r/w and any log has already been replayed, so there is nothing (currently)
|
||||
* for us to do here
|
||||
*/
|
||||
static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
|
||||
if (s->log_replayed_on_open) {
|
||||
result->corruptions_fixed++;
|
||||
}
|
||||
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 = {
|
||||
@@ -1923,10 +1893,8 @@ static BlockDriver bdrv_vhdx = {
|
||||
.bdrv_co_readv = vhdx_co_readv,
|
||||
.bdrv_co_writev = vhdx_co_writev,
|
||||
.bdrv_create = vhdx_create,
|
||||
.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)
|
||||
|
||||
+4
-8
@@ -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,
|
||||
@@ -62,7 +61,7 @@
|
||||
/* These structures are ones that are defined in the VHDX specification
|
||||
* document */
|
||||
|
||||
#define VHDX_FILE_SIGNATURE 0x656C696678646876ULL /* "vhdxfile" in ASCII */
|
||||
#define VHDX_FILE_SIGNATURE 0x656C696678646876 /* "vhdxfile" in ASCII */
|
||||
typedef struct VHDXFileIdentifier {
|
||||
uint64_t signature; /* "vhdxfile" in ASCII */
|
||||
uint16_t creator[256]; /* optional; utf-16 string to identify
|
||||
@@ -239,7 +238,7 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
|
||||
/* upper 44 bits are the file offset in 1MB units lower 3 bits are the state
|
||||
other bits are reserved */
|
||||
#define VHDX_BAT_STATE_BIT_MASK 0x07
|
||||
#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000ULL /* upper 44 bits */
|
||||
#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000 /* upper 44 bits */
|
||||
typedef uint64_t VHDXBatEntry;
|
||||
|
||||
/* ---- METADATA REGION STRUCTURES ---- */
|
||||
@@ -248,7 +247,7 @@ typedef uint64_t VHDXBatEntry;
|
||||
#define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */
|
||||
#define VHDX_METADATA_TABLE_MAX_SIZE \
|
||||
(VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1))
|
||||
#define VHDX_METADATA_SIGNATURE 0x617461646174656DULL /* "metadata" in ASCII */
|
||||
#define VHDX_METADATA_SIGNATURE 0x617461646174656D /* "metadata" in ASCII */
|
||||
typedef struct QEMU_PACKED VHDXMetadataTableHeader {
|
||||
uint64_t signature; /* "metadata" in ASCII */
|
||||
uint16_t reserved;
|
||||
@@ -395,8 +394,6 @@ typedef struct BDRVVHDXState {
|
||||
|
||||
Error *migration_blocker;
|
||||
|
||||
bool log_replayed_on_open;
|
||||
|
||||
QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions;
|
||||
} BDRVVHDXState;
|
||||
|
||||
@@ -411,8 +408,7 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
|
||||
|
||||
bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
|
||||
|
||||
int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
|
||||
Error **errp);
|
||||
int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed);
|
||||
|
||||
int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
void *data, uint32_t length, uint64_t offset);
|
||||
|
||||
+205
-438
@@ -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);
|
||||
}
|
||||
|
||||
@@ -526,34 +526,8 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
Error **errp);
|
||||
|
||||
static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t size;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
size = bdrv_getlength(file);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "Could not access file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = MIN(size, 1 << 20); /* avoid unbounded allocation */
|
||||
buf = g_malloc0(size + 1);
|
||||
|
||||
ret = bdrv_pread(file, desc_offset, buf, size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read from file");
|
||||
g_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
||||
uint64_t desc_offset, Error **errp);
|
||||
|
||||
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
BlockDriverState *file,
|
||||
@@ -572,18 +546,11 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read header from file '%s'",
|
||||
file->filename);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (header.capacity == 0) {
|
||||
uint64_t desc_offset = le64_to_cpu(header.desc_offset);
|
||||
if (desc_offset) {
|
||||
char *buf = vmdk_read_desc(file, desc_offset << 9, errp);
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||
g_free(buf);
|
||||
return ret;
|
||||
return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,24 +605,17 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
header = footer.header;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(header.version) > 3) {
|
||||
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,
|
||||
bs->device_name, "vmdk", buf);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
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
|
||||
* persistent changed block tracking (CBT), and backup software can
|
||||
* read it as version=1 if it doesn't care about the changed area
|
||||
* information. So we are safe to enable read only. */
|
||||
error_setg(errp, "VMDK version 3 must be read only");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(header.num_gtes_per_gt) > 512) {
|
||||
error_setg(errp, "L2 table size too big");
|
||||
error_report("L2 table size too big");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -669,14 +629,6 @@ 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_getlength(file) <
|
||||
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes",
|
||||
(int64_t)(le64_to_cpu(header.grain_offset)
|
||||
* BDRV_SECTOR_SIZE));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = vmdk_add_extent(bs, file, false,
|
||||
le64_to_cpu(header.capacity),
|
||||
le64_to_cpu(header.gd_offset) << 9,
|
||||
@@ -691,10 +643,6 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
}
|
||||
extent->compressed =
|
||||
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;
|
||||
if (extent->compressed) {
|
||||
g_free(s->create_type);
|
||||
s->create_type = g_strdup("streamOptimized");
|
||||
}
|
||||
extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
|
||||
extent->version = le32_to_cpu(header.version);
|
||||
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;
|
||||
@@ -735,12 +683,16 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
|
||||
|
||||
/* Open an extent file and append to bs array */
|
||||
static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
BlockDriverState *file, int flags,
|
||||
char *buf, Error **errp)
|
||||
BlockDriverState *file,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
magic = ldl_be_p(buf);
|
||||
if (bdrv_pread(file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
magic = be32_to_cpu(magic);
|
||||
switch (magic) {
|
||||
case VMDK3_MAGIC:
|
||||
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
|
||||
@@ -749,8 +701,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
return vmdk_open_vmdk4(bs, file, flags, errp);
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Image not in VMDK format");
|
||||
return -EINVAL;
|
||||
return -EMEDIUMTYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -787,14 +738,9 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(type, "VMFS")) {
|
||||
if (ret == 4) {
|
||||
flat_offset = 0;
|
||||
} else {
|
||||
error_setg(errp, "Invalid extent lines:\n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
flat_offset = 0;
|
||||
} else if (ret != 4) {
|
||||
error_setg(errp, "Invalid extent lines:\n%s", p);
|
||||
error_setg(errp, "Invalid extent lines: \n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -807,9 +753,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
|
||||
path_combine(extent_path, sizeof(extent_path),
|
||||
desc_file_path, fname);
|
||||
extent_file = NULL;
|
||||
ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
|
||||
bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
|
||||
ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
|
||||
errp);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
@@ -826,14 +771,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
extent->flat_start_offset = flat_offset << 9;
|
||||
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
|
||||
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
|
||||
char *buf = vmdk_read_desc(extent_file, 0, errp);
|
||||
if (!buf) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
|
||||
}
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp);
|
||||
if (ret) {
|
||||
g_free(buf);
|
||||
bdrv_unref(extent_file);
|
||||
return ret;
|
||||
}
|
||||
@@ -856,16 +795,29 @@ next_line:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
Error **errp)
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
||||
uint64_t desc_offset, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char *buf = NULL;
|
||||
char ct[128];
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int64_t size;
|
||||
|
||||
size = bdrv_getlength(bs->file);
|
||||
if (size < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = MIN(size, 1 << 20); /* avoid unbounded allocation */
|
||||
buf = g_malloc0(size + 1);
|
||||
|
||||
ret = bdrv_pread(bs->file, desc_offset, buf, size);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
|
||||
error_setg(errp, "invalid VMDK image descriptor");
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(ct, "monolithicFlat") &&
|
||||
@@ -881,37 +833,24 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
s->desc_offset = 0;
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
|
||||
exit:
|
||||
g_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
char *buf = NULL;
|
||||
int ret;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
uint32_t magic;
|
||||
|
||||
buf = vmdk_read_desc(bs->file, 0, errp);
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) {
|
||||
s->desc_offset = 0x200;
|
||||
} else {
|
||||
ret = vmdk_open_desc_file(bs, flags, 0, errp);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
magic = ldl_be_p(buf);
|
||||
switch (magic) {
|
||||
case VMDK3_MAGIC:
|
||||
case VMDK4_MAGIC:
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
|
||||
s->desc_offset = 0x200;
|
||||
break;
|
||||
default:
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* try to open parent images, if exist */
|
||||
ret = vmdk_parent_open(bs);
|
||||
if (ret) {
|
||||
@@ -926,32 +865,16 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"vmdk", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
g_free(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
g_free(buf);
|
||||
g_free(s->create_type);
|
||||
s->create_type = NULL;
|
||||
vmdk_free_extents(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
if (!s->extents[i].flat) {
|
||||
bs->bl.write_zeroes_alignment =
|
||||
MAX(bs->bl.write_zeroes_alignment,
|
||||
s->extents[i].cluster_sectors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_whole_cluster(BlockDriverState *bs,
|
||||
VmdkExtent *extent,
|
||||
uint64_t cluster_offset,
|
||||
@@ -1183,7 +1106,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
break;
|
||||
case VMDK_OK:
|
||||
ret = BDRV_BLOCK_DATA;
|
||||
if (extent->file == bs->file && !extent->compressed) {
|
||||
if (extent->file == bs->file) {
|
||||
ret |= BDRV_BLOCK_OFFSET_VALID | offset;
|
||||
}
|
||||
|
||||
@@ -1386,8 +1309,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent = NULL;
|
||||
int ret;
|
||||
int64_t index_in_cluster, n;
|
||||
int n, ret;
|
||||
int64_t index_in_cluster;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
uint64_t cluster_offset;
|
||||
VmdkMetaData m_data;
|
||||
@@ -1494,23 +1417,9 @@ 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,
|
||||
BdrvRequestFlags flags)
|
||||
int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
@@ -1526,35 +1435,23 @@ 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)
|
||||
bool flat, bool compress, bool zeroed_grain)
|
||||
{
|
||||
int ret, i;
|
||||
BlockDriverState *bs = NULL;
|
||||
int fd = 0;
|
||||
VMDK4Header header;
|
||||
Error *local_err = NULL;
|
||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||
uint32_t *gd_buf = NULL;
|
||||
int gd_buf_size;
|
||||
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
assert(bs == NULL);
|
||||
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 exit;
|
||||
}
|
||||
|
||||
if (flat) {
|
||||
ret = bdrv_truncate(bs, filesize);
|
||||
ret = ftruncate(fd, filesize);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
ret = -errno;
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
@@ -1565,23 +1462,24 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
| (compress ? VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : 0)
|
||||
| (zeroed_grain ? VMDK4_FLAG_ZERO_GRAIN : 0);
|
||||
header.compressAlgorithm = compress ? VMDK4_COMPRESSION_DEFLATE : 0;
|
||||
header.capacity = filesize / BDRV_SECTOR_SIZE;
|
||||
header.capacity = filesize / 512;
|
||||
header.granularity = 128;
|
||||
header.num_gtes_per_gt = BDRV_SECTOR_SIZE;
|
||||
header.num_gtes_per_gt = 512;
|
||||
|
||||
grains = DIV_ROUND_UP(filesize / BDRV_SECTOR_SIZE, header.granularity);
|
||||
gt_size = DIV_ROUND_UP(header.num_gtes_per_gt * sizeof(uint32_t),
|
||||
BDRV_SECTOR_SIZE);
|
||||
gt_count = DIV_ROUND_UP(grains, header.num_gtes_per_gt);
|
||||
gd_sectors = DIV_ROUND_UP(gt_count * sizeof(uint32_t), BDRV_SECTOR_SIZE);
|
||||
grains = (filesize / 512 + header.granularity - 1) / header.granularity;
|
||||
gt_size = ((header.num_gtes_per_gt * sizeof(uint32_t)) + 511) >> 9;
|
||||
gt_count =
|
||||
(grains + header.num_gtes_per_gt - 1) / header.num_gtes_per_gt;
|
||||
gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
|
||||
|
||||
header.desc_offset = 1;
|
||||
header.desc_size = 20;
|
||||
header.rgd_offset = header.desc_offset + header.desc_size;
|
||||
header.gd_offset = header.rgd_offset + gd_sectors + (gt_size * gt_count);
|
||||
header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
|
||||
header.grain_offset =
|
||||
ROUND_UP(header.gd_offset + gd_sectors + (gt_size * gt_count),
|
||||
header.granularity);
|
||||
((header.gd_offset + gd_size + (gt_size * gt_count) +
|
||||
header.granularity - 1) / header.granularity) *
|
||||
header.granularity;
|
||||
/* swap endianness for all header fields */
|
||||
header.version = cpu_to_le32(header.version);
|
||||
header.flags = cpu_to_le32(header.flags);
|
||||
@@ -1601,55 +1499,48 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
header.check_bytes[3] = 0xa;
|
||||
|
||||
/* write all the data */
|
||||
ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic));
|
||||
if (ret < 0) {
|
||||
error_set(errp, QERR_IO_ERROR);
|
||||
ret = qemu_write_full(fd, &magic, sizeof(magic));
|
||||
if (ret != sizeof(magic)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_set(errp, QERR_IO_ERROR);
|
||||
ret = qemu_write_full(fd, &header, sizeof(header));
|
||||
if (ret != sizeof(header)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
|
||||
ret = ftruncate(fd, le64_to_cpu(header.grain_offset) << 9);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* write grain directory */
|
||||
gd_buf_size = gd_sectors * BDRV_SECTOR_SIZE;
|
||||
gd_buf = g_malloc0(gd_buf_size);
|
||||
for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_sectors;
|
||||
lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
|
||||
for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_size;
|
||||
i < gt_count; i++, tmp += gt_size) {
|
||||
gd_buf[i] = cpu_to_le32(tmp);
|
||||
}
|
||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
if (ret < 0) {
|
||||
error_set(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
ret = qemu_write_full(fd, &tmp, sizeof(tmp));
|
||||
if (ret != sizeof(tmp)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* write backup grain directory */
|
||||
for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_sectors;
|
||||
lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
|
||||
for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_size;
|
||||
i < gt_count; i++, tmp += gt_size) {
|
||||
gd_buf[i] = cpu_to_le32(tmp);
|
||||
}
|
||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
if (ret < 0) {
|
||||
error_set(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
ret = qemu_write_full(fd, &tmp, sizeof(tmp));
|
||||
if (ret != sizeof(tmp)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
g_free(gd_buf);
|
||||
exit:
|
||||
qemu_close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1693,20 +1584,19 @@ 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;
|
||||
char *desc = NULL;
|
||||
int fd, idx = 0;
|
||||
char desc[BUF_SIZE];
|
||||
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;
|
||||
GString *ext_desc_lines;
|
||||
char ext_desc_lines[BUF_SIZE] = "";
|
||||
char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
|
||||
const int64_t split_size = 0x80000000; /* VMDK has constant split size */
|
||||
const char *desc_extent_line;
|
||||
@@ -1714,12 +1604,11 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
uint32_t parent_cid = 0xffffffff;
|
||||
uint32_t number_heads = 16;
|
||||
bool zeroed_grain = false;
|
||||
uint32_t desc_offset = 0, desc_len;
|
||||
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"
|
||||
@@ -1731,37 +1620,38 @@ 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";
|
||||
|
||||
ext_desc_lines = g_string_new(NULL);
|
||||
|
||||
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Read out options */
|
||||
total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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") &&
|
||||
strcmp(adapter_type, "legacyESX")) {
|
||||
error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (strcmp(adapter_type, "ide") != 0) {
|
||||
/* that's the number of heads with which vmware operates when
|
||||
@@ -1770,15 +1660,14 @@ 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") &&
|
||||
strcmp(fmt, "twoGbMaxExtentFlat") &&
|
||||
strcmp(fmt, "streamOptimized")) {
|
||||
error_setg(errp, "Unknown subformat: '%s'", fmt);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
return -EINVAL;
|
||||
}
|
||||
split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
|
||||
strcmp(fmt, "twoGbMaxExtentSparse"));
|
||||
@@ -1786,31 +1675,28 @@ 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");
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (flat && zeroed_grain) {
|
||||
error_setg(errp, "Flat image can't enable zeroed grain");
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (backing_file) {
|
||||
BlockDriverState *bs = NULL;
|
||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_NO_BACKING, NULL,
|
||||
errp);
|
||||
BlockDriverState *bs = bdrv_new("");
|
||||
ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp);
|
||||
if (ret != 0) {
|
||||
goto exit;
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
||||
bdrv_unref(bs);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
return -EINVAL;
|
||||
}
|
||||
parent_cid = vmdk_read_cid(bs, 0);
|
||||
bdrv_unref(bs);
|
||||
@@ -1843,69 +1729,51 @@ 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)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
flat, compress, zeroed_grain)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
filesize -= size;
|
||||
|
||||
/* Format description line */
|
||||
snprintf(desc_line, sizeof(desc_line),
|
||||
desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename);
|
||||
g_string_append(ext_desc_lines, desc_line);
|
||||
desc_extent_line, size / 512, desc_filename);
|
||||
pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line);
|
||||
}
|
||||
/* generate descriptor file */
|
||||
desc = g_strdup_printf(desc_template,
|
||||
(uint32_t)time(NULL),
|
||||
parent_cid,
|
||||
fmt,
|
||||
parent_desc_line,
|
||||
ext_desc_lines->str,
|
||||
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
||||
total_size /
|
||||
(int64_t)(63 * number_heads * BDRV_SECTOR_SIZE),
|
||||
number_heads,
|
||||
adapter_type);
|
||||
desc_len = strlen(desc);
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (!split && !flat) {
|
||||
desc_offset = 0x200;
|
||||
snprintf(desc, sizeof(desc), desc_template,
|
||||
(unsigned int)time(NULL),
|
||||
parent_cid,
|
||||
fmt,
|
||||
parent_desc_line,
|
||||
ext_desc_lines,
|
||||
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
||||
total_size / (int64_t)(63 * number_heads * 512), number_heads,
|
||||
adapter_type);
|
||||
if (split || flat) {
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
} else {
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
}
|
||||
assert(new_bs == NULL);
|
||||
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);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write description");
|
||||
ret = qemu_write_full(fd, desc, strlen(desc));
|
||||
if (ret != strlen(desc)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
||||
* for description file */
|
||||
if (desc_offset == 0) {
|
||||
ret = bdrv_truncate(new_bs, desc_len);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
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);
|
||||
qemu_close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1993,53 +1861,6 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
|
||||
return info;
|
||||
}
|
||||
|
||||
static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent = NULL;
|
||||
int64_t sector_num = 0;
|
||||
int64_t total_sectors = bdrv_getlength(bs) / BDRV_SECTOR_SIZE;
|
||||
int ret;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
if (fix) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (sector_num >= total_sectors) {
|
||||
return 0;
|
||||
}
|
||||
extent = find_extent(s, sector_num, extent);
|
||||
if (!extent) {
|
||||
fprintf(stderr,
|
||||
"ERROR: could not find extent for sector %" PRId64 "\n",
|
||||
sector_num);
|
||||
break;
|
||||
}
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num << BDRV_SECTOR_BITS,
|
||||
0, &cluster_offset);
|
||||
if (ret == VMDK_ERROR) {
|
||||
fprintf(stderr,
|
||||
"ERROR: could not get cluster_offset for sector %"
|
||||
PRId64 "\n", sector_num);
|
||||
break;
|
||||
}
|
||||
if (ret == VMDK_OK && cluster_offset >= bdrv_getlength(extent->file)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: cluster offset for sector %"
|
||||
PRId64 " points after EOF\n", sector_num);
|
||||
break;
|
||||
}
|
||||
sector_num += extent->cluster_sectors;
|
||||
}
|
||||
|
||||
result->corruptions++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
||||
{
|
||||
int i;
|
||||
@@ -2071,88 +1892,41 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
||||
return spec_info;
|
||||
}
|
||||
|
||||
static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
int i;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
assert(s->num_extents);
|
||||
bdi->needs_compressed_writes = s->extents[0].compressed;
|
||||
if (!s->extents[0].flat) {
|
||||
bdi->cluster_size = s->extents[0].cluster_sectors << BDRV_SECTOR_BITS;
|
||||
}
|
||||
/* See if we have multiple extents but they have different cases */
|
||||
for (i = 1; i < s->num_extents; i++) {
|
||||
if (bdi->needs_compressed_writes != s->extents[i].compressed ||
|
||||
(bdi->cluster_size && bdi->cluster_size !=
|
||||
s->extents[i].cluster_sectors << BDRV_SECTOR_BITS)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
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 = {
|
||||
@@ -2160,11 +1934,9 @@ static BlockDriver bdrv_vmdk = {
|
||||
.instance_size = sizeof(BDRVVmdkState),
|
||||
.bdrv_probe = vmdk_probe,
|
||||
.bdrv_open = vmdk_open,
|
||||
.bdrv_check = vmdk_check,
|
||||
.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,
|
||||
@@ -2173,13 +1945,8 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
||||
.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)
|
||||
|
||||
+28
-79
@@ -29,13 +29,6 @@
|
||||
#if defined(CONFIG_UUID)
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifndef FS_NOCOW_FL
|
||||
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -200,8 +193,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
if (strncmp(footer->creator, "conectix", 8)) {
|
||||
error_setg(errp, "invalid VPC image");
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
disk_type = VHD_FIXED;
|
||||
@@ -487,19 +479,6 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVVPCState *s = (BDRVVPCState *)bs->opaque;
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (cpu_to_be32(footer->type) != VHD_FIXED) {
|
||||
bdi->cluster_size = s->block_size;
|
||||
}
|
||||
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
@@ -745,11 +724,12 @@ static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
|
||||
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;
|
||||
QEMUOptionParameter *disk_type_param;
|
||||
int fd, i;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
@@ -758,45 +738,27 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
bool nocow = false;
|
||||
|
||||
/* Read out options */
|
||||
total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
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;
|
||||
}
|
||||
nocow = qemu_opt_get_bool_del(opts, BLOCK_OPT_NOCOW, false);
|
||||
|
||||
/* Create the file */
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nocow) {
|
||||
#ifdef __linux__
|
||||
/* Set NOCOW flag to solve performance issue on fs like btrfs.
|
||||
* This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
|
||||
* be ignored since any failure of this operation should not block the
|
||||
* left work.
|
||||
*/
|
||||
int attr;
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
|
||||
attr |= FS_NOCOW_FL;
|
||||
ioctl(fd, FS_IOC_SETFLAGS, &attr);
|
||||
}
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -861,10 +823,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ret = create_fixed_disk(fd, buf, total_size);
|
||||
}
|
||||
|
||||
fail:
|
||||
fail:
|
||||
qemu_close(fd);
|
||||
out:
|
||||
g_free(disk_type_param);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -892,29 +852,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 = {
|
||||
@@ -930,9 +881,7 @@ static BlockDriver bdrv_vpc = {
|
||||
.bdrv_read = vpc_co_read,
|
||||
.bdrv_write = vpc_co_write,
|
||||
|
||||
.bdrv_get_info = vpc_get_info,
|
||||
|
||||
.create_opts = &vpc_create_opts,
|
||||
.create_options = vpc_create_options,
|
||||
.bdrv_has_zero_init = vpc_has_zero_init,
|
||||
};
|
||||
|
||||
|
||||
+59
-60
@@ -266,7 +266,8 @@ typedef struct mbr_t {
|
||||
} QEMU_PACKED mbr_t;
|
||||
|
||||
typedef struct direntry_t {
|
||||
uint8_t name[8 + 3];
|
||||
uint8_t name[8];
|
||||
uint8_t extension[3];
|
||||
uint8_t attributes;
|
||||
uint8_t reserved[2];
|
||||
uint16_t ctime;
|
||||
@@ -517,9 +518,11 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
|
||||
uint8_t chksum=0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(entry->name); i++) {
|
||||
chksum = (((chksum & 0xfe) >> 1) |
|
||||
((chksum & 0x01) ? 0x80 : 0)) + entry->name[i];
|
||||
for(i=0;i<11;i++) {
|
||||
unsigned char c;
|
||||
|
||||
c = (i < 8) ? entry->name[i] : entry->extension[i-8];
|
||||
chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
|
||||
}
|
||||
|
||||
return chksum;
|
||||
@@ -614,7 +617,7 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
|
||||
|
||||
if(is_dot) {
|
||||
entry=array_get_next(&(s->directory));
|
||||
memset(entry->name, 0x20, sizeof(entry->name));
|
||||
memset(entry->name,0x20,11);
|
||||
memcpy(entry->name,filename,strlen(filename));
|
||||
return entry;
|
||||
}
|
||||
@@ -629,14 +632,12 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
|
||||
i = 8;
|
||||
|
||||
entry=array_get_next(&(s->directory));
|
||||
memset(entry->name, 0x20, sizeof(entry->name));
|
||||
memset(entry->name,0x20,11);
|
||||
memcpy(entry->name, filename, i);
|
||||
|
||||
if (j > 0) {
|
||||
for (i = 0; i < 3 && filename[j + 1 + i]; i++) {
|
||||
entry->name[8 + i] = filename[j + 1 + i];
|
||||
}
|
||||
}
|
||||
if(j > 0)
|
||||
for (i = 0; i < 3 && filename[j+1+i]; i++)
|
||||
entry->extension[i] = filename[j+1+i];
|
||||
|
||||
/* upcase & remove unwanted characters */
|
||||
for(i=10;i>=0;i--) {
|
||||
@@ -833,8 +834,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;
|
||||
@@ -863,7 +863,8 @@ static int init_directories(BDRVVVFATState* s,
|
||||
{
|
||||
direntry_t* entry=array_get_next(&(s->directory));
|
||||
entry->attributes=0x28; /* archive | volume label */
|
||||
memcpy(entry->name, "QEMU VVFAT ", sizeof(entry->name));
|
||||
memcpy(entry->name,"QEMU VVF",8);
|
||||
memcpy(entry->extension,"AT ",3);
|
||||
}
|
||||
|
||||
/* Now build FAT, and write back information into directory */
|
||||
@@ -895,8 +896,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);
|
||||
@@ -922,10 +923,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 */
|
||||
@@ -983,7 +983,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)
|
||||
@@ -1087,17 +1087,19 @@ DLOG(if (stderr == NULL) {
|
||||
setbuf(stderr, NULL);
|
||||
})
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dirname = qemu_opt_get(opts, "dir");
|
||||
if (!dirname) {
|
||||
error_setg(errp, "vvfat block driver requires a 'dir' option");
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires "
|
||||
"a 'dir' option");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1123,7 +1125,6 @@ DLOG(if (stderr == NULL) {
|
||||
if (!s->fat_type) {
|
||||
s->fat_type = 16;
|
||||
}
|
||||
s->first_sectors_number = 0x40;
|
||||
cyls = s->fat_type == 12 ? 64 : 1024;
|
||||
heads = 16;
|
||||
secs = 63;
|
||||
@@ -1138,7 +1139,8 @@ DLOG(if (stderr == NULL) {
|
||||
case 12:
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Valid FAT types are only 12, 16 and 32");
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only "
|
||||
"12, 16 and 32");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1151,6 +1153,7 @@ DLOG(if (stderr == NULL) {
|
||||
|
||||
s->current_cluster=0xffffffff;
|
||||
|
||||
s->first_sectors_number=0x40;
|
||||
/* read only is the default for safety */
|
||||
bs->read_only = 1;
|
||||
s->qcow = s->write_target = NULL;
|
||||
@@ -1164,7 +1167,7 @@ DLOG(if (stderr == NULL) {
|
||||
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;
|
||||
}
|
||||
@@ -1173,7 +1176,7 @@ DLOG(if (stderr == NULL) {
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1590,20 +1593,17 @@ static int parse_short_name(BDRVVVFATState* s,
|
||||
lfn->name[i] = direntry->name[i];
|
||||
}
|
||||
|
||||
for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
|
||||
}
|
||||
for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
|
||||
if (j >= 0) {
|
||||
lfn->name[i++] = '.';
|
||||
lfn->name[i + j + 1] = '\0';
|
||||
for (;j >= 0; j--) {
|
||||
uint8_t c = direntry->name[8 + j];
|
||||
if (c <= ' ' || c > 0x7f) {
|
||||
return -2;
|
||||
} else if (s->downcase_short_names) {
|
||||
lfn->name[i + j] = qemu_tolower(c);
|
||||
} else {
|
||||
lfn->name[i + j] = c;
|
||||
}
|
||||
if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
|
||||
return -2;
|
||||
else if (s->downcase_short_names)
|
||||
lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
|
||||
else
|
||||
lfn->name[i + j] = direntry->extension[j];
|
||||
}
|
||||
} else
|
||||
lfn->name[i + j + 1] = '\0';
|
||||
@@ -2908,10 +2908,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);
|
||||
@@ -2921,32 +2922,30 @@ 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;
|
||||
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:");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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:");
|
||||
s->qcow = bdrv_new("");
|
||||
|
||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
|
||||
qemu_opts_del(opts);
|
||||
if (ret < 0) {
|
||||
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);
|
||||
ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
|
||||
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);
|
||||
bdrv_unref(s->qcow);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -2954,7 +2953,7 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
unlink(s->qcow_filename);
|
||||
#endif
|
||||
|
||||
bdrv_set_backing_hd(s->bs, bdrv_new("", &error_abort));
|
||||
s->bs->backing_hd = bdrv_new("");
|
||||
s->bs->backing_hd->drv = &vvfat_write_target;
|
||||
s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
|
||||
*(void**)s->bs->backing_hd->opaque = s;
|
||||
|
||||
+3
-24
@@ -40,7 +40,6 @@ struct QEMUWin32AIOState {
|
||||
HANDLE hIOCP;
|
||||
EventNotifier e;
|
||||
int count;
|
||||
bool is_aio_context_attached;
|
||||
};
|
||||
|
||||
typedef struct QEMUWin32AIOCB {
|
||||
@@ -115,7 +114,7 @@ static void win32_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
* wait for completion.
|
||||
*/
|
||||
while (!HasOverlappedIoCompleted(&waiocb->ov)) {
|
||||
aio_poll(bdrv_get_aio_context(blockacb->bs), true);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,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;
|
||||
@@ -209,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:
|
||||
@@ -217,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);
|
||||
}
|
||||
|
||||
+138
-438
File diff suppressed because it is too large
Load Diff
+32
-63
@@ -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,7 +34,6 @@
|
||||
#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, BlockDriverCompletionFunc *cb,
|
||||
@@ -41,16 +41,14 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
{
|
||||
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_set_in_use(bs, 1);
|
||||
|
||||
job = g_malloc0(driver->instance_size);
|
||||
job->driver = driver;
|
||||
job->bs = bs;
|
||||
job->cb = cb;
|
||||
@@ -63,11 +61,10 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
Error *local_err = NULL;
|
||||
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
if (error_is_set(&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;
|
||||
}
|
||||
@@ -82,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)
|
||||
@@ -92,11 +88,11 @@ 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);
|
||||
if (local_err) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
@@ -187,7 +183,7 @@ int block_job_cancel_sync(BlockJob *job)
|
||||
job->opaque = &data;
|
||||
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;
|
||||
}
|
||||
@@ -210,20 +206,6 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
||||
job->busy = true;
|
||||
}
|
||||
|
||||
void block_job_yield(BlockJob *job)
|
||||
{
|
||||
assert(job->busy);
|
||||
|
||||
/* Check cancellation *before* setting busy = false, too! */
|
||||
if (block_job_is_cancelled(job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
job->busy = false;
|
||||
qemu_coroutine_yield();
|
||||
job->busy = true;
|
||||
}
|
||||
|
||||
BlockJobInfo *block_job_query(BlockJob *job)
|
||||
{
|
||||
BlockJobInfo *info = g_new0(BlockJobInfo, 1);
|
||||
@@ -246,35 +228,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)
|
||||
{
|
||||
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,
|
||||
@@ -285,26 +258,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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-3
@@ -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;
|
||||
|
||||
@@ -1000,11 +1000,13 @@ int main(int argc, char **argv)
|
||||
memset(ts, 0, sizeof(TaskState));
|
||||
init_task_state(ts);
|
||||
ts->info = info;
|
||||
cpu->opaque = ts;
|
||||
env->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));
|
||||
}
|
||||
|
||||
+26
-10
@@ -30,14 +30,20 @@ typedef struct {
|
||||
CoroutineAction action;
|
||||
} CoroutineGThread;
|
||||
|
||||
static CompatGMutex coroutine_lock;
|
||||
static CompatGCond coroutine_cond;
|
||||
static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
|
||||
|
||||
/* GLib 2.31 and beyond deprecated various parts of the thread API,
|
||||
* but the new interfaces are not available in older GLib versions
|
||||
* so we have to cope with both.
|
||||
*/
|
||||
#if GLIB_CHECK_VERSION(2, 31, 0)
|
||||
/* Default zero-initialisation is sufficient for 2.31+ GCond */
|
||||
static GCond the_coroutine_cond;
|
||||
static GCond *coroutine_cond = &the_coroutine_cond;
|
||||
static inline void init_coroutine_cond(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Awkwardly, the GPrivate API doesn't provide a way to update the
|
||||
* GDestroyNotify handler for the coroutine key dynamically. So instead
|
||||
* we track whether or not the CoroutineGThread should be freed on
|
||||
@@ -78,6 +84,11 @@ static inline GThread *create_thread(GThreadFunc func, gpointer data)
|
||||
#else
|
||||
|
||||
/* Handle older GLib versions */
|
||||
static GCond *coroutine_cond;
|
||||
static inline void init_coroutine_cond(void)
|
||||
{
|
||||
coroutine_cond = g_cond_new();
|
||||
}
|
||||
|
||||
static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
|
||||
|
||||
@@ -104,25 +115,30 @@ static inline GThread *create_thread(GThreadFunc func, gpointer data)
|
||||
|
||||
static void __attribute__((constructor)) coroutine_init(void)
|
||||
{
|
||||
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
||||
if (!g_thread_supported()) {
|
||||
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
||||
g_thread_init(NULL);
|
||||
}
|
||||
#else
|
||||
fprintf(stderr, "glib threading failed to initialize.\n");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
init_coroutine_cond();
|
||||
}
|
||||
|
||||
static void coroutine_wait_runnable_locked(CoroutineGThread *co)
|
||||
{
|
||||
while (!co->runnable) {
|
||||
g_cond_wait(&coroutine_cond, &coroutine_lock);
|
||||
g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock));
|
||||
}
|
||||
}
|
||||
|
||||
static void coroutine_wait_runnable(CoroutineGThread *co)
|
||||
{
|
||||
g_mutex_lock(&coroutine_lock);
|
||||
g_static_mutex_lock(&coroutine_lock);
|
||||
coroutine_wait_runnable_locked(co);
|
||||
g_mutex_unlock(&coroutine_lock);
|
||||
g_static_mutex_unlock(&coroutine_lock);
|
||||
}
|
||||
|
||||
static gpointer coroutine_thread(gpointer opaque)
|
||||
@@ -164,17 +180,17 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_,
|
||||
CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_);
|
||||
CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_);
|
||||
|
||||
g_mutex_lock(&coroutine_lock);
|
||||
g_static_mutex_lock(&coroutine_lock);
|
||||
from->runnable = false;
|
||||
from->action = action;
|
||||
to->runnable = true;
|
||||
to->action = action;
|
||||
g_cond_broadcast(&coroutine_cond);
|
||||
g_cond_broadcast(coroutine_cond);
|
||||
|
||||
if (action != COROUTINE_TERMINATE) {
|
||||
coroutine_wait_runnable_locked(from);
|
||||
}
|
||||
g_mutex_unlock(&coroutine_lock);
|
||||
g_static_mutex_unlock(&coroutine_lock);
|
||||
return from->action;
|
||||
}
|
||||
|
||||
|
||||
+86
-104
@@ -23,22 +23,29 @@
|
||||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
void cpu_loop_exit(CPUState *cpu)
|
||||
bool qemu_cpu_has_work(CPUState *cpu)
|
||||
{
|
||||
return cpu_has_work(cpu);
|
||||
}
|
||||
|
||||
void cpu_loop_exit(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
cpu->current_tb = NULL;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
siglongjmp(env->jmp_env, 1);
|
||||
}
|
||||
|
||||
/* exit the current TB from a signal handler. The host registers are
|
||||
restored in a state compatible with the CPU emulator
|
||||
*/
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
void cpu_resume_from_signal(CPUState *cpu, void *puc)
|
||||
void cpu_resume_from_signal(CPUArchState *env, void *puc)
|
||||
{
|
||||
/* XXX: restore cpu registers saved in host registers */
|
||||
|
||||
cpu->exception_index = -1;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
env->exception_index = -1;
|
||||
siglongjmp(env->jmp_env, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -46,25 +53,7 @@ void cpu_resume_from_signal(CPUState *cpu, void *puc)
|
||||
static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
uintptr_t next_tb;
|
||||
|
||||
#if defined(DEBUG_DISAS)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
|
||||
#if defined(TARGET_I386)
|
||||
log_cpu_state(cpu, CPU_DUMP_CCOP);
|
||||
#elif defined(TARGET_M68K)
|
||||
/* ??? Should not modify env state for dumping. */
|
||||
cpu_m68k_flush_flags(env, env->cc_op);
|
||||
env->cc_op = CC_OP_FLAGS;
|
||||
env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4);
|
||||
log_cpu_state(cpu, 0);
|
||||
#else
|
||||
log_cpu_state(cpu, 0);
|
||||
#endif
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
|
||||
next_tb = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
uintptr_t next_tb = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
if ((next_tb & TB_EXIT_MASK) > TB_EXIT_IDX1) {
|
||||
/* We didn't start executing this TB (eg because the instruction
|
||||
* counter hit zero); we must restore the guest PC to the address
|
||||
@@ -101,7 +90,7 @@ static void cpu_exec_nocache(CPUArchState *env, int max_cycles,
|
||||
if (max_cycles > CF_COUNT_MASK)
|
||||
max_cycles = CF_COUNT_MASK;
|
||||
|
||||
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
|
||||
tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
|
||||
max_cycles);
|
||||
cpu->current_tb = tb;
|
||||
/* execute the generated code */
|
||||
@@ -116,7 +105,6 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
|
||||
target_ulong cs_base,
|
||||
uint64_t flags)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
TranslationBlock *tb, **ptb1;
|
||||
unsigned int h;
|
||||
tb_page_addr_t phys_pc, phys_page1;
|
||||
@@ -154,7 +142,7 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
|
||||
}
|
||||
not_found:
|
||||
/* if no translated code available, then translate it now */
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, 0);
|
||||
tb = tb_gen_code(env, pc, cs_base, flags, 0);
|
||||
|
||||
found:
|
||||
/* Move the last found TB to the head of the list */
|
||||
@@ -164,13 +152,12 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
|
||||
tcg_ctx.tb_ctx.tb_phys_hash[h] = tb;
|
||||
}
|
||||
/* we add the TB in the virtual pc hash table */
|
||||
cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
|
||||
env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
|
||||
return tb;
|
||||
}
|
||||
|
||||
static inline TranslationBlock *tb_find_fast(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
int flags;
|
||||
@@ -179,7 +166,7 @@ static inline TranslationBlock *tb_find_fast(CPUArchState *env)
|
||||
always be the same before a given translated block
|
||||
is executed. */
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
tb = cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
|
||||
tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
|
||||
if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base ||
|
||||
tb->flags != flags)) {
|
||||
tb = tb_find_slow(env, pc, cs_base, flags);
|
||||
@@ -196,11 +183,10 @@ void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
|
||||
|
||||
static void cpu_handle_debug_exception(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
CPUWatchpoint *wp;
|
||||
|
||||
if (!cpu->watchpoint_hit) {
|
||||
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
|
||||
if (!env->watchpoint_hit) {
|
||||
QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
|
||||
wp->flags &= ~BP_WATCHPOINT_HIT;
|
||||
}
|
||||
}
|
||||
@@ -219,16 +205,11 @@ int cpu_exec(CPUArchState *env)
|
||||
#if !(defined(CONFIG_USER_ONLY) && \
|
||||
(defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X)))
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
#endif
|
||||
#ifdef TARGET_I386
|
||||
X86CPU *x86_cpu = X86_CPU(cpu);
|
||||
#endif
|
||||
int ret, interrupt_request;
|
||||
TranslationBlock *tb;
|
||||
uint8_t *tc_ptr;
|
||||
uintptr_t next_tb;
|
||||
/* This must be volatile so it is not trashed by longjmp() */
|
||||
volatile bool have_tb_lock = false;
|
||||
|
||||
if (cpu->halted) {
|
||||
if (!cpu_has_work(cpu)) {
|
||||
@@ -281,16 +262,16 @@ int cpu_exec(CPUArchState *env)
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
cpu->exception_index = -1;
|
||||
env->exception_index = -1;
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
for(;;) {
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
if (sigsetjmp(env->jmp_env, 0) == 0) {
|
||||
/* if an exception is pending, we execute it here */
|
||||
if (cpu->exception_index >= 0) {
|
||||
if (cpu->exception_index >= EXCP_INTERRUPT) {
|
||||
if (env->exception_index >= 0) {
|
||||
if (env->exception_index >= EXCP_INTERRUPT) {
|
||||
/* exit request from the cpu execution loop */
|
||||
ret = cpu->exception_index;
|
||||
ret = env->exception_index;
|
||||
if (ret == EXCP_DEBUG) {
|
||||
cpu_handle_debug_exception(env);
|
||||
}
|
||||
@@ -303,11 +284,11 @@ int cpu_exec(CPUArchState *env)
|
||||
#if defined(TARGET_I386)
|
||||
cc->do_interrupt(cpu);
|
||||
#endif
|
||||
ret = cpu->exception_index;
|
||||
ret = env->exception_index;
|
||||
break;
|
||||
#else
|
||||
cc->do_interrupt(cpu);
|
||||
cpu->exception_index = -1;
|
||||
env->exception_index = -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -322,8 +303,8 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
||||
cpu->exception_index = EXCP_DEBUG;
|
||||
cpu_loop_exit(cpu);
|
||||
env->exception_index = EXCP_DEBUG;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
#if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \
|
||||
defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \
|
||||
@@ -331,38 +312,32 @@ int cpu_exec(CPUArchState *env)
|
||||
if (interrupt_request & CPU_INTERRUPT_HALT) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
||||
cpu->halted = 1;
|
||||
cpu->exception_index = EXCP_HLT;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#endif
|
||||
#if defined(TARGET_I386)
|
||||
if (interrupt_request & CPU_INTERRUPT_INIT) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0);
|
||||
do_cpu_init(x86_cpu);
|
||||
cpu->exception_index = EXCP_HALTED;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#else
|
||||
if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
cpu_reset(cpu);
|
||||
env->exception_index = EXCP_HLT;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
#endif
|
||||
#if defined(TARGET_I386)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (interrupt_request & CPU_INTERRUPT_POLL) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
|
||||
apic_poll_irq(x86_cpu->apic_state);
|
||||
apic_poll_irq(env->apic_state);
|
||||
}
|
||||
#endif
|
||||
if (interrupt_request & CPU_INTERRUPT_SIPI) {
|
||||
do_cpu_sipi(x86_cpu);
|
||||
if (interrupt_request & CPU_INTERRUPT_INIT) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT,
|
||||
0);
|
||||
do_cpu_init(x86_env_get_cpu(env));
|
||||
env->exception_index = EXCP_HALTED;
|
||||
cpu_loop_exit(env);
|
||||
} else if (interrupt_request & CPU_INTERRUPT_SIPI) {
|
||||
do_cpu_sipi(x86_env_get_cpu(env));
|
||||
} else if (env->hflags2 & HF2_GIF_MASK) {
|
||||
if ((interrupt_request & CPU_INTERRUPT_SMI) &&
|
||||
!(env->hflags & HF_SMM_MASK)) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_SMI,
|
||||
0);
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
|
||||
do_smm_enter(x86_cpu);
|
||||
do_smm_enter(x86_env_get_cpu(env));
|
||||
next_tb = 0;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
|
||||
!(env->hflags2 & HF2_NMI_MASK)) {
|
||||
@@ -399,10 +374,7 @@ int cpu_exec(CPUArchState *env)
|
||||
/* FIXME: this should respect TPR */
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR,
|
||||
0);
|
||||
intno = ldl_phys(cpu->as,
|
||||
env->vm_vmcb
|
||||
+ offsetof(struct vmcb,
|
||||
control.int_vector));
|
||||
intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||
@@ -411,6 +383,9 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
}
|
||||
#elif defined(TARGET_PPC)
|
||||
if ((interrupt_request & CPU_INTERRUPT_RESET)) {
|
||||
cpu_reset(cpu);
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
ppc_hw_interrupt(env);
|
||||
if (env->pending_interrupts == 0) {
|
||||
@@ -421,7 +396,7 @@ int cpu_exec(CPUArchState *env)
|
||||
#elif defined(TARGET_LM32)
|
||||
if ((interrupt_request & CPU_INTERRUPT_HARD)
|
||||
&& (env->ie & IE_IE)) {
|
||||
cpu->exception_index = EXCP_IRQ;
|
||||
env->exception_index = EXCP_IRQ;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -430,7 +405,7 @@ int cpu_exec(CPUArchState *env)
|
||||
&& (env->sregs[SR_MSR] & MSR_IE)
|
||||
&& !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))
|
||||
&& !(env->iflags & (D_FLAG | IMM_FLAG))) {
|
||||
cpu->exception_index = EXCP_IRQ;
|
||||
env->exception_index = EXCP_IRQ;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -438,7 +413,7 @@ int cpu_exec(CPUArchState *env)
|
||||
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
cpu_mips_hw_interrupts_pending(env)) {
|
||||
/* Raise it */
|
||||
cpu->exception_index = EXCP_EXT_INTERRUPT;
|
||||
env->exception_index = EXCP_EXT_INTERRUPT;
|
||||
env->error_code = 0;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
@@ -455,7 +430,7 @@ int cpu_exec(CPUArchState *env)
|
||||
idx = EXCP_TICK;
|
||||
}
|
||||
if (idx >= 0) {
|
||||
cpu->exception_index = idx;
|
||||
env->exception_index = idx;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -470,7 +445,7 @@ int cpu_exec(CPUArchState *env)
|
||||
if (((type == TT_EXTINT) &&
|
||||
cpu_pil_allowed(env, pil)) ||
|
||||
type != TT_EXTINT) {
|
||||
cpu->exception_index = env->interrupt_index;
|
||||
env->exception_index = env->interrupt_index;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -478,8 +453,8 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
#elif defined(TARGET_ARM)
|
||||
if (interrupt_request & CPU_INTERRUPT_FIQ
|
||||
&& !(env->daif & PSTATE_F)) {
|
||||
cpu->exception_index = EXCP_FIQ;
|
||||
&& !(env->uncached_cpsr & CPSR_F)) {
|
||||
env->exception_index = EXCP_FIQ;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -494,15 +469,15 @@ int cpu_exec(CPUArchState *env)
|
||||
pc contains a magic address. */
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
||||
|| !(env->daif & PSTATE_I))) {
|
||||
cpu->exception_index = EXCP_IRQ;
|
||||
|| !(env->uncached_cpsr & CPSR_I))) {
|
||||
env->exception_index = EXCP_IRQ;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& !(env->uncached_asr & ASR_I)) {
|
||||
cpu->exception_index = UC32_EXCP_INTR;
|
||||
env->exception_index = UC32_EXCP_INTR;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -537,7 +512,7 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
}
|
||||
if (idx >= 0) {
|
||||
cpu->exception_index = idx;
|
||||
env->exception_index = idx;
|
||||
env->error_code = 0;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
@@ -547,7 +522,7 @@ int cpu_exec(CPUArchState *env)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& (env->pregs[PR_CCS] & I_FLAG)
|
||||
&& !env->locked_irq) {
|
||||
cpu->exception_index = EXCP_IRQ;
|
||||
env->exception_index = EXCP_IRQ;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -559,7 +534,7 @@ int cpu_exec(CPUArchState *env)
|
||||
m_flag_archval = M_FLAG_V32;
|
||||
}
|
||||
if ((env->pregs[PR_CCS] & m_flag_archval)) {
|
||||
cpu->exception_index = EXCP_NMI;
|
||||
env->exception_index = EXCP_NMI;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -573,7 +548,7 @@ int cpu_exec(CPUArchState *env)
|
||||
hardware doesn't rely on this, so we
|
||||
provide/save the vector when the interrupt is
|
||||
first signalled. */
|
||||
cpu->exception_index = env->pending_vector;
|
||||
env->exception_index = env->pending_vector;
|
||||
do_interrupt_m68k_hardirq(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -585,7 +560,7 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
#elif defined(TARGET_XTENSA)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
cpu->exception_index = EXC_IRQ;
|
||||
env->exception_index = EXC_IRQ;
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -601,11 +576,26 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
if (unlikely(cpu->exit_request)) {
|
||||
cpu->exit_request = 0;
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit(cpu);
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
#if defined(DEBUG_DISAS)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
|
||||
/* restore flags in standard format */
|
||||
#if defined(TARGET_I386)
|
||||
log_cpu_state(cpu, CPU_DUMP_CCOP);
|
||||
#elif defined(TARGET_M68K)
|
||||
cpu_m68k_flush_flags(env, env->cc_op);
|
||||
env->cc_op = CC_OP_FLAGS;
|
||||
env->sr = (env->sr & 0xffe0)
|
||||
| env->cc_dest | (env->cc_x << 4);
|
||||
log_cpu_state(cpu, 0);
|
||||
#else
|
||||
log_cpu_state(cpu, 0);
|
||||
#endif
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
spin_lock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
have_tb_lock = true;
|
||||
tb = tb_find_fast(env);
|
||||
/* Note: we do it here to avoid a gcc bug on Mac OS X when
|
||||
doing it in tb_find_slow */
|
||||
@@ -627,7 +617,6 @@ int cpu_exec(CPUArchState *env)
|
||||
tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK),
|
||||
next_tb & TB_EXIT_MASK, tb);
|
||||
}
|
||||
have_tb_lock = false;
|
||||
spin_unlock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
|
||||
/* cpu_interrupt might be called while translating the
|
||||
@@ -657,25 +646,25 @@ int cpu_exec(CPUArchState *env)
|
||||
/* Instruction counter expired. */
|
||||
int insns_left;
|
||||
tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
|
||||
insns_left = cpu->icount_decr.u32;
|
||||
if (cpu->icount_extra && insns_left >= 0) {
|
||||
insns_left = env->icount_decr.u32;
|
||||
if (env->icount_extra && insns_left >= 0) {
|
||||
/* Refill decrementer and continue execution. */
|
||||
cpu->icount_extra += insns_left;
|
||||
if (cpu->icount_extra > 0xffff) {
|
||||
env->icount_extra += insns_left;
|
||||
if (env->icount_extra > 0xffff) {
|
||||
insns_left = 0xffff;
|
||||
} else {
|
||||
insns_left = cpu->icount_extra;
|
||||
insns_left = env->icount_extra;
|
||||
}
|
||||
cpu->icount_extra -= insns_left;
|
||||
cpu->icount_decr.u16.low = insns_left;
|
||||
env->icount_extra -= insns_left;
|
||||
env->icount_decr.u16.low = insns_left;
|
||||
} else {
|
||||
if (insns_left > 0) {
|
||||
/* Execute remaining instructions. */
|
||||
cpu_exec_nocache(env, insns_left, tb);
|
||||
}
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
next_tb = 0;
|
||||
cpu_loop_exit(cpu);
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -696,13 +685,6 @@ int cpu_exec(CPUArchState *env)
|
||||
(defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X)))
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
#endif
|
||||
#ifdef TARGET_I386
|
||||
x86_cpu = X86_CPU(cpu);
|
||||
#endif
|
||||
if (have_tb_lock) {
|
||||
spin_unlock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
have_tb_lock = false;
|
||||
}
|
||||
}
|
||||
} /* for(;;) */
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "config-host.h"
|
||||
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/dma.h"
|
||||
@@ -39,7 +38,6 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/seqlock.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "qemu/compatfd.h"
|
||||
@@ -78,7 +76,7 @@ static bool cpu_thread_is_idle(CPUState *cpu)
|
||||
if (cpu_is_stopped(cpu)) {
|
||||
return true;
|
||||
}
|
||||
if (!cpu->halted || cpu_has_work(cpu) ||
|
||||
if (!cpu->halted || qemu_cpu_has_work(cpu) ||
|
||||
kvm_halt_in_kernel()) {
|
||||
return false;
|
||||
}
|
||||
@@ -141,10 +139,11 @@ static int64_t cpu_get_icount_locked(void)
|
||||
|
||||
icount = qemu_icount;
|
||||
if (cpu) {
|
||||
if (!cpu_can_do_io(cpu)) {
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
if (!can_do_io(env)) {
|
||||
fprintf(stderr, "Bad clock read\n");
|
||||
}
|
||||
icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
|
||||
icount -= (env->icount_decr.u16.low + env->icount_extra);
|
||||
}
|
||||
return qemu_icount_bias + (icount << icount_time_shift);
|
||||
}
|
||||
@@ -349,7 +348,7 @@ void qtest_clock_warp(int64_t dest)
|
||||
assert(qtest_enabled());
|
||||
while (clock < dest) {
|
||||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
|
||||
int64_t warp = MIN(dest - clock, deadline);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock);
|
||||
qemu_icount_bias += warp;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
|
||||
@@ -432,7 +431,8 @@ static const VMStateDescription vmstate_timers = {
|
||||
.name = "timer",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(cpu_ticks_offset, TimersState),
|
||||
VMSTATE_INT64(dummy, TimersState),
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
@@ -523,15 +523,6 @@ void cpu_synchronize_all_post_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_clean_all_dirty(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_clean_state(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_vm_stop(RunState state)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -541,7 +532,7 @@ static int do_vm_stop(RunState state)
|
||||
pause_all_vcpus();
|
||||
runstate_set(state);
|
||||
vm_state_notify(0, state);
|
||||
qapi_event_send_stop(&error_abort);
|
||||
monitor_protocol_event(QEVENT_STOP, NULL);
|
||||
}
|
||||
|
||||
bdrv_drain_all();
|
||||
@@ -1126,25 +1117,16 @@ void resume_all_vcpus(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* For temporary buffers for forming a name */
|
||||
#define VCPU_THREAD_NAME_SIZE 16
|
||||
|
||||
static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
tcg_cpu_address_space_init(cpu, cpu->as);
|
||||
|
||||
/* share a single thread for all cpus with TCG */
|
||||
if (!tcg_cpu_thread) {
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
tcg_halt_cond = cpu->halt_cond;
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
qemu_thread_create(cpu->thread, qemu_tcg_cpu_thread_fn, cpu,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
#ifdef _WIN32
|
||||
cpu->hThread = qemu_thread_get_handle(cpu->thread);
|
||||
#endif
|
||||
@@ -1160,15 +1142,11 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||
|
||||
static void qemu_kvm_start_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, qemu_kvm_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
qemu_thread_create(cpu->thread, qemu_kvm_cpu_thread_fn, cpu,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
while (!cpu->created) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
}
|
||||
@@ -1176,14 +1154,10 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
|
||||
|
||||
static void qemu_dummy_start_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, qemu_dummy_cpu_thread_fn, cpu,
|
||||
qemu_thread_create(cpu->thread, qemu_dummy_cpu_thread_fn, cpu,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
while (!cpu->created) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
@@ -1217,7 +1191,6 @@ void cpu_stop_current(void)
|
||||
int vm_stop(RunState state)
|
||||
{
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
qemu_system_vmstop_request_prepare();
|
||||
qemu_system_vmstop_request(state);
|
||||
/*
|
||||
* FIXME: should not return to device code in case
|
||||
@@ -1246,7 +1219,6 @@ int vm_stop_force_state(RunState state)
|
||||
|
||||
static int tcg_cpu_exec(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int ret;
|
||||
#ifdef CONFIG_PROFILER
|
||||
int64_t ti;
|
||||
@@ -1259,9 +1231,9 @@ static int tcg_cpu_exec(CPUArchState *env)
|
||||
int64_t count;
|
||||
int64_t deadline;
|
||||
int decr;
|
||||
qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
|
||||
cpu->icount_decr.u16.low = 0;
|
||||
cpu->icount_extra = 0;
|
||||
qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
|
||||
env->icount_decr.u16.low = 0;
|
||||
env->icount_extra = 0;
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
/* Maintain prior (possibly buggy) behaviour where if no deadline
|
||||
@@ -1277,8 +1249,8 @@ static int tcg_cpu_exec(CPUArchState *env)
|
||||
qemu_icount += count;
|
||||
decr = (count > 0xffff) ? 0xffff : count;
|
||||
count -= decr;
|
||||
cpu->icount_decr.u16.low = decr;
|
||||
cpu->icount_extra = count;
|
||||
env->icount_decr.u16.low = decr;
|
||||
env->icount_extra = count;
|
||||
}
|
||||
ret = cpu_exec(env);
|
||||
#ifdef CONFIG_PROFILER
|
||||
@@ -1287,9 +1259,10 @@ static int tcg_cpu_exec(CPUArchState *env)
|
||||
if (use_icount) {
|
||||
/* Fold pending instructions back into the
|
||||
instruction counter, and clear the interrupt flag. */
|
||||
qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
|
||||
cpu->icount_decr.u32 = 0;
|
||||
cpu->icount_extra = 0;
|
||||
qemu_icount -= (env->icount_decr.u16.low
|
||||
+ env->icount_extra);
|
||||
env->icount_decr.u32 = 0;
|
||||
env->icount_extra = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1324,6 +1297,20 @@ static void tcg_exec_all(void)
|
||||
exit_request = 0;
|
||||
}
|
||||
|
||||
void set_numa_modes(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
int i;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
if (test_bit(cpu->cpu_index, node_cpumask[i])) {
|
||||
cpu->numa_node = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
|
||||
{
|
||||
/* XXX: implement xxx_cpu_list for targets that still miss it */
|
||||
@@ -1451,7 +1438,7 @@ void qmp_pmemsave(int64_t addr, int64_t size, const char *filename,
|
||||
l = sizeof(buf);
|
||||
if (l > size)
|
||||
l = size;
|
||||
cpu_physical_memory_read(addr, buf, l);
|
||||
cpu_physical_memory_rw(addr, buf, l, 0);
|
||||
if (fwrite(buf, 1, l, f) != l) {
|
||||
error_set(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
@@ -1471,11 +1458,12 @@ void qmp_inject_nmi(Error **errp)
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
CPUX86State *env = &cpu->env;
|
||||
|
||||
if (!cpu->apic_state) {
|
||||
if (!env->apic_state) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_NMI);
|
||||
} else {
|
||||
apic_deliver_nmi(cpu->apic_state);
|
||||
apic_deliver_nmi(env->apic_state);
|
||||
}
|
||||
}
|
||||
#elif defined(TARGET_S390X)
|
||||
|
||||
@@ -22,13 +22,10 @@
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#include "exec/cputlb.h"
|
||||
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
//#define DEBUG_TLB
|
||||
//#define DEBUG_TLB_CHECK
|
||||
@@ -36,6 +33,13 @@
|
||||
/* statistics */
|
||||
int tlb_flush_count;
|
||||
|
||||
static const CPUTLBEntry s_cputlb_empty_entry = {
|
||||
.addr_read = -1,
|
||||
.addr_write = -1,
|
||||
.addr_code = -1,
|
||||
.addend = -1,
|
||||
};
|
||||
|
||||
/* NOTE:
|
||||
* If flush_global is true (the usual case), flush all tlb entries.
|
||||
* If flush_global is false, flush (at least) all tlb entries not
|
||||
@@ -48,9 +52,10 @@ int tlb_flush_count;
|
||||
* entries from the TLB at any time, so flushing more entries than
|
||||
* required is only an efficiency issue, not a correctness issue.
|
||||
*/
|
||||
void tlb_flush(CPUState *cpu, int flush_global)
|
||||
void tlb_flush(CPUArchState *env, int flush_global)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int i;
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf("tlb_flush:\n");
|
||||
@@ -59,8 +64,15 @@ void tlb_flush(CPUState *cpu, int flush_global)
|
||||
links while we are modifying them */
|
||||
cpu->current_tb = NULL;
|
||||
|
||||
memset(env->tlb_table, -1, sizeof(env->tlb_table));
|
||||
memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache));
|
||||
for (i = 0; i < CPU_TLB_SIZE; i++) {
|
||||
int mmu_idx;
|
||||
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
env->tlb_table[mmu_idx][i] = s_cputlb_empty_entry;
|
||||
}
|
||||
}
|
||||
|
||||
memset(env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
|
||||
|
||||
env->tlb_flush_addr = -1;
|
||||
env->tlb_flush_mask = 0;
|
||||
@@ -75,13 +87,13 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
|
||||
(TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
|
||||
addr == (tlb_entry->addr_code &
|
||||
(TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
memset(tlb_entry, -1, sizeof(*tlb_entry));
|
||||
*tlb_entry = s_cputlb_empty_entry;
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
void tlb_flush_page(CPUArchState *env, target_ulong addr)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
@@ -95,7 +107,7 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
env->tlb_flush_addr, env->tlb_flush_mask);
|
||||
#endif
|
||||
tlb_flush(cpu, 1);
|
||||
tlb_flush(env, 1);
|
||||
return;
|
||||
}
|
||||
/* must reset current TB so that interrupts cannot modify the
|
||||
@@ -108,23 +120,24 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
|
||||
}
|
||||
|
||||
tb_flush_jmp_cache(cpu, addr);
|
||||
tb_flush_jmp_cache(env, addr);
|
||||
}
|
||||
|
||||
/* update the TLBs so that writes to code in the virtual page 'addr'
|
||||
can be detected */
|
||||
void tlb_protect_code(ram_addr_t ram_addr)
|
||||
{
|
||||
cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_CODE);
|
||||
cpu_physical_memory_reset_dirty(ram_addr,
|
||||
ram_addr + TARGET_PAGE_SIZE,
|
||||
CODE_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
/* update the TLB so that writes in physical page 'phys_addr' are no longer
|
||||
tested for self modifying code */
|
||||
void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr,
|
||||
void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr,
|
||||
target_ulong vaddr)
|
||||
{
|
||||
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
|
||||
cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe)
|
||||
@@ -223,11 +236,10 @@ static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr,
|
||||
/* Add a new TLB entry. At most one entry for a given virtual address
|
||||
is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
|
||||
supplied size is only used by tlb_flush_page. */
|
||||
void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
||||
hwaddr paddr, int prot,
|
||||
int mmu_idx, target_ulong size)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
MemoryRegionSection *section;
|
||||
unsigned int index;
|
||||
target_ulong address;
|
||||
@@ -242,7 +254,7 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
}
|
||||
|
||||
sz = size;
|
||||
section = address_space_translate_for_iotlb(cpu->as, paddr,
|
||||
section = address_space_translate_for_iotlb(&address_space_memory, paddr,
|
||||
&xlat, &sz);
|
||||
assert(sz >= TARGET_PAGE_SIZE);
|
||||
|
||||
@@ -263,7 +275,7 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
}
|
||||
|
||||
code_address = address;
|
||||
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat,
|
||||
iotlb = memory_region_section_get_iotlb(env, section, vaddr, paddr, xlat,
|
||||
prot, &address);
|
||||
|
||||
index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
@@ -287,8 +299,7 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
/* Write access calls the I/O callback. */
|
||||
te->addr_write = address | TLB_MMIO;
|
||||
} else if (memory_region_is_ram(section->mr)
|
||||
&& cpu_physical_memory_is_clean(section->mr->ram_addr
|
||||
+ xlat)) {
|
||||
&& !cpu_physical_memory_is_dirty(section->mr->ram_addr + xlat)) {
|
||||
te->addr_write = address | TLB_NOTDIRTY;
|
||||
} else {
|
||||
te->addr_write = address;
|
||||
@@ -308,7 +319,6 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
int mmu_idx, page_index, pd;
|
||||
void *p;
|
||||
MemoryRegion *mr;
|
||||
CPUState *cpu = ENV_GET_CPU(env1);
|
||||
|
||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = cpu_mmu_index(env1);
|
||||
@@ -317,14 +327,15 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
cpu_ldub_code(env1, addr);
|
||||
}
|
||||
pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK;
|
||||
mr = iotlb_to_region(cpu->as, pd);
|
||||
mr = iotlb_to_region(pd);
|
||||
if (memory_region_is_unassigned(mr)) {
|
||||
CPUState *cpu = ENV_GET_CPU(env1);
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
if (cc->do_unassigned_access) {
|
||||
cc->do_unassigned_access(cpu, addr, false, true, 0, 4);
|
||||
} else {
|
||||
cpu_abort(cpu, "Trying to execute code outside RAM or ROM at 0x"
|
||||
cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x"
|
||||
TARGET_FMT_lx "\n", addr);
|
||||
}
|
||||
}
|
||||
@@ -332,21 +343,6 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
return qemu_ram_addr_from_host_nofail(p);
|
||||
}
|
||||
|
||||
#define MMUSUFFIX _mmu
|
||||
|
||||
#define SHIFT 0
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 1
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 2
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 3
|
||||
#include "softmmu_template.h"
|
||||
#undef MMUSUFFIX
|
||||
|
||||
#define MMUSUFFIX _cmmu
|
||||
#undef GETPC_ADJ
|
||||
#define GETPC_ADJ 0
|
||||
@@ -355,13 +351,15 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
#define SOFTMMU_CODE_ACCESS
|
||||
|
||||
#define SHIFT 0
|
||||
#include "softmmu_template.h"
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#define SHIFT 1
|
||||
#include "softmmu_template.h"
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#define SHIFT 2
|
||||
#include "softmmu_template.h"
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#define SHIFT 3
|
||||
#include "softmmu_template.h"
|
||||
#include "exec/softmmu_template.h"
|
||||
|
||||
#undef env
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Default configuration for aarch64-linux-user
|
||||
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
@@ -1,6 +0,0 @@
|
||||
# Default configuration for aarch64-softmmu
|
||||
|
||||
# We support all the 32 bit boards so need all their config
|
||||
include arm-softmmu.mak
|
||||
|
||||
# Currently no 64-bit specific config requirements
|
||||
@@ -27,7 +27,6 @@ CONFIG_SSI_SD=y
|
||||
CONFIG_SSI_M25P80=y
|
||||
CONFIG_LAN9118=y
|
||||
CONFIG_SMC91C111=y
|
||||
CONFIG_ALLWINNER_EMAC=y
|
||||
CONFIG_DS1338=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
@@ -42,7 +41,6 @@ CONFIG_ARM_GIC=y
|
||||
CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
|
||||
CONFIG_ARM_TIMER=y
|
||||
CONFIG_ARM_MPTIMER=y
|
||||
CONFIG_A9_GTIMER=y
|
||||
CONFIG_PL011=y
|
||||
CONFIG_PL022=y
|
||||
CONFIG_PL031=y
|
||||
@@ -65,7 +63,6 @@ CONFIG_XILINX_SPIPS=y
|
||||
|
||||
CONFIG_ARM11SCU=y
|
||||
CONFIG_A9SCU=y
|
||||
CONFIG_DIGIC=y
|
||||
CONFIG_MARVELL_88W8618=y
|
||||
CONFIG_OMAP=y
|
||||
CONFIG_TSC210X=y
|
||||
@@ -84,7 +81,3 @@ CONFIG_VERSATILE_I2C=y
|
||||
|
||||
CONFIG_SDHCI=y
|
||||
CONFIG_INTEGRATOR_DEBUG=y
|
||||
|
||||
CONFIG_ALLWINNER_A10_PIT=y
|
||||
CONFIG_ALLWINNER_A10_PIC=y
|
||||
CONFIG_ALLWINNER_A10=y
|
||||
|
||||
@@ -44,4 +44,3 @@ CONFIG_APIC=y
|
||||
CONFIG_IOAPIC=y
|
||||
CONFIG_ICC_BUS=y
|
||||
CONFIG_PVPANIC=y
|
||||
CONFIG_MEM_HOTPLUG=y
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
# Default configuration for ppc-linux-user
|
||||
CONFIG_LIBDECNUMBER=y
|
||||
|
||||
@@ -41,12 +41,8 @@ CONFIG_I8259=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_XILINX_ETHLITE=y
|
||||
CONFIG_OPENPIC=y
|
||||
CONFIG_PREP=y
|
||||
CONFIG_MAC=y
|
||||
CONFIG_E500=y
|
||||
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
||||
# For PReP
|
||||
CONFIG_MC146818RTC=y
|
||||
CONFIG_ETSEC=y
|
||||
CONFIG_ISA_TESTDEV=y
|
||||
CONFIG_LIBDECNUMBER=y
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user