Compare commits
60
Commits
v2.5.1
..
stable-2.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32d24131b2 | ||
|
|
fc63922556 | ||
|
|
36e1eee760 | ||
|
|
9137bd24c8 | ||
|
|
08231cbb76 | ||
|
|
70a4483abb | ||
|
|
39809852a7 | ||
|
|
db97d9d886 | ||
|
|
243b80c9c5 | ||
|
|
91232d98da | ||
|
|
d68ba3cab3 | ||
|
|
381a290266 | ||
|
|
696317f189 | ||
|
|
c2a550d3df | ||
|
|
a64d4cafa9 | ||
|
|
2f99c80963 | ||
|
|
f62c10bd20 | ||
|
|
8c4fa92d01 | ||
|
|
7c22dcdeb8 | ||
|
|
16514367ef | ||
|
|
55b4efb034 | ||
|
|
2874c6565e | ||
|
|
2d0583fc79 | ||
|
|
40161bf27b | ||
|
|
2935ae915a | ||
|
|
2f3c310818 | ||
|
|
1f21d3b8dc | ||
|
|
bac9ce97d3 | ||
|
|
33fca8589c | ||
|
|
a479b21c11 | ||
|
|
d11ff15fd5 | ||
|
|
1b8e1f7ad9 | ||
|
|
e00bf9ee70 | ||
|
|
78aeb6984c | ||
|
|
6d62d0e3dd | ||
|
|
5644f6f924 | ||
|
|
63d761388d | ||
|
|
c13b1c8314 | ||
|
|
052677b2c8 | ||
|
|
0fdf9f756f | ||
|
|
d077545dfe | ||
|
|
f6737604da | ||
|
|
d2b0f96fe2 | ||
|
|
a00431853f | ||
|
|
b51715e1c0 | ||
|
|
267bc47438 | ||
|
|
955ff148de | ||
|
|
71b685832d | ||
|
|
9a20ccaecd | ||
|
|
d9af73191c | ||
|
|
637dd0bb7c | ||
|
|
2ac9fa162e | ||
|
|
5b7d840e74 | ||
|
|
0de7d2b793 | ||
|
|
f399ea092e | ||
|
|
83c92b4514 | ||
|
|
5a1ccdfe44 | ||
|
|
7aa2bcad0c | ||
|
|
3a56af1fbc | ||
|
|
efec4dcd25 |
No files matched your search
@@ -1,2 +0,0 @@
|
||||
((c-mode . ((c-file-style . "stroustrup")
|
||||
(indent-tabs-mode . nil))))
|
||||
+1
-5
@@ -19,13 +19,12 @@
|
||||
/trace/generated-ust.c
|
||||
/ui/shader/texture-blit-frag.h
|
||||
/ui/shader/texture-blit-vert.h
|
||||
/libcacard/trace/generated-tracers.c
|
||||
*-timestamp
|
||||
/*-softmmu
|
||||
/*-darwin-user
|
||||
/*-linux-user
|
||||
/*-bsd-user
|
||||
/ivshmem-client
|
||||
/ivshmem-server
|
||||
/libdis*
|
||||
/libuser
|
||||
/linux-headers/asm
|
||||
@@ -35,7 +34,6 @@
|
||||
/qapi-visit.[ch]
|
||||
/qapi-event.[ch]
|
||||
/qmp-commands.h
|
||||
/qmp-introspect.[ch]
|
||||
/qmp-marshal.c
|
||||
/qemu-doc.html
|
||||
/qemu-tech.html
|
||||
@@ -51,7 +49,6 @@
|
||||
/qemu-ga
|
||||
/qemu-bridge-helper
|
||||
/qemu-monitor.texi
|
||||
/qemu-monitor-info.texi
|
||||
/qmp-commands.txt
|
||||
/vscclient
|
||||
/fsdev/virtfs-proxy-helper
|
||||
@@ -61,7 +58,6 @@
|
||||
*.cp
|
||||
*.dvi
|
||||
*.exe
|
||||
*.msi
|
||||
*.dll
|
||||
*.so
|
||||
*.mo
|
||||
|
||||
+9
-1
@@ -54,7 +54,15 @@ matrix:
|
||||
include:
|
||||
# Make check target (we only do this once)
|
||||
- env:
|
||||
- TARGETS=alpha-softmmu,arm-softmmu,aarch64-softmmu,cris-softmmu,i386-softmmu,x86_64-softmmu,m68k-softmmu,microblaze-softmmu,microblazeel-softmmu,mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu,or32-softmmu,ppc-softmmu,ppc64-softmmu,ppcemb-softmmu,s390x-softmmu,sh4-softmmu,sh4eb-softmmu,sparc-softmmu,sparc64-softmmu,unicore32-softmmu,unicore32-linux-user,lm32-softmmu,moxie-softmmu,tricore-softmmu,xtensa-softmmu,xtensaeb-softmmu
|
||||
- TARGETS=alpha-softmmu,arm-softmmu,aarch64-softmmu,cris-softmmu,
|
||||
i386-softmmu,x86_64-softmmu,m68k-softmmu,microblaze-softmmu,
|
||||
microblazeel-softmmu,mips-softmmu,mips64-softmmu,
|
||||
mips64el-softmmu,mipsel-softmmu,or32-softmmu,ppc-softmmu,
|
||||
ppc64-softmmu,ppcemb-softmmu,s390x-softmmu,sh4-softmmu,
|
||||
sh4eb-softmmu,sparc-softmmu,sparc64-softmmu,
|
||||
unicore32-softmmu,unicore32-linux-user,
|
||||
lm32-softmmu,moxie-softmmu,tricore-softmmu,xtensa-softmmu,
|
||||
xtensaeb-softmmu
|
||||
TEST_CMD="make check"
|
||||
compiler: gcc
|
||||
# Debug related options
|
||||
|
||||
+4
-9
@@ -87,15 +87,10 @@ Furthermore, it is the QEMU coding style.
|
||||
|
||||
5. Declarations
|
||||
|
||||
Mixed declarations (interleaving statements and declarations within
|
||||
blocks) are generally not allowed; declarations should be at the beginning
|
||||
of blocks.
|
||||
|
||||
Every now and then, an exception is made for declarations inside a
|
||||
#ifdef or #ifndef block: if the code looks nicer, such declarations can
|
||||
be placed at the top of the block even if there are statements above.
|
||||
On the other hand, however, it's often best to move that #ifdef/#ifndef
|
||||
block to a separate function altogether.
|
||||
Mixed declarations (interleaving statements and declarations within blocks)
|
||||
are not allowed; declarations should be at the beginning of blocks. In other
|
||||
words, the code should not generate warnings if using GCC's
|
||||
-Wdeclaration-after-statement option.
|
||||
|
||||
6. Conditional statements
|
||||
|
||||
|
||||
+36
-236
@@ -62,22 +62,14 @@ Guest CPU cores (TCG):
|
||||
----------------------
|
||||
Overall
|
||||
L: qemu-devel@nongnu.org
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
S: Odd fixes
|
||||
F: cpu-exec.c
|
||||
F: cpu-exec-common.c
|
||||
F: cpus.c
|
||||
F: cputlb.c
|
||||
F: exec.c
|
||||
F: softmmu_template.h
|
||||
F: translate-all.*
|
||||
F: translate-common.c
|
||||
F: include/exec/cpu*.h
|
||||
F: include/exec/exec-all.h
|
||||
F: translate-all.c
|
||||
F: include/exec/cpu_ldst.h
|
||||
F: include/exec/cpu_ldst_template.h
|
||||
F: include/exec/helper*.h
|
||||
F: include/exec/tb-hash.h
|
||||
|
||||
Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
@@ -85,18 +77,13 @@ S: Maintained
|
||||
F: target-alpha/
|
||||
F: hw/alpha/
|
||||
F: tests/tcg/alpha/
|
||||
F: disas/alpha.c
|
||||
|
||||
ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: target-arm/
|
||||
F: hw/arm/
|
||||
F: hw/cpu/a*mpcore.c
|
||||
F: disas/arm.c
|
||||
F: disas/arm-a64.cc
|
||||
F: disas/libvixl/
|
||||
|
||||
CRIS
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
@@ -104,7 +91,6 @@ S: Maintained
|
||||
F: target-cris/
|
||||
F: hw/cris/
|
||||
F: tests/tcg/cris/
|
||||
F: disas/cris.c
|
||||
|
||||
LM32
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
@@ -128,7 +114,6 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: target-microblaze/
|
||||
F: hw/microblaze/
|
||||
F: disas/microblaze.c
|
||||
|
||||
MIPS
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
@@ -137,13 +122,11 @@ S: Maintained
|
||||
F: target-mips/
|
||||
F: hw/mips/
|
||||
F: tests/tcg/mips/
|
||||
F: disas/mips.c
|
||||
|
||||
Moxie
|
||||
M: Anthony Green <green@moxielogic.com>
|
||||
S: Maintained
|
||||
F: target-moxie/
|
||||
F: disas/moxie.c
|
||||
|
||||
OpenRISC
|
||||
M: Jia Liu <proljc@gmail.com>
|
||||
@@ -158,7 +141,6 @@ L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: target-ppc/
|
||||
F: hw/ppc/
|
||||
F: disas/ppc.c
|
||||
|
||||
S390
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
@@ -166,14 +148,12 @@ M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target-s390x/
|
||||
F: hw/s390x/
|
||||
F: disas/s390.c
|
||||
|
||||
SH4
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Odd Fixes
|
||||
F: target-sh4/
|
||||
F: hw/sh4/
|
||||
F: disas/sh4.c
|
||||
|
||||
SPARC
|
||||
M: Blue Swirl <blauwirbel@gmail.com>
|
||||
@@ -182,7 +162,6 @@ S: Maintained
|
||||
F: target-sparc/
|
||||
F: hw/sparc/
|
||||
F: hw/sparc64/
|
||||
F: disas/sparc.c
|
||||
|
||||
UniCore32
|
||||
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
|
||||
@@ -197,7 +176,6 @@ M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
S: Maintained
|
||||
F: target-i386/
|
||||
F: hw/i386/
|
||||
F: disas/i386.c
|
||||
|
||||
Xtensa
|
||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||
@@ -225,7 +203,6 @@ F: */kvm.*
|
||||
|
||||
ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: target-arm/kvm.c
|
||||
|
||||
@@ -245,14 +222,9 @@ M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target-s390x/kvm.c
|
||||
F: target-s390x/ioinst.[ch]
|
||||
F: target-s390x/machine.c
|
||||
F: hw/intc/s390_flic.c
|
||||
F: hw/intc/s390_flic_kvm.c
|
||||
F: include/hw/s390x/s390_flic.h
|
||||
F: gdb-xml/s390*.xml
|
||||
T: git git://github.com/cohuck/qemu.git s390-next
|
||||
T: git git://github.com/borntraeger/qemu.git s390-next
|
||||
|
||||
X86
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
@@ -270,12 +242,6 @@ L: xen-devel@lists.xensource.com
|
||||
S: Supported
|
||||
F: xen-*
|
||||
F: */xen*
|
||||
F: hw/char/xen_console.c
|
||||
F: hw/display/xenfb.c
|
||||
F: hw/net/xen_nic.c
|
||||
F: hw/xen/
|
||||
F: hw/xenpv/
|
||||
F: include/hw/xen/
|
||||
|
||||
Hosts:
|
||||
------
|
||||
@@ -296,163 +262,103 @@ L: qemu-devel@nongnu.org
|
||||
M: Stefan Weil <sw@weilnetz.de>
|
||||
S: Maintained
|
||||
F: *win32*
|
||||
F: qemu.nsi
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
Allwinner-a10
|
||||
M: Beniamino Galvani <b.galvani@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
M: Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
S: Maintained
|
||||
F: hw/*/allwinner*
|
||||
F: include/hw/*/allwinner*
|
||||
F: hw/*/allwinner-a10*
|
||||
F: include/hw/*/allwinner-a10*
|
||||
F: hw/arm/cubieboard.c
|
||||
|
||||
ARM PrimeCell
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/char/pl011.c
|
||||
F: hw/display/pl110*
|
||||
F: hw/dma/pl080.c
|
||||
F: hw/dma/pl330.c
|
||||
F: hw/gpio/pl061.c
|
||||
F: hw/input/pl050.c
|
||||
F: hw/intc/pl190.c
|
||||
F: hw/sd/pl181.c
|
||||
F: hw/timer/pl031.c
|
||||
F: include/hw/arm/primecell.h
|
||||
|
||||
ARM cores
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/intc/arm*
|
||||
F: hw/intc/gic_internal.h
|
||||
F: hw/misc/a9scu.c
|
||||
F: hw/misc/arm11scu.c
|
||||
F: hw/timer/a9gtimer*
|
||||
F: hw/timer/arm_*
|
||||
F: include/hw/arm/arm.h
|
||||
F: include/hw/intc/arm*
|
||||
F: include/hw/misc/a9scu.h
|
||||
F: include/hw/misc/arm11scu.h
|
||||
F: include/hw/timer/a9gtimer.h
|
||||
F: include/hw/timer/arm_mptimer.h
|
||||
|
||||
Exynos
|
||||
M: Evgeny Voevodin <e.voevodin@samsung.com>
|
||||
M: Maksim Kozlov <m.kozlov@samsung.com>
|
||||
M: Igor Mitsyanko <i.mitsyanko@gmail.com>
|
||||
M: Dmitry Solodkiy <d.solodkiy@samsung.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/exynos*
|
||||
|
||||
Calxeda Highbank
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/highbank.c
|
||||
F: hw/net/xgmac.c
|
||||
|
||||
Canon DIGIC
|
||||
M: Antony Pavlov <antonynpavlov@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: include/hw/arm/digic.h
|
||||
F: hw/*/digic*
|
||||
|
||||
Gumstix
|
||||
L: qemu-devel@nongnu.org
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/arm/gumstix.c
|
||||
|
||||
i.MX31
|
||||
M: Peter Chubb <peter.chubb@nicta.com.au>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd fixes
|
||||
F: hw/*/imx*
|
||||
F: hw/arm/kzm.c
|
||||
|
||||
Integrator CP
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/integratorcp.c
|
||||
|
||||
Mainstone
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/arm/mainstone.c
|
||||
|
||||
Musicpal
|
||||
M: Jan Kiszka <jan.kiszka@web.de>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/musicpal.c
|
||||
|
||||
nSeries
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/nseries.c
|
||||
|
||||
Palm
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/palm.c
|
||||
|
||||
Real View
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/realview*
|
||||
F: hw/intc/realview_gic.c
|
||||
F: include/hw/intc/realview_gic.h
|
||||
|
||||
PXA2XX
|
||||
Spitz
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/mainstone.c
|
||||
F: hw/arm/spitz.c
|
||||
F: hw/arm/tosa.c
|
||||
F: hw/arm/z2.c
|
||||
F: hw/*/pxa2xx*
|
||||
|
||||
Stellaris
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/stellaris*
|
||||
|
||||
Versatile PB
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/versatile*
|
||||
|
||||
Xilinx Zynq
|
||||
M: Alistair Francis <alistair.francis@xilinx.com>
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
S: Maintained
|
||||
F: hw/arm/xilinx_zynq.c
|
||||
F: hw/misc/zynq_slcr.c
|
||||
F: hw/*/cadence_*
|
||||
F: hw/ssi/xilinx_spips.c
|
||||
|
||||
Xilinx ZynqMP
|
||||
M: Alistair Francis <alistair.francis@xilinx.com>
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/xlnx-zynqmp.c
|
||||
F: hw/arm/xlnx-ep108.c
|
||||
F: include/hw/arm/xlnx-zynqmp.h
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <zhaoshenglong@huawei.com>
|
||||
M: Shannon Zhao <shannon.zhao@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
F: include/hw/arm/virt-acpi-build.h
|
||||
@@ -499,7 +405,7 @@ S: Maintained
|
||||
F: hw/microblaze/petalogix_s3adsp1800_mmu.c
|
||||
|
||||
petalogix_ml605
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
S: Maintained
|
||||
F: hw/microblaze/petalogix_ml605_mmu.c
|
||||
|
||||
@@ -649,14 +555,14 @@ 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/char/sclp*.[hc]
|
||||
F: hw/s390x/
|
||||
X: hw/s390x/s390-virtio-bus.[ch]
|
||||
F: hw/s390x/s390-virtio-ccw.c
|
||||
F: hw/s390x/css.[hc]
|
||||
F: hw/s390x/sclp*.[hc]
|
||||
F: hw/s390x/ipl*.[hc]
|
||||
F: hw/s390x/*pci*.[hc]
|
||||
F: include/hw/s390x/
|
||||
F: pc-bios/s390-ccw/
|
||||
F: hw/watchdog/wdt_diag288.c
|
||||
T: git git://github.com/cohuck/qemu.git s390-next
|
||||
T: git git://github.com/borntraeger/qemu.git s390-next
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
UniCore32 Machines
|
||||
-------------
|
||||
@@ -685,25 +591,6 @@ F: hw/acpi/piix4.c
|
||||
F: hw/acpi/ich9.c
|
||||
F: include/hw/acpi/ich9.h
|
||||
F: include/hw/acpi/piix.h
|
||||
F: hw/misc/sga.c
|
||||
|
||||
PC Chipset
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Support
|
||||
F: hw/char/debugcon.c
|
||||
F: hw/char/parallel.c
|
||||
F: hw/char/serial*
|
||||
F: hw/dma/i8257*
|
||||
F: hw/i2c/pm_smbus.c
|
||||
F: hw/intc/apic*
|
||||
F: hw/intc/ioapic*
|
||||
F: hw/intc/i8259*
|
||||
F: hw/misc/debugexit.c
|
||||
F: hw/misc/pc-testdev.c
|
||||
F: hw/timer/hpet*
|
||||
F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
|
||||
|
||||
Xtensa Machines
|
||||
@@ -737,7 +624,6 @@ F: hw/block/cdrom.c
|
||||
F: hw/block/hd-geometry.c
|
||||
F: tests/ide-test.c
|
||||
F: tests/ahci-test.c
|
||||
F: tests/libqos/ahci*
|
||||
T: git git://github.com/jnsnow/qemu.git ide
|
||||
|
||||
Floppy
|
||||
@@ -746,7 +632,6 @@ L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: hw/block/fdc.c
|
||||
F: include/hw/block/fdc.h
|
||||
F: tests/fdc-test.c
|
||||
T: git git://github.com/jnsnow/qemu.git ide
|
||||
|
||||
OMAP
|
||||
@@ -754,29 +639,19 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: hw/*/omap*
|
||||
|
||||
IPack
|
||||
M: Alberto Garcia <berto@igalia.com>
|
||||
S: Odd Fixes
|
||||
F: hw/char/ipoctal232.c
|
||||
F: hw/ipack/
|
||||
|
||||
PCI
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/pci/*
|
||||
F: hw/misc/pci-testdev.c
|
||||
F: hw/pci/*
|
||||
F: hw/pci-bridge/*
|
||||
|
||||
ACPI/SMBIOS
|
||||
ACPI
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Igor Mammedov <imammedo@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/acpi/*
|
||||
F: include/hw/smbios/*
|
||||
F: hw/mem/*
|
||||
F: hw/acpi/*
|
||||
F: hw/smbios/*
|
||||
F: hw/i386/acpi-build.[hc]
|
||||
F: hw/i386/*dsl
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
@@ -795,19 +670,6 @@ M: Scott Wood <scottwood@freescale.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Supported
|
||||
F: hw/ppc/e500*
|
||||
F: hw/pci-host/ppce500.c
|
||||
F: hw/net/fsl_etsec/
|
||||
|
||||
Character devices
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: hw/char/
|
||||
|
||||
Network devices
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: hw/net/
|
||||
T: git git://github.com/jasowang/qemu.git net
|
||||
|
||||
SCSI
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
@@ -821,17 +683,10 @@ S: Orphan
|
||||
F: hw/scsi/lsi53c895a.c
|
||||
|
||||
SSI
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
S: Maintained
|
||||
F: hw/ssi/*
|
||||
F: hw/block/m25p80.c
|
||||
X: hw/ssi/xilinx_*
|
||||
|
||||
Xilinx SPI
|
||||
M: Alistair Francis <alistair.francis@xilinx.com>
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/ssi/xilinx_*
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
@@ -863,7 +718,6 @@ F: net/vhost-user.c
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
M: Greg Kurz <gkurz@linux.vnet.ibm.com>
|
||||
S: Supported
|
||||
F: hw/9pfs/
|
||||
F: fsdev/
|
||||
@@ -884,8 +738,7 @@ 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.git s390-next
|
||||
T: git git://github.com/borntraeger/qemu.git s390-next
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
virtio-input
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
@@ -922,9 +775,8 @@ F: hw/scsi/megasas.c
|
||||
F: hw/scsi/mfi.h
|
||||
|
||||
Xilinx EDK
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
M: Alistair Francis <alistair.francis@xilinx.com>
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/*/xilinx_*
|
||||
F: include/hw/xilinx.h
|
||||
@@ -995,7 +847,7 @@ F: block/qapi.c
|
||||
F: qapi/block*.json
|
||||
T: git git://repo.or.cz/qemu/armbru.git block-next
|
||||
|
||||
Character device backends
|
||||
Character Devices
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: qemu-char.c
|
||||
@@ -1026,19 +878,11 @@ F: include/hw/cpu/icc_bus.h
|
||||
F: hw/cpu/icc_bus.c
|
||||
|
||||
Device Tree
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: device_tree.[ch]
|
||||
|
||||
Error reporting
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
S: Supported
|
||||
F: include/qapi/error.h
|
||||
F: include/qemu/error-report.h
|
||||
F: util/error.c
|
||||
F: util/qemu-error.c
|
||||
|
||||
GDB stub
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Odd Fixes
|
||||
@@ -1051,7 +895,6 @@ S: Supported
|
||||
F: include/exec/ioport.h
|
||||
F: ioport.c
|
||||
F: include/exec/memory.h
|
||||
F: include/exec/ram_addr.h
|
||||
F: memory.c
|
||||
F: include/exec/memory-internal.h
|
||||
F: exec.c
|
||||
@@ -1091,11 +934,12 @@ F: hmp.c
|
||||
F: hmp-commands.hx
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
Network device backends
|
||||
Network device layer
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
T: git git://github.com/jasowang/qemu.git net
|
||||
T: git git://github.com/stefanha/qemu.git net
|
||||
|
||||
Netmap network backend
|
||||
M: Luigi Rizzo <rizzo@iet.unipi.it>
|
||||
@@ -1128,12 +972,7 @@ M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
S: Supported
|
||||
F: qapi/
|
||||
X: qapi/*.json
|
||||
F: include/qapi/
|
||||
X: include/qapi/qmp/
|
||||
F: include/qapi/qmp/dispatch.h
|
||||
F: tests/qapi-schema/
|
||||
F: tests/test-*-visitor.c
|
||||
F: tests/test-qmp-*.c
|
||||
F: scripts/qapi*
|
||||
F: docs/qapi*
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
@@ -1150,14 +989,6 @@ QObject
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
S: Maintained
|
||||
F: qobject/
|
||||
F: include/qapi/qmp/
|
||||
X: include/qapi/qmp/dispatch.h
|
||||
F: tests/check-qdict.c
|
||||
F: tests/check-qfloat.c
|
||||
F: tests/check-qint.c
|
||||
F: tests/check-qjson.c
|
||||
F: tests/check-qlist.c
|
||||
F: tests/check-qstring.c
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
|
||||
QEMU Guest Agent
|
||||
@@ -1174,8 +1005,6 @@ F: include/qom/
|
||||
X: include/qom/cpu.h
|
||||
F: qom/
|
||||
X: qom/cpu.c
|
||||
F: tests/check-qom-interface.c
|
||||
F: tests/check-qom-proplist.c
|
||||
F: tests/qom-test.c
|
||||
|
||||
QMP
|
||||
@@ -1184,7 +1013,7 @@ S: Supported
|
||||
F: qmp.c
|
||||
F: monitor.c
|
||||
F: qmp-commands.hx
|
||||
F: docs/*qmp-*
|
||||
F: docs/qmp/
|
||||
F: scripts/qmp/
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
@@ -1230,19 +1059,6 @@ F: crypto/
|
||||
F: include/crypto/
|
||||
F: tests/test-crypto-*
|
||||
|
||||
Coroutines
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
F: util/*coroutine*
|
||||
F: include/qemu/coroutine*
|
||||
F: tests/test-coroutine.c
|
||||
|
||||
Buffers
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Odd fixes
|
||||
F: util/buffer.c
|
||||
F: include/qemu/buffer.h
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
@@ -1272,61 +1088,54 @@ AArch64 target
|
||||
M: Claudio Fontana <claudio.fontana@huawei.com>
|
||||
M: Claudio Fontana <claudio.fontana@gmail.com>
|
||||
S: Maintained
|
||||
L: qemu-arm@nongnu.org
|
||||
F: tcg/aarch64/
|
||||
F: disas/arm-a64.cc
|
||||
F: disas/libvixl/
|
||||
|
||||
ARM target
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
S: Maintained
|
||||
L: qemu-arm@nongnu.org
|
||||
F: tcg/arm/
|
||||
F: disas/arm.c
|
||||
|
||||
i386 target
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Maintained
|
||||
F: tcg/i386/
|
||||
F: disas/i386.c
|
||||
|
||||
IA64 target
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Maintained
|
||||
F: tcg/ia64/
|
||||
F: disas/ia64.c
|
||||
|
||||
MIPS target
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Maintained
|
||||
F: tcg/mips/
|
||||
F: disas/mips.c
|
||||
|
||||
PPC
|
||||
M: Vassili Karpov (malc) <av1474@comtv.ru>
|
||||
S: Maintained
|
||||
F: tcg/ppc/
|
||||
F: disas/ppc.c
|
||||
|
||||
PPC64 target
|
||||
M: Vassili Karpov (malc) <av1474@comtv.ru>
|
||||
S: Maintained
|
||||
F: tcg/ppc64/
|
||||
|
||||
S390 target
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: tcg/s390/
|
||||
F: disas/s390.c
|
||||
|
||||
SPARC target
|
||||
M: Blue Swirl <blauwirbel@gmail.com>
|
||||
S: Maintained
|
||||
F: tcg/sparc/
|
||||
F: disas/sparc.c
|
||||
|
||||
TCI target
|
||||
M: Stefan Weil <sw@weilnetz.de>
|
||||
S: Maintained
|
||||
F: tcg/tci/
|
||||
F: tci.c
|
||||
F: disas/tci.c
|
||||
|
||||
Stable branches
|
||||
---------------
|
||||
@@ -1479,7 +1288,6 @@ F: block/dmg.c
|
||||
|
||||
parallels
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Denis V. Lunev <den@openvz.org>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/parallels.c
|
||||
@@ -1536,11 +1344,3 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: tests/image-fuzzer/
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Build system architecture
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: docs/build-system.txt
|
||||
@@ -52,8 +52,6 @@ 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-introspect.h
|
||||
GENERATED_SOURCES += qmp-introspect.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-events.h
|
||||
GENERATED_SOURCES += trace/generated-events.c
|
||||
@@ -90,8 +88,7 @@ LIBS+=-lz $(LIBS_TOOLS)
|
||||
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
||||
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
DOCS+=qmp-commands.txt
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt
|
||||
ifdef CONFIG_LINUX
|
||||
DOCS+=kvm_stat.1
|
||||
endif
|
||||
@@ -151,20 +148,18 @@ dummy := $(call unnest-vars,, \
|
||||
stub-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
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
|
||||
|
||||
@@ -177,7 +172,6 @@ SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||
|
||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||
|
||||
subdir-%:
|
||||
@@ -202,7 +196,7 @@ subdir-dtc:dtc/libfdt dtc/tests
|
||||
dtc/%:
|
||||
mkdir -p $@
|
||||
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y)
|
||||
|
||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
romsubdir-%:
|
||||
@@ -232,9 +226,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||
|
||||
@@ -269,7 +263,7 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
|
||||
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
||||
$(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
|
||||
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
|
||||
$(SRC_PATH)/qapi/event.json
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
@@ -291,11 +285,6 @@ $(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 $<, \
|
||||
" GEN $@")
|
||||
qmp-introspect.h qmp-introspect.c :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
|
||||
$(gen-out-type) -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)
|
||||
@@ -306,30 +295,24 @@ qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a
|
||||
ifdef QEMU_GA_MSI_ENABLED
|
||||
QEMU_GA_MSI=qemu-ga-$(ARCH).msi
|
||||
|
||||
msi: $(QEMU_GA_MSI)
|
||||
msi: ${QEMU_GA_MSI}
|
||||
|
||||
$(QEMU_GA_MSI): qemu-ga.exe $(QGA_VSS_PROVIDER)
|
||||
$(QEMU_GA_MSI): qemu-ga.exe
|
||||
|
||||
ifdef QEMU_GA_MSI_WITH_VSS
|
||||
$(QEMU_GA_MSI): qga/vss-win32/qga-vss.dll
|
||||
endif
|
||||
|
||||
$(QEMU_GA_MSI): config-host.mak
|
||||
|
||||
$(QEMU_GA_MSI): $(SRC_PATH)/qga/installer/qemu-ga.wxs
|
||||
$(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" BUILD_DIR="$(BUILD_DIR)" \
|
||||
$(QEMU_GA_MSI): qga/installer/qemu-ga.wxs
|
||||
$(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" \
|
||||
wixl -o $@ $(QEMU_GA_MSI_ARCH) $(QEMU_GA_MSI_WITH_VSS) $(QEMU_GA_MSI_MINGW_DLL_PATH) $<, " WIXL $@")
|
||||
else
|
||||
msi:
|
||||
@echo "MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)"
|
||||
@echo MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)
|
||||
endif
|
||||
|
||||
ifneq ($(EXESUF),)
|
||||
.PHONY: qemu-ga
|
||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||
endif
|
||||
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y)
|
||||
$(call LINK, $^)
|
||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
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
|
||||
@@ -361,7 +344,7 @@ qemu-%.tar.bz2:
|
||||
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
|
||||
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
|
||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
@@ -417,9 +400,6 @@ ifneq ($(TOOLS),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
endif
|
||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_VIRTFS
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
@@ -440,7 +420,7 @@ endif
|
||||
install: all $(if $(BUILD_DOCS),install-doc) \
|
||||
install-datadir install-localstatedir
|
||||
ifneq ($(TOOLS),)
|
||||
$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
|
||||
$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir))
|
||||
endif
|
||||
ifneq ($(CONFIG_MODULES),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
|
||||
@@ -528,16 +508,13 @@ qemu-options.texi: $(SRC_PATH)/qemu-options.hx
|
||||
qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
|
||||
|
||||
qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
|
||||
|
||||
qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@")
|
||||
|
||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
|
||||
|
||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
|
||||
$(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \
|
||||
@@ -561,12 +538,6 @@ qemu-nbd.8: qemu-nbd.texi
|
||||
$(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
|
||||
" GEN $@")
|
||||
|
||||
qemu-ga.8: qemu-ga.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga.pod && \
|
||||
$(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
|
||||
" GEN $@")
|
||||
|
||||
kvm_stat.1: scripts/kvm/kvm_stat.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \
|
||||
@@ -580,8 +551,7 @@ pdf: qemu-doc.pdf qemu-tech.pdf
|
||||
|
||||
qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi \
|
||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi
|
||||
qemu-monitor.texi qemu-img-cmds.texi
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
|
||||
@@ -631,7 +601,6 @@ endif # SIGNCODE
|
||||
$(if $(DLL_PATH),-DDLLDIR="$(DLL_PATH)") \
|
||||
-DSRCDIR="$(SRC_PATH)" \
|
||||
-DOUTFILE="$(INSTALLER)" \
|
||||
-DDISPLAYVERSION="$(VERSION)" \
|
||||
$(SRC_PATH)/qemu.nsi
|
||||
rm -r ${INSTDIR}
|
||||
ifdef SIGNCODE
|
||||
|
||||
+18
-18
@@ -1,8 +1,8 @@
|
||||
#######################################################################
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
||||
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
|
||||
util-obj-y += crypto/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
@@ -15,18 +15,24 @@ block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||
block-obj-y += block/
|
||||
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/
|
||||
|
||||
#######################################################################
|
||||
# crypto-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
crypto-obj-y = crypto/
|
||||
crypto-aes-obj-y = crypto/
|
||||
######################################################################
|
||||
# smartcard
|
||||
|
||||
#######################################################################
|
||||
# qom-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
qom-obj-y = qom/
|
||||
libcacard-y += libcacard/cac.o libcacard/event.o
|
||||
libcacard-y += libcacard/vcard.o libcacard/vreader.o
|
||||
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
|
||||
@@ -54,8 +60,6 @@ common-obj-y += audio/
|
||||
common-obj-y += hw/
|
||||
common-obj-y += accel.o
|
||||
|
||||
common-obj-y += replay/
|
||||
|
||||
common-obj-y += ui/
|
||||
common-obj-y += bt-host.o bt-vhci.o
|
||||
bt-host.o-cflags := $(BLUEZ_CFLAGS)
|
||||
@@ -71,13 +75,14 @@ common-obj-y += backends/
|
||||
|
||||
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
|
||||
|
||||
common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
|
||||
|
||||
common-obj-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
common-obj-y += qmp-marshal.o
|
||||
common-obj-y += qmp-introspect.o
|
||||
common-obj-y += qmp.o hmp.o
|
||||
endif
|
||||
|
||||
@@ -106,8 +111,3 @@ target-obj-y += trace/
|
||||
# by libqemuutil.a. These should be moved to a separate .json schema.
|
||||
qga-obj-y = qga/
|
||||
qga-vss-dll-obj-y = qga/
|
||||
|
||||
######################################################################
|
||||
# contrib
|
||||
ivshmem-client-obj-y = contrib/ivshmem-client/
|
||||
ivshmem-server-obj-y = contrib/ivshmem-server/
|
||||
+2
-14
@@ -7,7 +7,7 @@ include config-target.mak
|
||||
include config-devices.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
|
||||
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||
$(call set-vpath, $(SRC_PATH))
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
endif
|
||||
@@ -85,11 +85,8 @@ all: $(PROGS) stap
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
obj-y = exec.o translate-all.o cpu-exec.o
|
||||
obj-y += translate-common.o
|
||||
obj-y += cpu-exec-common.o
|
||||
obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += tci.o
|
||||
obj-y += tcg/tcg-common.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target-$(TARGET_BASE_ARCH)/
|
||||
@@ -154,7 +151,7 @@ else
|
||||
obj-y += hw/$(TARGET_BASE_ARCH)/
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS += hmp-commands.h hmp-commands-info.h qmp-commands-old.h
|
||||
GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
|
||||
@@ -173,18 +170,12 @@ target-obj-y-save := $(target-obj-y)
|
||||
dummy := $(call unnest-vars,.., \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
target-obj-y := $(target-obj-y-save)
|
||||
all-obj-y += $(common-obj-y)
|
||||
all-obj-y += $(target-obj-y)
|
||||
all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
|
||||
$(QEMU_PROG_BUILD): config-devices.mak
|
||||
|
||||
@@ -202,9 +193,6 @@ gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh
|
||||
hmp-commands.h: $(SRC_PATH)/hmp-commands.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
|
||||
hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
|
||||
qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
|
||||
|
||||
@@ -1,107 +1,3 @@
|
||||
QEMU README
|
||||
===========
|
||||
Read the documentation in qemu-doc.html or on http://wiki.qemu-project.org
|
||||
|
||||
QEMU is a generic and open source machine & userspace emulator and
|
||||
virtualizer.
|
||||
|
||||
QEMU is capable of emulating a complete machine in software without any
|
||||
need for hardware virtualization support. By using dynamic translation,
|
||||
it achieves very good performance. QEMU can also integrate with the Xen
|
||||
and KVM hypervisors to provide emulated hardware while allowing the
|
||||
hypervisor to manage the CPU. With hypervisor support, QEMU can achieve
|
||||
near native performance for CPUs. When QEMU emulates CPUs directly it is
|
||||
capable of running operating systems made for one machine (e.g. an ARMv7
|
||||
board) on a different machine (e.g. an x86_64 PC board).
|
||||
|
||||
QEMU is also capable of providing userspace API virtualization for Linux
|
||||
and BSD kernel interfaces. This allows binaries compiled against one
|
||||
architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a
|
||||
different architecture ABI (e.g. the Linux x86_64 ABI). This does not
|
||||
involve any hardware emulation, simply CPU and syscall emulation.
|
||||
|
||||
QEMU aims to fit into a variety of use cases. It can be invoked directly
|
||||
by users wishing to have full control over its behaviour and settings.
|
||||
It also aims to facilitate integration into higher level management
|
||||
layers, by providing a stable command line interface and monitor API.
|
||||
It is commonly invoked indirectly via the libvirt library when using
|
||||
open source applications such as oVirt, OpenStack and virt-manager.
|
||||
|
||||
QEMU as a whole is released under the GNU General Public License,
|
||||
version 2. For full licensing details, consult the LICENSE file.
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
QEMU is multi-platform software intended to be buildable on all modern
|
||||
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
|
||||
of other UNIX targets. The simple steps to build QEMU are:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
make
|
||||
|
||||
Complete details of the process for building and configuring QEMU for
|
||||
all supported host platforms can be found in the qemu-tech.html file.
|
||||
Additional information can also be found online via the QEMU website:
|
||||
|
||||
http://qemu-project.org/Hosts/Linux
|
||||
http://qemu-project.org/Hosts/W32
|
||||
|
||||
|
||||
Submitting patches
|
||||
==================
|
||||
|
||||
The QEMU source code is maintained under the GIT version control system.
|
||||
|
||||
git clone git://git.qemu-project.org/qemu.git
|
||||
|
||||
When submitting patches, the preferred approach is to use 'git
|
||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||
qemu-devel@nongnu.org mailing list. All patches submitted must contain
|
||||
a 'Signed-off-by' line from the author. Patches should follow the
|
||||
guidelines set out in the HACKING and CODING_STYLE files.
|
||||
|
||||
Additional information on submitting patches can be found online via
|
||||
the QEMU website
|
||||
|
||||
http://qemu-project.org/Contribute/SubmitAPatch
|
||||
http://qemu-project.org/Contribute/TrivialPatches
|
||||
|
||||
|
||||
Bug reporting
|
||||
=============
|
||||
|
||||
The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs
|
||||
found when running code built from QEMU git or upstream released sources
|
||||
should be reported via:
|
||||
|
||||
https://bugs.launchpad.net/qemu/
|
||||
|
||||
If using QEMU via an operating system vendor pre-built binary package, it
|
||||
is preferable to report bugs to the vendor's own bug tracker first. If
|
||||
the bug is also known to affect latest upstream code, it can also be
|
||||
reported via launchpad.
|
||||
|
||||
For additional information on bug reporting consult:
|
||||
|
||||
http://qemu-project.org/Contribute/ReportABug
|
||||
|
||||
|
||||
Contact
|
||||
=======
|
||||
|
||||
The QEMU community can be contacted in a number of ways, with the two
|
||||
main methods being email and IRC
|
||||
|
||||
- qemu-devel@nongnu.org
|
||||
http://lists.nongnu.org/mailman/listinfo/qemu-devel
|
||||
- #qemu on irc.oftc.net
|
||||
|
||||
Information on additional methods of contacting the community can be
|
||||
found online via the QEMU website:
|
||||
|
||||
http://qemu-project.org/Contribute/StartHere
|
||||
|
||||
-- End
|
||||
- QEMU team
|
||||
+4
-199
@@ -17,9 +17,6 @@
|
||||
#include "block/block.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/sockets.h"
|
||||
#ifdef CONFIG_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
|
||||
struct AioHandler
|
||||
{
|
||||
@@ -28,166 +25,9 @@ struct AioHandler
|
||||
IOHandler *io_write;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
bool is_external;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_EPOLL
|
||||
|
||||
/* The fd number threashold to switch to epoll */
|
||||
#define EPOLL_ENABLE_THRESHOLD 64
|
||||
|
||||
static void aio_epoll_disable(AioContext *ctx)
|
||||
{
|
||||
ctx->epoll_available = false;
|
||||
if (!ctx->epoll_enabled) {
|
||||
return;
|
||||
}
|
||||
ctx->epoll_enabled = false;
|
||||
close(ctx->epollfd);
|
||||
}
|
||||
|
||||
static inline int epoll_events_from_pfd(int pfd_events)
|
||||
{
|
||||
return (pfd_events & G_IO_IN ? EPOLLIN : 0) |
|
||||
(pfd_events & G_IO_OUT ? EPOLLOUT : 0) |
|
||||
(pfd_events & G_IO_HUP ? EPOLLHUP : 0) |
|
||||
(pfd_events & G_IO_ERR ? EPOLLERR : 0);
|
||||
}
|
||||
|
||||
static bool aio_epoll_try_enable(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
struct epoll_event event;
|
||||
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
int r;
|
||||
if (node->deleted || !node->pfd.events) {
|
||||
continue;
|
||||
}
|
||||
event.events = epoll_events_from_pfd(node->pfd.events);
|
||||
event.data.ptr = node;
|
||||
r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event);
|
||||
if (r) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ctx->epoll_enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
|
||||
{
|
||||
struct epoll_event event;
|
||||
int r;
|
||||
|
||||
if (!ctx->epoll_enabled) {
|
||||
return;
|
||||
}
|
||||
if (!node->pfd.events) {
|
||||
r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, node->pfd.fd, &event);
|
||||
if (r) {
|
||||
aio_epoll_disable(ctx);
|
||||
}
|
||||
} else {
|
||||
event.data.ptr = node;
|
||||
event.events = epoll_events_from_pfd(node->pfd.events);
|
||||
if (is_new) {
|
||||
r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event);
|
||||
if (r) {
|
||||
aio_epoll_disable(ctx);
|
||||
}
|
||||
} else {
|
||||
r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, node->pfd.fd, &event);
|
||||
if (r) {
|
||||
aio_epoll_disable(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int aio_epoll(AioContext *ctx, GPollFD *pfds,
|
||||
unsigned npfd, int64_t timeout)
|
||||
{
|
||||
AioHandler *node;
|
||||
int i, ret = 0;
|
||||
struct epoll_event events[128];
|
||||
|
||||
assert(npfd == 1);
|
||||
assert(pfds[0].fd == ctx->epollfd);
|
||||
if (timeout > 0) {
|
||||
ret = qemu_poll_ns(pfds, npfd, timeout);
|
||||
}
|
||||
if (timeout <= 0 || ret > 0) {
|
||||
ret = epoll_wait(ctx->epollfd, events,
|
||||
sizeof(events) / sizeof(events[0]),
|
||||
timeout);
|
||||
if (ret <= 0) {
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < ret; i++) {
|
||||
int ev = events[i].events;
|
||||
node = events[i].data.ptr;
|
||||
node->pfd.revents = (ev & EPOLLIN ? G_IO_IN : 0) |
|
||||
(ev & EPOLLOUT ? G_IO_OUT : 0) |
|
||||
(ev & EPOLLHUP ? G_IO_HUP : 0) |
|
||||
(ev & EPOLLERR ? G_IO_ERR : 0);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool aio_epoll_enabled(AioContext *ctx)
|
||||
{
|
||||
/* Fall back to ppoll when external clients are disabled. */
|
||||
return !aio_external_disabled(ctx) && ctx->epoll_enabled;
|
||||
}
|
||||
|
||||
static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds,
|
||||
unsigned npfd, int64_t timeout)
|
||||
{
|
||||
if (!ctx->epoll_available) {
|
||||
return false;
|
||||
}
|
||||
if (aio_epoll_enabled(ctx)) {
|
||||
return true;
|
||||
}
|
||||
if (npfd >= EPOLL_ENABLE_THRESHOLD) {
|
||||
if (aio_epoll_try_enable(ctx)) {
|
||||
return true;
|
||||
} else {
|
||||
aio_epoll_disable(ctx);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
|
||||
{
|
||||
}
|
||||
|
||||
static int aio_epoll(AioContext *ctx, GPollFD *pfds,
|
||||
unsigned npfd, int64_t timeout)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static bool aio_epoll_enabled(AioContext *ctx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds,
|
||||
unsigned npfd, int64_t timeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static AioHandler *find_aio_handler(AioContext *ctx, int fd)
|
||||
{
|
||||
AioHandler *node;
|
||||
@@ -203,14 +43,11 @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd)
|
||||
|
||||
void aio_set_fd_handler(AioContext *ctx,
|
||||
int fd,
|
||||
bool is_external,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool is_new = false;
|
||||
bool deleted = false;
|
||||
|
||||
node = find_aio_handler(ctx, fd);
|
||||
|
||||
@@ -229,7 +66,7 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
deleted = true;
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -240,32 +77,25 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
|
||||
g_source_add_poll(&ctx->source, &node->pfd);
|
||||
is_new = true;
|
||||
}
|
||||
/* Update handler with latest information */
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
node->opaque = opaque;
|
||||
node->is_external = is_external;
|
||||
|
||||
node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0);
|
||||
node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0);
|
||||
}
|
||||
|
||||
aio_epoll_update(ctx, node, is_new);
|
||||
aio_notify(ctx);
|
||||
if (deleted) {
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *notifier,
|
||||
bool is_external,
|
||||
EventNotifierHandler *io_read)
|
||||
{
|
||||
aio_set_fd_handler(ctx, event_notifier_get_fd(notifier),
|
||||
is_external, (IOHandler *)io_read, NULL, notifier);
|
||||
(IOHandler *)io_read, NULL, notifier);
|
||||
}
|
||||
|
||||
bool aio_prepare(AioContext *ctx)
|
||||
@@ -427,9 +257,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
/* fill pollfds */
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (!node->deleted && node->pfd.events
|
||||
&& !aio_epoll_enabled(ctx)
|
||||
&& aio_node_check(ctx, node->is_external)) {
|
||||
if (!node->deleted && node->pfd.events) {
|
||||
add_pollfd(node);
|
||||
}
|
||||
}
|
||||
@@ -440,17 +268,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
if (timeout) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
|
||||
AioHandler epoll_handler;
|
||||
|
||||
epoll_handler.pfd.fd = ctx->epollfd;
|
||||
epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
|
||||
npfd = 0;
|
||||
add_pollfd(&epoll_handler);
|
||||
ret = aio_epoll(ctx, pollfds, npfd, timeout);
|
||||
} else {
|
||||
ret = qemu_poll_ns(pollfds, npfd, timeout);
|
||||
}
|
||||
ret = qemu_poll_ns((GPollFD *)pollfds, npfd, timeout);
|
||||
if (blocking) {
|
||||
atomic_sub(&ctx->notify_me, 2);
|
||||
}
|
||||
@@ -479,16 +297,3 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
void aio_context_setup(AioContext *ctx, Error **errp)
|
||||
{
|
||||
#ifdef CONFIG_EPOLL
|
||||
assert(!ctx->epollfd);
|
||||
ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (ctx->epollfd == -1) {
|
||||
ctx->epoll_available = false;
|
||||
} else {
|
||||
ctx->epoll_available = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+1
-11
@@ -28,13 +28,11 @@ struct AioHandler {
|
||||
GPollFD pfd;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
bool is_external;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
void aio_set_fd_handler(AioContext *ctx,
|
||||
int fd,
|
||||
bool is_external,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
@@ -88,7 +86,6 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
node->opaque = opaque;
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
node->is_external = is_external;
|
||||
|
||||
event = event_notifier_get_handle(&ctx->notifier);
|
||||
WSAEventSelect(node->pfd.fd, event,
|
||||
@@ -101,7 +98,6 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *e,
|
||||
bool is_external,
|
||||
EventNotifierHandler *io_notify)
|
||||
{
|
||||
AioHandler *node;
|
||||
@@ -137,7 +133,6 @@ void aio_set_event_notifier(AioContext *ctx,
|
||||
node->e = e;
|
||||
node->pfd.fd = (uintptr_t)event_notifier_get_handle(e);
|
||||
node->pfd.events = G_IO_IN;
|
||||
node->is_external = is_external;
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
|
||||
g_source_add_poll(&ctx->source, &node->pfd);
|
||||
@@ -309,8 +304,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
/* fill fd sets */
|
||||
count = 0;
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (!node->deleted && node->io_notify
|
||||
&& aio_node_check(ctx, node->is_external)) {
|
||||
if (!node->deleted && node->io_notify) {
|
||||
events[count++] = event_notifier_get_handle(node->e);
|
||||
}
|
||||
}
|
||||
@@ -369,7 +363,3 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
aio_context_release(ctx);
|
||||
return progress;
|
||||
}
|
||||
|
||||
void aio_context_setup(AioContext *ctx, Error **errp)
|
||||
{
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/audio.h"
|
||||
#include "hw/smbios/smbios.h"
|
||||
#include "hw/i386/smbios.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
@@ -59,11 +59,6 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
|
||||
return bh;
|
||||
}
|
||||
|
||||
void aio_bh_call(QEMUBH *bh)
|
||||
{
|
||||
bh->cb(bh->opaque);
|
||||
}
|
||||
|
||||
/* Multiple occurrences of aio_bh_poll cannot be called concurrently */
|
||||
int aio_bh_poll(AioContext *ctx)
|
||||
{
|
||||
@@ -89,7 +84,7 @@ int aio_bh_poll(AioContext *ctx)
|
||||
ret = 1;
|
||||
}
|
||||
bh->idle = 0;
|
||||
aio_bh_call(bh);
|
||||
bh->cb(bh->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +247,7 @@ aio_ctx_finalize(GSource *source)
|
||||
}
|
||||
qemu_mutex_unlock(&ctx->bh_lock);
|
||||
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, false, NULL);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, NULL);
|
||||
event_notifier_cleanup(&ctx->notifier);
|
||||
rfifolock_destroy(&ctx->lock);
|
||||
qemu_mutex_destroy(&ctx->bh_lock);
|
||||
@@ -325,22 +320,15 @@ AioContext *aio_context_new(Error **errp)
|
||||
{
|
||||
int ret;
|
||||
AioContext *ctx;
|
||||
Error *local_err = NULL;
|
||||
|
||||
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
|
||||
aio_context_setup(ctx, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
ret = event_notifier_init(&ctx->notifier, false);
|
||||
if (ret < 0) {
|
||||
g_source_destroy(&ctx->source);
|
||||
error_setg_errno(errp, -ret, "Failed to initialize event notifier");
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
g_source_set_can_recurse(&ctx->source, true);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
false,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_dummy_cb);
|
||||
ctx->thread_pool = NULL;
|
||||
@@ -351,9 +339,6 @@ AioContext *aio_context_new(Error **errp)
|
||||
ctx->notify_dummy_bh = aio_bh_new(ctx, notify_dummy_bh, NULL);
|
||||
|
||||
return ctx;
|
||||
fail:
|
||||
g_source_destroy(&ctx->source);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void aio_context_ref(AioContext *ctx)
|
||||
|
||||
+6
-13
@@ -303,7 +303,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
|
||||
return 0;
|
||||
cur++;
|
||||
}
|
||||
DPRINTF("Dropped %td bytes!\n", cur - buf);
|
||||
DPRINTF("Dropped %d bytes!\n", cur - buf);
|
||||
}
|
||||
|
||||
#define EAT(c) do {\
|
||||
@@ -561,10 +561,7 @@ static void baum_close(struct CharDriverState *chr)
|
||||
g_free(baum);
|
||||
}
|
||||
|
||||
static CharDriverState *chr_baum_init(const char *id,
|
||||
ChardevBackend *backend,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
CharDriverState *chr_baum_init(void)
|
||||
{
|
||||
BaumDriverState *baum;
|
||||
CharDriverState *chr;
|
||||
@@ -589,16 +586,14 @@ static CharDriverState *chr_baum_init(const char *id,
|
||||
|
||||
baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL);
|
||||
if (baum->brlapi_fd == -1) {
|
||||
error_setg(errp, "brlapi__openConnection: %s",
|
||||
brlapi_strerror(brlapi_error_location()));
|
||||
brlapi_perror("baum_init: brlapi_openConnection");
|
||||
goto fail_handle;
|
||||
}
|
||||
|
||||
baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
|
||||
|
||||
if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) {
|
||||
error_setg(errp, "brlapi__getDisplaySize: %s",
|
||||
brlapi_strerror(brlapi_error_location()));
|
||||
brlapi_perror("baum_init: brlapi_getDisplaySize");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -614,8 +609,7 @@ static CharDriverState *chr_baum_init(const char *id,
|
||||
tty = BRLAPI_TTY_DEFAULT;
|
||||
|
||||
if (brlapi__enterTtyMode(handle, tty, NULL) == -1) {
|
||||
error_setg(errp, "brlapi__enterTtyMode: %s",
|
||||
brlapi_strerror(brlapi_error_location()));
|
||||
brlapi_perror("baum_init: brlapi_enterTtyMode");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -635,8 +629,7 @@ fail_handle:
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
|
||||
chr_baum_init);
|
||||
register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -83,7 +83,9 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
g_free(fb->mem_path);
|
||||
if (fb->mem_path) {
|
||||
g_free(fb->mem_path);
|
||||
}
|
||||
fb->mem_path = g_strdup(str);
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -313,11 +313,9 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
assert(maxnode <= MAX_NODES);
|
||||
if (mbind(ptr, sz, backend->policy,
|
||||
maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) {
|
||||
if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) {
|
||||
error_setg_errno(errp, errno,
|
||||
"cannot bind memory to host NUMA nodes");
|
||||
return;
|
||||
}
|
||||
error_setg_errno(errp, errno,
|
||||
"cannot bind memory to host NUMA nodes");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* Preallocate memory after the NUMA policy has been instantiated.
|
||||
|
||||
+2
-6
@@ -63,10 +63,7 @@ static void msmouse_chr_close (struct CharDriverState *chr)
|
||||
g_free (chr);
|
||||
}
|
||||
|
||||
static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
||||
ChardevBackend *backend,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
CharDriverState *qemu_chr_open_msmouse(void)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
|
||||
@@ -82,8 +79,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
|
||||
qemu_chr_open_msmouse);
|
||||
register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
+4
-8
@@ -108,16 +108,13 @@ static void testdev_close(struct CharDriverState *chr)
|
||||
g_free(testdev);
|
||||
}
|
||||
|
||||
static CharDriverState *chr_testdev_init(const char *id,
|
||||
ChardevBackend *backend,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
CharDriverState *chr_testdev_init(void)
|
||||
{
|
||||
TestdevCharState *testdev;
|
||||
CharDriverState *chr;
|
||||
|
||||
testdev = g_new0(TestdevCharState, 1);
|
||||
testdev->chr = chr = g_new0(CharDriverState, 1);
|
||||
testdev = g_malloc0(sizeof(TestdevCharState));
|
||||
testdev->chr = chr = g_malloc0(sizeof(CharDriverState));
|
||||
|
||||
chr->opaque = testdev;
|
||||
chr->chr_write = testdev_write;
|
||||
@@ -128,8 +125,7 @@ static CharDriverState *chr_testdev_init(const char *id,
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
|
||||
chr_testdev_init);
|
||||
register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -36,17 +36,6 @@
|
||||
static QEMUBalloonEvent *balloon_event_fn;
|
||||
static QEMUBalloonStatus *balloon_stat_fn;
|
||||
static void *balloon_opaque;
|
||||
static bool balloon_inhibited;
|
||||
|
||||
bool qemu_balloon_is_inhibited(void)
|
||||
{
|
||||
return balloon_inhibited;
|
||||
}
|
||||
|
||||
void qemu_balloon_inhibit(bool state)
|
||||
{
|
||||
balloon_inhibited = state;
|
||||
}
|
||||
|
||||
static bool have_balloon(Error **errp)
|
||||
{
|
||||
|
||||
+8
-117
@@ -2,7 +2,6 @@
|
||||
* QEMU System Emulator block accounting
|
||||
*
|
||||
* Copyright (c) 2011 Christoph Hellwig
|
||||
* Copyright (c) 2015 Igalia, S.L.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,54 +25,6 @@
|
||||
#include "block/accounting.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
||||
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
||||
|
||||
void block_acct_init(BlockAcctStats *stats, bool account_invalid,
|
||||
bool account_failed)
|
||||
{
|
||||
stats->account_invalid = account_invalid;
|
||||
stats->account_failed = account_failed;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_cleanup(BlockAcctStats *stats)
|
||||
{
|
||||
BlockAcctTimedStats *s, *next;
|
||||
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
|
||||
g_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
unsigned i;
|
||||
|
||||
s = g_new0(BlockAcctTimedStats, 1);
|
||||
s->interval_length = interval_length;
|
||||
QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
timed_average_init(&s->latency[i], clock_type,
|
||||
(uint64_t) interval_length * NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
||||
BlockAcctTimedStats *s)
|
||||
{
|
||||
if (s == NULL) {
|
||||
return QSLIST_FIRST(&stats->intervals);
|
||||
} else {
|
||||
return QSLIST_NEXT(s, entries);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
int64_t bytes, enum BlockAcctType type)
|
||||
@@ -81,69 +32,26 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
cookie->bytes = bytes;
|
||||
cookie->start_time_ns = qemu_clock_get_ns(clock_type);
|
||||
cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
latency_ns = qtest_latency_ns;
|
||||
}
|
||||
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
stats->total_time_ns[cookie->type] +=
|
||||
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns;
|
||||
}
|
||||
|
||||
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
|
||||
void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num,
|
||||
unsigned int nb_sectors)
|
||||
{
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->failed_ops[cookie->type]++;
|
||||
|
||||
if (stats->account_failed) {
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
latency_ns = qtest_latency_ns;
|
||||
}
|
||||
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
|
||||
{
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
/* block_acct_done() and block_acct_failed() update
|
||||
* total_time_ns[], but this one does not. The reason is that
|
||||
* invalid requests are accounted during their submission,
|
||||
* therefore there's no actual I/O involved. */
|
||||
|
||||
stats->invalid_ops[type]++;
|
||||
|
||||
if (stats->account_invalid) {
|
||||
stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
|
||||
if (stats->wr_highest_sector < sector_num + nb_sectors - 1) {
|
||||
stats->wr_highest_sector = sector_num + nb_sectors - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,20 +61,3 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
stats->merged[type] += num_requests;
|
||||
}
|
||||
|
||||
int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
|
||||
{
|
||||
return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns;
|
||||
}
|
||||
|
||||
double block_acct_queue_depth(BlockAcctTimedStats *stats,
|
||||
enum BlockAcctType type)
|
||||
{
|
||||
uint64_t sum, elapsed;
|
||||
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
sum = timed_average_sum(&stats->latency[type], &elapsed);
|
||||
|
||||
return (double) sum / elapsed;
|
||||
}
|
||||
+25
-62
@@ -21,7 +21,6 @@
|
||||
#include "block/blockjob.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
#define BACKUP_CLUSTER_BITS 16
|
||||
#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS)
|
||||
@@ -90,8 +89,7 @@ static void cow_request_end(CowRequest *req)
|
||||
|
||||
static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
bool *error_is_read,
|
||||
bool is_write_notifier)
|
||||
bool *error_is_read)
|
||||
{
|
||||
BackupBlockJob *job = (BackupBlockJob *)bs->job;
|
||||
CowRequest cow_request;
|
||||
@@ -131,14 +129,8 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
|
||||
|
||||
if (is_write_notifier) {
|
||||
ret = bdrv_co_readv_no_serialising(bs,
|
||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
||||
n, &bounce_qiov);
|
||||
} else {
|
||||
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
|
||||
&bounce_qiov);
|
||||
}
|
||||
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
|
||||
&bounce_qiov);
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_read_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
@@ -198,7 +190,7 @@ static int coroutine_fn backup_before_write_notify(
|
||||
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, true);
|
||||
return backup_do_cow(req->bs, sector_num, nb_sectors, NULL);
|
||||
}
|
||||
|
||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -216,41 +208,7 @@ static void backup_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
|
||||
if (s->target->blk) {
|
||||
blk_iostatus_reset(s->target->blk);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDriverState *bs = job->common.bs;
|
||||
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
} else {
|
||||
/* Everything is fine, delete this bitmap and install the backup. */
|
||||
bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_commit(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_abort(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, -1);
|
||||
}
|
||||
bdrv_iostatus_reset(s->target);
|
||||
}
|
||||
|
||||
static const BlockJobDriver backup_job_driver = {
|
||||
@@ -258,8 +216,6 @@ static const BlockJobDriver backup_job_driver = {
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.set_speed = backup_set_speed,
|
||||
.iostatus_reset = backup_iostatus_reset,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
};
|
||||
|
||||
static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
@@ -347,8 +303,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
return ret;
|
||||
}
|
||||
ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
|
||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read,
|
||||
false);
|
||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
|
||||
if ((ret < 0) &&
|
||||
backup_error_action(job, error_is_read, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT) {
|
||||
@@ -397,10 +352,8 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
job->bitmap = hbitmap_alloc(end, 0);
|
||||
|
||||
bdrv_set_enable_write_cache(target, true);
|
||||
if (target->blk) {
|
||||
blk_set_on_error(target->blk, on_target_error, on_target_error);
|
||||
blk_iostatus_enable(target->blk);
|
||||
}
|
||||
bdrv_set_on_error(target, on_target_error, on_target_error);
|
||||
bdrv_iostatus_enable(target);
|
||||
|
||||
bdrv_add_before_write_notifier(bs, &before_write);
|
||||
|
||||
@@ -455,7 +408,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
}
|
||||
/* FULL sync mode we copy the whole drive. */
|
||||
ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
|
||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read, false);
|
||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
|
||||
if (ret < 0) {
|
||||
/* Depending on error action, fail now or retry cluster */
|
||||
BlockErrorAction action =
|
||||
@@ -475,11 +428,22 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
|
||||
if (job->sync_bitmap) {
|
||||
BdrvDirtyBitmap *bm;
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
} else {
|
||||
/* Everything is fine, delete this bitmap and install the backup. */
|
||||
bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
}
|
||||
}
|
||||
hbitmap_free(job->bitmap);
|
||||
|
||||
if (target->blk) {
|
||||
blk_iostatus_disable(target->blk);
|
||||
}
|
||||
bdrv_iostatus_disable(target);
|
||||
bdrv_op_unblock_all(target, job->common.blocker);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
@@ -493,7 +457,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
BlockJobTxn *txn, Error **errp)
|
||||
Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
|
||||
@@ -508,7 +472,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error");
|
||||
return;
|
||||
}
|
||||
@@ -575,7 +539,6 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
sync_bitmap : NULL;
|
||||
job->common.len = len;
|
||||
job->common.co = qemu_coroutine_create(backup_run);
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
qemu_coroutine_enter(job->common.co, job);
|
||||
return;
|
||||
|
||||
|
||||
+18
-25
@@ -30,7 +30,6 @@
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qint.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
@@ -427,11 +426,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Set initial state */
|
||||
s->state = 1;
|
||||
|
||||
/* Open the image file */
|
||||
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
|
||||
bs, &child_file, false, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
/* Open the backing file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
||||
bs, &child_file, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
@@ -450,7 +449,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
|
||||
fail_unref:
|
||||
bdrv_unref_child(bs, bs->file);
|
||||
bdrv_unref(bs->file);
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
@@ -511,8 +510,7 @@ static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors,
|
||||
cb, opaque);
|
||||
return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
@@ -534,8 +532,7 @@ static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors,
|
||||
cb, opaque);
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
@@ -554,7 +551,7 @@ static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_aio_flush(bs->file->bs, cb, opaque);
|
||||
return bdrv_aio_flush(bs->file, cb, opaque);
|
||||
}
|
||||
|
||||
|
||||
@@ -584,13 +581,9 @@ static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
remove_rule(rule);
|
||||
QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
|
||||
|
||||
if (!qtest_enabled()) {
|
||||
printf("blkdebug: Suspended request '%s'\n", r.tag);
|
||||
}
|
||||
printf("blkdebug: Suspended request '%s'\n", r.tag);
|
||||
qemu_coroutine_yield();
|
||||
if (!qtest_enabled()) {
|
||||
printf("blkdebug: Resuming request '%s'\n", r.tag);
|
||||
}
|
||||
printf("blkdebug: Resuming request '%s'\n", r.tag);
|
||||
|
||||
QLIST_REMOVE(&r, next);
|
||||
g_free(r.tag);
|
||||
@@ -723,12 +716,12 @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
|
||||
static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
return bdrv_getlength(bs->file);
|
||||
}
|
||||
|
||||
static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
return bdrv_truncate(bs->file->bs, offset);
|
||||
return bdrv_truncate(bs->file, offset);
|
||||
}
|
||||
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
@@ -748,24 +741,24 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
if (force_json && !bs->file->bs->full_open_options) {
|
||||
if (force_json && !bs->file->full_open_options) {
|
||||
/* The config file cannot be recreated, so creating a plain filename
|
||||
* is impossible */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force_json && bs->file->bs->exact_filename[0]) {
|
||||
if (!force_json && bs->file->exact_filename[0]) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkdebug:%s:%s",
|
||||
qdict_get_try_str(bs->options, "config") ?: "",
|
||||
bs->file->bs->exact_filename);
|
||||
bs->file->exact_filename);
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug")));
|
||||
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
|
||||
QINCREF(bs->file->full_open_options);
|
||||
qdict_put_obj(opts, "image", QOBJECT(bs->file->full_open_options));
|
||||
|
||||
for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image") &&
|
||||
|
||||
+31
-40
@@ -14,7 +14,7 @@
|
||||
#include "qapi/qmp/qstring.h"
|
||||
|
||||
typedef struct {
|
||||
BdrvChild *test_file;
|
||||
BlockDriverState *test_file;
|
||||
} BDRVBlkverifyState;
|
||||
|
||||
typedef struct BlkverifyAIOCB BlkverifyAIOCB;
|
||||
@@ -123,29 +123,26 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Open the raw file */
|
||||
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw",
|
||||
bs, &child_file, false, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
||||
"raw", bs, &child_file, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the test file */
|
||||
s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options,
|
||||
"test", bs, &child_format, false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
assert(s->test_file == NULL);
|
||||
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
||||
"test", bs, &child_format, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
s->test_file = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
if (ret < 0) {
|
||||
bdrv_unref_child(bs, bs->file);
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
@@ -154,7 +151,7 @@ static void blkverify_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_unref_child(bs, s->test_file);
|
||||
bdrv_unref(s->test_file);
|
||||
s->test_file = NULL;
|
||||
}
|
||||
|
||||
@@ -162,7 +159,7 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
return bdrv_getlength(s->test_file->bs);
|
||||
return bdrv_getlength(s->test_file);
|
||||
}
|
||||
|
||||
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||
@@ -241,13 +238,13 @@ static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
nb_sectors, cb, opaque);
|
||||
|
||||
acb->verify = blkverify_verify_readv;
|
||||
acb->buf = qemu_blockalign(bs->file->bs, qiov->size);
|
||||
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);
|
||||
|
||||
bdrv_aio_readv(s->test_file->bs, sector_num, qiov, nb_sectors,
|
||||
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
bdrv_aio_readv(bs->file->bs, sector_num, &acb->raw_qiov, nb_sectors,
|
||||
bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
return &acb->common;
|
||||
}
|
||||
@@ -260,9 +257,9 @@ static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs,
|
||||
BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
|
||||
nb_sectors, cb, opaque);
|
||||
|
||||
bdrv_aio_writev(s->test_file->bs, sector_num, qiov, nb_sectors,
|
||||
bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors,
|
||||
bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
return &acb->common;
|
||||
}
|
||||
@@ -274,7 +271,7 @@ static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs,
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
/* Only flush test file, the raw file is not important */
|
||||
return bdrv_aio_flush(s->test_file->bs, cb, opaque);
|
||||
return bdrv_aio_flush(s->test_file, cb, opaque);
|
||||
}
|
||||
|
||||
static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
@@ -282,13 +279,13 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
|
||||
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->bs, candidate);
|
||||
return bdrv_recurse_is_first_non_filter(s->test_file, candidate);
|
||||
}
|
||||
|
||||
/* Propagate AioContext changes to ->test_file */
|
||||
@@ -296,7 +293,7 @@ static void blkverify_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_detach_aio_context(s->test_file->bs);
|
||||
bdrv_detach_aio_context(s->test_file);
|
||||
}
|
||||
|
||||
static void blkverify_attach_aio_context(BlockDriverState *bs,
|
||||
@@ -304,38 +301,32 @@ static void blkverify_attach_aio_context(BlockDriverState *bs,
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_attach_aio_context(s->test_file->bs, new_context);
|
||||
bdrv_attach_aio_context(s->test_file, new_context);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
/* bs->file->bs has already been refreshed */
|
||||
bdrv_refresh_filename(s->test_file->bs);
|
||||
/* bs->file has already been refreshed */
|
||||
bdrv_refresh_filename(s->test_file);
|
||||
|
||||
if (bs->file->bs->full_open_options
|
||||
&& s->test_file->bs->full_open_options)
|
||||
{
|
||||
if (bs->file->full_open_options && s->test_file->full_open_options) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify")));
|
||||
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put_obj(opts, "raw", QOBJECT(bs->file->bs->full_open_options));
|
||||
QINCREF(s->test_file->bs->full_open_options);
|
||||
qdict_put_obj(opts, "test",
|
||||
QOBJECT(s->test_file->bs->full_open_options));
|
||||
QINCREF(bs->file->full_open_options);
|
||||
qdict_put_obj(opts, "raw", QOBJECT(bs->file->full_open_options));
|
||||
QINCREF(s->test_file->full_open_options);
|
||||
qdict_put_obj(opts, "test", QOBJECT(s->test_file->full_open_options));
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
if (bs->file->bs->exact_filename[0]
|
||||
&& s->test_file->bs->exact_filename[0])
|
||||
{
|
||||
if (bs->file->exact_filename[0] && s->test_file->exact_filename[0]) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkverify:%s:%s",
|
||||
bs->file->bs->exact_filename,
|
||||
s->test_file->bs->exact_filename);
|
||||
bs->file->exact_filename, s->test_file->exact_filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-414
@@ -12,17 +12,12 @@
|
||||
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
/* Number of coroutines to reserve per attached device model */
|
||||
#define COROUTINE_POOL_RESERVATION 64
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
|
||||
|
||||
struct BlockBackend {
|
||||
char *name;
|
||||
int refcnt;
|
||||
@@ -34,31 +29,15 @@ struct BlockBackend {
|
||||
/* TODO change to DeviceState when all users are qdevified */
|
||||
const BlockDevOps *dev_ops;
|
||||
void *dev_opaque;
|
||||
|
||||
/* the block size for which the guest device expects atomicity */
|
||||
int guest_block_size;
|
||||
|
||||
/* If the BDS tree is removed, some of its options are stored here (which
|
||||
* can be used to restore those options in the new BDS on insert) */
|
||||
BlockBackendRootState root_state;
|
||||
|
||||
/* I/O stats (display with "info blockstats"). */
|
||||
BlockAcctStats stats;
|
||||
|
||||
BlockdevOnError on_read_error, on_write_error;
|
||||
bool iostatus_enabled;
|
||||
BlockDeviceIoStatus iostatus;
|
||||
};
|
||||
|
||||
typedef struct BlockBackendAIOCB {
|
||||
BlockAIOCB common;
|
||||
QEMUBH *bh;
|
||||
BlockBackend *blk;
|
||||
int ret;
|
||||
} BlockBackendAIOCB;
|
||||
|
||||
static const AIOCBInfo block_backend_aiocb_info = {
|
||||
.get_aio_context = blk_aiocb_get_aio_context,
|
||||
.aiocb_size = sizeof(BlockBackendAIOCB),
|
||||
};
|
||||
|
||||
@@ -147,7 +126,7 @@ BlockBackend *blk_new_open(const char *name, const char *filename,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = bdrv_open(&blk->bs, filename, reference, options, flags, errp);
|
||||
ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp);
|
||||
if (ret < 0) {
|
||||
blk_unref(blk);
|
||||
return NULL;
|
||||
@@ -166,17 +145,12 @@ static void blk_delete(BlockBackend *blk)
|
||||
bdrv_unref(blk->bs);
|
||||
blk->bs = NULL;
|
||||
}
|
||||
if (blk->root_state.throttle_state) {
|
||||
g_free(blk->root_state.throttle_group);
|
||||
throttle_group_unref(blk->root_state.throttle_state);
|
||||
}
|
||||
/* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */
|
||||
if (blk->name[0]) {
|
||||
QTAILQ_REMOVE(&blk_backends, blk, link);
|
||||
}
|
||||
g_free(blk->name);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
g_free(blk);
|
||||
}
|
||||
|
||||
@@ -190,11 +164,6 @@ static void drive_info_del(DriveInfo *dinfo)
|
||||
g_free(dinfo);
|
||||
}
|
||||
|
||||
int blk_get_refcnt(BlockBackend *blk)
|
||||
{
|
||||
return blk ? blk->refcnt : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment @blk's reference count.
|
||||
* @blk must not be null.
|
||||
@@ -269,23 +238,6 @@ BlockDriverState *blk_bs(BlockBackend *blk)
|
||||
return blk->bs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Changes the BlockDriverState attached to @blk
|
||||
*/
|
||||
void blk_set_bs(BlockBackend *blk, BlockDriverState *bs)
|
||||
{
|
||||
bdrv_ref(bs);
|
||||
|
||||
if (blk->bs) {
|
||||
blk->bs->blk = NULL;
|
||||
bdrv_unref(blk->bs);
|
||||
}
|
||||
assert(bs->blk == NULL);
|
||||
|
||||
blk->bs = bs;
|
||||
bs->blk = blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return @blk's DriveInfo if any, else null.
|
||||
*/
|
||||
@@ -339,29 +291,6 @@ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disassociates the currently associated BlockDriverState from @blk.
|
||||
*/
|
||||
void blk_remove_bs(BlockBackend *blk)
|
||||
{
|
||||
blk_update_root_state(blk);
|
||||
|
||||
blk->bs->blk = NULL;
|
||||
bdrv_unref(blk->bs);
|
||||
blk->bs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Associates a new BlockDriverState with @blk.
|
||||
*/
|
||||
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
|
||||
{
|
||||
assert(!blk->bs && !bs->blk);
|
||||
bdrv_ref(bs);
|
||||
blk->bs = bs;
|
||||
bs->blk = blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach device model @dev to @blk.
|
||||
* Return 0 on success, -EBUSY when a device model is attached already.
|
||||
@@ -374,7 +303,7 @@ int blk_attach_dev(BlockBackend *blk, void *dev)
|
||||
}
|
||||
blk_ref(blk);
|
||||
blk->dev = dev;
|
||||
blk_iostatus_reset(blk);
|
||||
bdrv_iostatus_reset(blk->bs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -401,7 +330,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev)
|
||||
blk->dev = NULL;
|
||||
blk->dev_ops = NULL;
|
||||
blk->dev_opaque = NULL;
|
||||
blk->guest_block_size = 512;
|
||||
bdrv_set_guest_block_size(blk->bs, 512);
|
||||
blk_unref(blk);
|
||||
}
|
||||
|
||||
@@ -435,15 +364,18 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
|
||||
void blk_dev_change_media_cb(BlockBackend *blk, bool load)
|
||||
{
|
||||
if (blk->dev_ops && blk->dev_ops->change_media_cb) {
|
||||
bool tray_was_open, tray_is_open;
|
||||
bool tray_was_closed = !blk_dev_is_tray_open(blk);
|
||||
|
||||
tray_was_open = blk_dev_is_tray_open(blk);
|
||||
blk->dev_ops->change_media_cb(blk->dev_opaque, load);
|
||||
tray_is_open = blk_dev_is_tray_open(blk);
|
||||
|
||||
if (tray_was_open != tray_is_open) {
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), tray_is_open,
|
||||
&error_abort);
|
||||
if (tray_was_closed) {
|
||||
/* tray open */
|
||||
qapi_event_send_device_tray_moved(blk_name(blk),
|
||||
true, &error_abort);
|
||||
}
|
||||
if (load) {
|
||||
/* tray close */
|
||||
qapi_event_send_device_tray_moved(blk_name(blk),
|
||||
false, &error_abort);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,14 +389,6 @@ bool blk_dev_has_removable_media(BlockBackend *blk)
|
||||
return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does @blk's attached device model have a tray?
|
||||
*/
|
||||
bool blk_dev_has_tray(BlockBackend *blk)
|
||||
{
|
||||
return blk->dev_ops && blk->dev_ops->is_tray_open;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify @blk's attached device model of a media eject request.
|
||||
* If @force is true, the medium is about to be yanked out forcefully.
|
||||
@@ -481,7 +405,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force)
|
||||
*/
|
||||
bool blk_dev_is_tray_open(BlockBackend *blk)
|
||||
{
|
||||
if (blk_dev_has_tray(blk)) {
|
||||
if (blk->dev_ops && blk->dev_ops->is_tray_open) {
|
||||
return blk->dev_ops->is_tray_open(blk->dev_opaque);
|
||||
}
|
||||
return false;
|
||||
@@ -511,47 +435,7 @@ void blk_dev_resize_cb(BlockBackend *blk)
|
||||
|
||||
void blk_iostatus_enable(BlockBackend *blk)
|
||||
{
|
||||
blk->iostatus_enabled = true;
|
||||
blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
||||
}
|
||||
|
||||
/* The I/O status is only enabled if the drive explicitly
|
||||
* enables it _and_ the VM is configured to stop on errors */
|
||||
bool blk_iostatus_is_enabled(const BlockBackend *blk)
|
||||
{
|
||||
return (blk->iostatus_enabled &&
|
||||
(blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC ||
|
||||
blk->on_write_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
blk->on_read_error == BLOCKDEV_ON_ERROR_STOP));
|
||||
}
|
||||
|
||||
BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk)
|
||||
{
|
||||
return blk->iostatus;
|
||||
}
|
||||
|
||||
void blk_iostatus_disable(BlockBackend *blk)
|
||||
{
|
||||
blk->iostatus_enabled = false;
|
||||
}
|
||||
|
||||
void blk_iostatus_reset(BlockBackend *blk)
|
||||
{
|
||||
if (blk_iostatus_is_enabled(blk)) {
|
||||
blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
||||
if (blk->bs && blk->bs->job) {
|
||||
block_job_iostatus_reset(blk->bs->job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blk_iostatus_set_err(BlockBackend *blk, int error)
|
||||
{
|
||||
assert(blk_iostatus_is_enabled(blk));
|
||||
if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||
blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE :
|
||||
BLOCK_DEVICE_IO_STATUS_FAILED;
|
||||
}
|
||||
bdrv_iostatus_enable(blk->bs);
|
||||
}
|
||||
|
||||
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
@@ -563,7 +447,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!blk_is_available(blk)) {
|
||||
if (!blk_is_inserted(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
@@ -650,15 +534,13 @@ static void error_callback_bh(void *opaque)
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, int ret)
|
||||
static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb,
|
||||
void *opaque, int ret)
|
||||
{
|
||||
struct BlockBackendAIOCB *acb;
|
||||
QEMUBH *bh;
|
||||
|
||||
acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque);
|
||||
acb->blk = blk;
|
||||
acb->ret = ret;
|
||||
|
||||
bh = aio_bh_new(blk_get_aio_context(blk), error_callback_bh, acb);
|
||||
@@ -674,7 +556,7 @@ BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num,
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return blk_abort_aio_request(blk, cb, opaque, ret);
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags,
|
||||
@@ -703,28 +585,16 @@ int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count)
|
||||
|
||||
int64_t blk_getlength(BlockBackend *blk)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_getlength(blk->bs);
|
||||
}
|
||||
|
||||
void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr)
|
||||
{
|
||||
if (!blk->bs) {
|
||||
*nb_sectors_ptr = 0;
|
||||
} else {
|
||||
bdrv_get_geometry(blk->bs, nb_sectors_ptr);
|
||||
}
|
||||
bdrv_get_geometry(blk->bs, nb_sectors_ptr);
|
||||
}
|
||||
|
||||
int64_t blk_nb_sectors(BlockBackend *blk)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_nb_sectors(blk->bs);
|
||||
}
|
||||
|
||||
@@ -734,7 +604,7 @@ BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return blk_abort_aio_request(blk, cb, opaque, ret);
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
|
||||
@@ -746,7 +616,7 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num,
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return blk_abort_aio_request(blk, cb, opaque, ret);
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
|
||||
@@ -755,10 +625,6 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num,
|
||||
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM);
|
||||
}
|
||||
|
||||
return bdrv_aio_flush(blk->bs, cb, opaque);
|
||||
}
|
||||
|
||||
@@ -768,7 +634,7 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk,
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return blk_abort_aio_request(blk, cb, opaque, ret);
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque);
|
||||
@@ -800,20 +666,12 @@ int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs)
|
||||
|
||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_ioctl(blk->bs, req, buf);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM);
|
||||
}
|
||||
|
||||
return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque);
|
||||
}
|
||||
|
||||
@@ -829,19 +687,11 @@ int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors)
|
||||
|
||||
int blk_co_flush(BlockBackend *blk)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_co_flush(blk->bs);
|
||||
}
|
||||
|
||||
int blk_flush(BlockBackend *blk)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_flush(blk->bs);
|
||||
}
|
||||
|
||||
@@ -852,9 +702,7 @@ int blk_flush_all(void)
|
||||
|
||||
void blk_drain(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_drain(blk->bs);
|
||||
}
|
||||
bdrv_drain(blk->bs);
|
||||
}
|
||||
|
||||
void blk_drain_all(void)
|
||||
@@ -862,178 +710,76 @@ void blk_drain_all(void)
|
||||
bdrv_drain_all();
|
||||
}
|
||||
|
||||
void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error,
|
||||
BlockdevOnError on_write_error)
|
||||
{
|
||||
blk->on_read_error = on_read_error;
|
||||
blk->on_write_error = on_write_error;
|
||||
}
|
||||
|
||||
BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read)
|
||||
{
|
||||
return is_read ? blk->on_read_error : blk->on_write_error;
|
||||
return bdrv_get_on_error(blk->bs, is_read);
|
||||
}
|
||||
|
||||
BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
|
||||
int error)
|
||||
{
|
||||
BlockdevOnError on_err = blk_get_on_error(blk, is_read);
|
||||
|
||||
switch (on_err) {
|
||||
case BLOCKDEV_ON_ERROR_ENOSPC:
|
||||
return (error == ENOSPC) ?
|
||||
BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT;
|
||||
case BLOCKDEV_ON_ERROR_STOP:
|
||||
return BLOCK_ERROR_ACTION_STOP;
|
||||
case BLOCKDEV_ON_ERROR_REPORT:
|
||||
return BLOCK_ERROR_ACTION_REPORT;
|
||||
case BLOCKDEV_ON_ERROR_IGNORE:
|
||||
return BLOCK_ERROR_ACTION_IGNORE;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return bdrv_get_error_action(blk->bs, is_read, error);
|
||||
}
|
||||
|
||||
static void send_qmp_error_event(BlockBackend *blk,
|
||||
BlockErrorAction action,
|
||||
bool is_read, int error)
|
||||
{
|
||||
IoOperationType optype;
|
||||
|
||||
optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
|
||||
qapi_event_send_block_io_error(blk_name(blk), optype, action,
|
||||
blk_iostatus_is_enabled(blk),
|
||||
error == ENOSPC, strerror(error),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
/* This is done by device models because, while the block layer knows
|
||||
* about the error, it does not know whether an operation comes from
|
||||
* the device or the block layer (from a job, for example).
|
||||
*/
|
||||
void blk_error_action(BlockBackend *blk, BlockErrorAction action,
|
||||
bool is_read, int error)
|
||||
{
|
||||
assert(error >= 0);
|
||||
|
||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||
/* First set the iostatus, so that "info block" returns an iostatus
|
||||
* that matches the events raised so far (an additional error iostatus
|
||||
* is fine, but not a lost one).
|
||||
*/
|
||||
blk_iostatus_set_err(blk, error);
|
||||
|
||||
/* Then raise the request to stop the VM and the event.
|
||||
* qemu_system_vmstop_request_prepare has two effects. First,
|
||||
* it ensures that the STOP event always comes after the
|
||||
* BLOCK_IO_ERROR event. Second, it ensures that even if management
|
||||
* can observe the STOP event and do a "cont" before the STOP
|
||||
* event is issued, the VM will not stop. In this case, vm_start()
|
||||
* also ensures that the STOP/RESUME pair of events is emitted.
|
||||
*/
|
||||
qemu_system_vmstop_request_prepare();
|
||||
send_qmp_error_event(blk, action, is_read, error);
|
||||
qemu_system_vmstop_request(RUN_STATE_IO_ERROR);
|
||||
} else {
|
||||
send_qmp_error_event(blk, action, is_read, error);
|
||||
}
|
||||
bdrv_error_action(blk->bs, action, is_read, error);
|
||||
}
|
||||
|
||||
int blk_is_read_only(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
return bdrv_is_read_only(blk->bs);
|
||||
} else {
|
||||
return blk->root_state.read_only;
|
||||
}
|
||||
return bdrv_is_read_only(blk->bs);
|
||||
}
|
||||
|
||||
int blk_is_sg(BlockBackend *blk)
|
||||
{
|
||||
if (!blk->bs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_is_sg(blk->bs);
|
||||
}
|
||||
|
||||
int blk_enable_write_cache(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
return bdrv_enable_write_cache(blk->bs);
|
||||
} else {
|
||||
return !!(blk->root_state.open_flags & BDRV_O_CACHE_WB);
|
||||
}
|
||||
return bdrv_enable_write_cache(blk->bs);
|
||||
}
|
||||
|
||||
void blk_set_enable_write_cache(BlockBackend *blk, bool wce)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_set_enable_write_cache(blk->bs, wce);
|
||||
} else {
|
||||
if (wce) {
|
||||
blk->root_state.open_flags |= BDRV_O_CACHE_WB;
|
||||
} else {
|
||||
blk->root_state.open_flags &= ~BDRV_O_CACHE_WB;
|
||||
}
|
||||
}
|
||||
bdrv_set_enable_write_cache(blk->bs, wce);
|
||||
}
|
||||
|
||||
void blk_invalidate_cache(BlockBackend *blk, Error **errp)
|
||||
{
|
||||
if (!blk->bs) {
|
||||
error_setg(errp, "Device '%s' has no medium", blk->name);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_invalidate_cache(blk->bs, errp);
|
||||
}
|
||||
|
||||
bool blk_is_inserted(BlockBackend *blk)
|
||||
int blk_is_inserted(BlockBackend *blk)
|
||||
{
|
||||
return blk->bs && bdrv_is_inserted(blk->bs);
|
||||
}
|
||||
|
||||
bool blk_is_available(BlockBackend *blk)
|
||||
{
|
||||
return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk);
|
||||
return bdrv_is_inserted(blk->bs);
|
||||
}
|
||||
|
||||
void blk_lock_medium(BlockBackend *blk, bool locked)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_lock_medium(blk->bs, locked);
|
||||
}
|
||||
bdrv_lock_medium(blk->bs, locked);
|
||||
}
|
||||
|
||||
void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_eject(blk->bs, eject_flag);
|
||||
}
|
||||
bdrv_eject(blk->bs, eject_flag);
|
||||
}
|
||||
|
||||
int blk_get_flags(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
return bdrv_get_flags(blk->bs);
|
||||
} else {
|
||||
return blk->root_state.open_flags;
|
||||
}
|
||||
return bdrv_get_flags(blk->bs);
|
||||
}
|
||||
|
||||
int blk_get_max_transfer_length(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
return blk->bs->bl.max_transfer_length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return blk->bs->bl.max_transfer_length;
|
||||
}
|
||||
|
||||
void blk_set_guest_block_size(BlockBackend *blk, int align)
|
||||
{
|
||||
blk->guest_block_size = align;
|
||||
bdrv_set_guest_block_size(blk->bs, align);
|
||||
}
|
||||
|
||||
void *blk_blockalign(BlockBackend *blk, size_t size)
|
||||
@@ -1043,64 +789,40 @@ void *blk_blockalign(BlockBackend *blk, size_t size)
|
||||
|
||||
bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp)
|
||||
{
|
||||
if (!blk->bs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bdrv_op_is_blocked(blk->bs, op, errp);
|
||||
}
|
||||
|
||||
void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_op_unblock(blk->bs, op, reason);
|
||||
}
|
||||
bdrv_op_unblock(blk->bs, op, reason);
|
||||
}
|
||||
|
||||
void blk_op_block_all(BlockBackend *blk, Error *reason)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_op_block_all(blk->bs, reason);
|
||||
}
|
||||
bdrv_op_block_all(blk->bs, reason);
|
||||
}
|
||||
|
||||
void blk_op_unblock_all(BlockBackend *blk, Error *reason)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_op_unblock_all(blk->bs, reason);
|
||||
}
|
||||
bdrv_op_unblock_all(blk->bs, reason);
|
||||
}
|
||||
|
||||
AioContext *blk_get_aio_context(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
return bdrv_get_aio_context(blk->bs);
|
||||
} else {
|
||||
return qemu_get_aio_context();
|
||||
}
|
||||
}
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
{
|
||||
BlockBackendAIOCB *blk_acb = DO_UPCAST(BlockBackendAIOCB, common, acb);
|
||||
return blk_get_aio_context(blk_acb->blk);
|
||||
return bdrv_get_aio_context(blk->bs);
|
||||
}
|
||||
|
||||
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_set_aio_context(blk->bs, new_context);
|
||||
}
|
||||
bdrv_set_aio_context(blk->bs, new_context);
|
||||
}
|
||||
|
||||
void blk_add_aio_context_notifier(BlockBackend *blk,
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque),
|
||||
void (*detach_aio_context)(void *opaque), void *opaque)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_add_aio_context_notifier(blk->bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
bdrv_add_aio_context_notifier(blk->bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
|
||||
void blk_remove_aio_context_notifier(BlockBackend *blk,
|
||||
@@ -1109,36 +831,28 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
|
||||
void (*detach_aio_context)(void *),
|
||||
void *opaque)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
|
||||
void blk_add_close_notifier(BlockBackend *blk, Notifier *notify)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_add_close_notifier(blk->bs, notify);
|
||||
}
|
||||
bdrv_add_close_notifier(blk->bs, notify);
|
||||
}
|
||||
|
||||
void blk_io_plug(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_io_plug(blk->bs);
|
||||
}
|
||||
bdrv_io_plug(blk->bs);
|
||||
}
|
||||
|
||||
void blk_io_unplug(BlockBackend *blk)
|
||||
{
|
||||
if (blk->bs) {
|
||||
bdrv_io_unplug(blk->bs);
|
||||
}
|
||||
bdrv_io_unplug(blk->bs);
|
||||
}
|
||||
|
||||
BlockAcctStats *blk_get_stats(BlockBackend *blk)
|
||||
{
|
||||
return &blk->stats;
|
||||
return bdrv_get_stats(blk->bs);
|
||||
}
|
||||
|
||||
void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
|
||||
@@ -1171,10 +885,6 @@ int blk_write_compressed(BlockBackend *blk, int64_t sector_num,
|
||||
|
||||
int blk_truncate(BlockBackend *blk, int64_t offset)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_truncate(blk->bs, offset);
|
||||
}
|
||||
|
||||
@@ -1191,94 +901,20 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors)
|
||||
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
|
||||
int64_t pos, int size)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_save_vmstate(blk->bs, buf, pos, size);
|
||||
}
|
||||
|
||||
int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_load_vmstate(blk->bs, buf, pos, size);
|
||||
}
|
||||
|
||||
int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_probe_blocksizes(blk->bs, bsz);
|
||||
}
|
||||
|
||||
int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
return bdrv_probe_geometry(blk->bs, geo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the BlockBackendRootState object with data from the currently
|
||||
* attached BlockDriverState.
|
||||
*/
|
||||
void blk_update_root_state(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->bs);
|
||||
|
||||
blk->root_state.open_flags = blk->bs->open_flags;
|
||||
blk->root_state.read_only = blk->bs->read_only;
|
||||
blk->root_state.detect_zeroes = blk->bs->detect_zeroes;
|
||||
|
||||
if (blk->root_state.throttle_group) {
|
||||
g_free(blk->root_state.throttle_group);
|
||||
throttle_group_unref(blk->root_state.throttle_state);
|
||||
}
|
||||
if (blk->bs->throttle_state) {
|
||||
const char *name = throttle_group_get_name(blk->bs);
|
||||
blk->root_state.throttle_group = g_strdup(name);
|
||||
blk->root_state.throttle_state = throttle_group_incref(name);
|
||||
} else {
|
||||
blk->root_state.throttle_group = NULL;
|
||||
blk->root_state.throttle_state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Applies the information in the root state to the given BlockDriverState. This
|
||||
* does not include the flags which have to be specified for bdrv_open(), use
|
||||
* blk_get_open_flags_from_root_state() to inquire them.
|
||||
*/
|
||||
void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs)
|
||||
{
|
||||
bs->detect_zeroes = blk->root_state.detect_zeroes;
|
||||
if (blk->root_state.throttle_group) {
|
||||
bdrv_io_limits_enable(bs, blk->root_state.throttle_group);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags to be used for bdrv_open() of a BlockDriverState which is
|
||||
* supposed to inherit the root state.
|
||||
*/
|
||||
int blk_get_open_flags_from_root_state(BlockBackend *blk)
|
||||
{
|
||||
int bs_flags;
|
||||
|
||||
bs_flags = blk->root_state.read_only ? 0 : BDRV_O_RDWR;
|
||||
bs_flags |= blk->root_state.open_flags & ~BDRV_O_RDWR;
|
||||
|
||||
return bs_flags;
|
||||
}
|
||||
|
||||
BlockBackendRootState *blk_get_root_state(BlockBackend *blk)
|
||||
{
|
||||
return &blk->root_state;
|
||||
}
|
||||
+4
-4
@@ -103,7 +103,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, &bochs, sizeof(bochs));
|
||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||
s->catalog_size * 4);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -206,7 +206,7 @@ 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->bs, bitmap_offset + (extent_offset / 8),
|
||||
ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
|
||||
&bitmap_entry, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -229,7 +229,7 @@ static int bochs_read(BlockDriverState *bs, int64_t sector_num,
|
||||
if (block_offset < 0) {
|
||||
return block_offset;
|
||||
} else if (block_offset > 0) {
|
||||
ret = bdrv_pread(bs->file->bs, block_offset, buf, 512);
|
||||
ret = bdrv_pread(bs->file, block_offset, buf, 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
+5
-5
@@ -66,7 +66,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
bs->read_only = 1;
|
||||
|
||||
/* read header */
|
||||
ret = bdrv_pread(bs->file->bs, 128, &s->block_size, 4);
|
||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 128 + 4, &s->n_blocks, 4);
|
||||
ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -123,7 +123,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 128 + 4 + 4, s->offsets, offsets_size);
|
||||
ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -203,8 +203,8 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num)
|
||||
int ret;
|
||||
uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num];
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->offsets[block_num],
|
||||
s->compressed_block, bytes);
|
||||
ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block,
|
||||
bytes);
|
||||
if (ret != bytes) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
+6
-7
@@ -17,7 +17,6 @@
|
||||
#include "block/blockjob.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
enum {
|
||||
/*
|
||||
@@ -214,7 +213,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_setg(errp, "Invalid parameter combination");
|
||||
return;
|
||||
}
|
||||
@@ -236,14 +235,14 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
orig_overlay_flags = bdrv_get_flags(overlay_bs);
|
||||
|
||||
/* convert base & overlay_bs to r/w, if necessary */
|
||||
if (!(orig_overlay_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL,
|
||||
orig_overlay_flags | BDRV_O_RDWR);
|
||||
}
|
||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, base,
|
||||
orig_base_flags | BDRV_O_RDWR);
|
||||
}
|
||||
if (!(orig_overlay_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs,
|
||||
orig_overlay_flags | BDRV_O_RDWR);
|
||||
}
|
||||
if (reopen_queue) {
|
||||
bdrv_reopen_multiple(reopen_queue, &local_err);
|
||||
if (local_err != NULL) {
|
||||
|
||||
+6
-8
@@ -154,20 +154,18 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
||||
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, false,
|
||||
curl_multi_read, NULL, state);
|
||||
aio_set_fd_handler(s->aio_context, fd, curl_multi_read,
|
||||
NULL, state);
|
||||
break;
|
||||
case CURL_POLL_OUT:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
NULL, curl_multi_do, state);
|
||||
aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, state);
|
||||
break;
|
||||
case CURL_POLL_INOUT:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
curl_multi_read, curl_multi_do, state);
|
||||
aio_set_fd_handler(s->aio_context, fd, curl_multi_read,
|
||||
curl_multi_do, state);
|
||||
break;
|
||||
case CURL_POLL_REMOVE:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
NULL, NULL, NULL);
|
||||
aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -85,7 +85,7 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result)
|
||||
uint64_t buffer;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, offset, &buffer, 8);
|
||||
ret = bdrv_pread(bs->file, offset, &buffer, 8);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
|
||||
uint32_t buffer;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, offset, &buffer, 4);
|
||||
ret = bdrv_pread(bs->file, offset, &buffer, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -354,7 +354,7 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds,
|
||||
offset += 4;
|
||||
|
||||
buffer = g_realloc(buffer, count);
|
||||
ret = bdrv_pread(bs->file->bs, offset, buffer, count);
|
||||
ret = bdrv_pread(bs->file, offset, buffer, count);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -391,7 +391,7 @@ static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds,
|
||||
|
||||
buffer = g_malloc(info_length + 1);
|
||||
buffer[info_length] = '\0';
|
||||
ret = bdrv_pread(bs->file->bs, info_begin, buffer, info_length);
|
||||
ret = bdrv_pread(bs->file, info_begin, buffer, info_length);
|
||||
if (ret != info_length) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@@ -446,7 +446,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ds.max_sectors_per_chunk = 1;
|
||||
|
||||
/* locate the UDIF trailer */
|
||||
offset = dmg_find_koly_offset(bs->file->bs, errp);
|
||||
offset = dmg_find_koly_offset(bs->file, errp);
|
||||
if (offset < 0) {
|
||||
ret = offset;
|
||||
goto fail;
|
||||
@@ -514,9 +514,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
s->compressed_chunk = qemu_try_blockalign(bs->file->bs,
|
||||
s->compressed_chunk = qemu_try_blockalign(bs->file,
|
||||
ds.max_compressed_size + 1);
|
||||
s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs,
|
||||
s->uncompressed_chunk = qemu_try_blockalign(bs->file,
|
||||
512 * ds.max_sectors_per_chunk);
|
||||
if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) {
|
||||
ret = -ENOMEM;
|
||||
@@ -592,7 +592,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||
case 0x80000005: { /* zlib compressed */
|
||||
/* we need to buffer, because only the chunk as whole can be
|
||||
* inflated. */
|
||||
ret = bdrv_pread(bs->file->bs, s->offsets[chunk],
|
||||
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||
s->compressed_chunk, s->lengths[chunk]);
|
||||
if (ret != s->lengths[chunk]) {
|
||||
return -1;
|
||||
@@ -616,7 +616,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||
case 0x80000006: /* bzip2 compressed */
|
||||
/* we need to buffer, because only the chunk as whole can be
|
||||
* inflated. */
|
||||
ret = bdrv_pread(bs->file->bs, s->offsets[chunk],
|
||||
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||
s->compressed_chunk, s->lengths[chunk]);
|
||||
if (ret != s->lengths[chunk]) {
|
||||
return -1;
|
||||
@@ -641,7 +641,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||
break;
|
||||
#endif /* CONFIG_BZIP2 */
|
||||
case 1: /* copy */
|
||||
ret = bdrv_pread(bs->file->bs, s->offsets[chunk],
|
||||
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||
s->uncompressed_chunk, s->lengths[chunk]);
|
||||
if (ret != s->lengths[chunk]) {
|
||||
return -1;
|
||||
|
||||
+53
-33
@@ -429,23 +429,28 @@ static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB acb;
|
||||
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);
|
||||
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);
|
||||
ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
return acb.ret;
|
||||
ret = acb->ret;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool gluster_supports_zerofill(void)
|
||||
@@ -536,30 +541,35 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB acb;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
size_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);
|
||||
acb->size = size;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
if (write) {
|
||||
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||
gluster_finish_aiocb, &acb);
|
||||
&gluster_finish_aiocb, acb);
|
||||
} else {
|
||||
ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||
gluster_finish_aiocb, &acb);
|
||||
&gluster_finish_aiocb, acb);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
return acb.ret;
|
||||
ret = acb->ret;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
||||
@@ -590,21 +600,26 @@ static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
|
||||
static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB acb;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
|
||||
acb.size = 0;
|
||||
acb.ret = 0;
|
||||
acb.coroutine = qemu_coroutine_self();
|
||||
acb.aio_context = bdrv_get_aio_context(bs);
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb);
|
||||
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
return acb.ret;
|
||||
ret = acb->ret;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||
@@ -612,23 +627,28 @@ static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB acb;
|
||||
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
size_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
|
||||
acb.size = 0;
|
||||
acb.ret = 0;
|
||||
acb.coroutine = qemu_coroutine_self();
|
||||
acb.aio_context = bdrv_get_aio_context(bs);
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
|
||||
ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
return acb.ret;
|
||||
ret = acb->ret;
|
||||
|
||||
out:
|
||||
g_slice_free(GlusterAIOCB, acb);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+66
-214
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "trace.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/throttle-groups.h"
|
||||
@@ -157,38 +156,38 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
|
||||
/* Take some limits from the children as a default */
|
||||
if (bs->file) {
|
||||
bdrv_refresh_limits(bs->file->bs, &local_err);
|
||||
bdrv_refresh_limits(bs->file, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
bs->bl.opt_transfer_length = bs->file->bs->bl.opt_transfer_length;
|
||||
bs->bl.max_transfer_length = bs->file->bs->bl.max_transfer_length;
|
||||
bs->bl.min_mem_alignment = bs->file->bs->bl.min_mem_alignment;
|
||||
bs->bl.opt_mem_alignment = bs->file->bs->bl.opt_mem_alignment;
|
||||
bs->bl.opt_transfer_length = bs->file->bl.opt_transfer_length;
|
||||
bs->bl.max_transfer_length = bs->file->bl.max_transfer_length;
|
||||
bs->bl.min_mem_alignment = bs->file->bl.min_mem_alignment;
|
||||
bs->bl.opt_mem_alignment = bs->file->bl.opt_mem_alignment;
|
||||
} else {
|
||||
bs->bl.min_mem_alignment = 512;
|
||||
bs->bl.opt_mem_alignment = getpagesize();
|
||||
}
|
||||
|
||||
if (bs->backing) {
|
||||
bdrv_refresh_limits(bs->backing->bs, &local_err);
|
||||
if (bs->backing_hd) {
|
||||
bdrv_refresh_limits(bs->backing_hd, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
bs->bl.opt_transfer_length =
|
||||
MAX(bs->bl.opt_transfer_length,
|
||||
bs->backing->bs->bl.opt_transfer_length);
|
||||
bs->backing_hd->bl.opt_transfer_length);
|
||||
bs->bl.max_transfer_length =
|
||||
MIN_NON_ZERO(bs->bl.max_transfer_length,
|
||||
bs->backing->bs->bl.max_transfer_length);
|
||||
bs->backing_hd->bl.max_transfer_length);
|
||||
bs->bl.opt_mem_alignment =
|
||||
MAX(bs->bl.opt_mem_alignment,
|
||||
bs->backing->bs->bl.opt_mem_alignment);
|
||||
bs->backing_hd->bl.opt_mem_alignment);
|
||||
bs->bl.min_mem_alignment =
|
||||
MAX(bs->bl.min_mem_alignment,
|
||||
bs->backing->bs->bl.min_mem_alignment);
|
||||
bs->backing_hd->bl.min_mem_alignment);
|
||||
}
|
||||
|
||||
/* Then let the driver override it */
|
||||
@@ -214,10 +213,8 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
/* Check if any requests are in-flight (including throttled requests) */
|
||||
bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
static bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
if (!QLIST_EMPTY(&bs->tracked_requests)) {
|
||||
return true;
|
||||
}
|
||||
@@ -227,31 +224,17 @@ bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
if (bdrv_requests_pending(child->bs)) {
|
||||
return true;
|
||||
}
|
||||
if (bs->file && bdrv_requests_pending(bs->file)) {
|
||||
return true;
|
||||
}
|
||||
if (bs->backing_hd && bdrv_requests_pending(bs->backing_hd)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bdrv_drain_recurse(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
if (bs->drv && bs->drv->bdrv_drain) {
|
||||
bs->drv->bdrv_drain(bs);
|
||||
}
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_drain_recurse(child->bs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for pending requests to complete on a single BlockDriverState subtree,
|
||||
* and suspend block driver's internal I/O until next request arrives.
|
||||
* Wait for pending requests to complete on a single BlockDriverState subtree
|
||||
*
|
||||
* Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState
|
||||
* AioContext.
|
||||
@@ -264,7 +247,6 @@ void bdrv_drain(BlockDriverState *bs)
|
||||
{
|
||||
bool busy = true;
|
||||
|
||||
bdrv_drain_recurse(bs);
|
||||
while (busy) {
|
||||
/* Keep iterating */
|
||||
bdrv_flush_io_queue(bs);
|
||||
@@ -362,14 +344,13 @@ static void tracked_request_end(BdrvTrackedRequest *req)
|
||||
static void tracked_request_begin(BdrvTrackedRequest *req,
|
||||
BlockDriverState *bs,
|
||||
int64_t offset,
|
||||
unsigned int bytes,
|
||||
enum BdrvTrackedRequestType type)
|
||||
unsigned int bytes, bool is_write)
|
||||
{
|
||||
*req = (BdrvTrackedRequest){
|
||||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.type = type,
|
||||
.is_write = is_write,
|
||||
.co = qemu_coroutine_self(),
|
||||
.serialising = false,
|
||||
.overlap_offset = offset,
|
||||
@@ -863,9 +844,7 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
|
||||
mark_request_serialising(req, bdrv_get_cluster_size(bs));
|
||||
}
|
||||
|
||||
if (!(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
wait_serialising_requests(req);
|
||||
}
|
||||
wait_serialising_requests(req);
|
||||
|
||||
if (flags & BDRV_REQ_COPY_ON_READ) {
|
||||
int pnum;
|
||||
@@ -953,8 +932,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Don't do copy-on-read if we read data before write operation */
|
||||
if (bs->copy_on_read && !(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
if (bs->copy_on_read) {
|
||||
flags |= BDRV_REQ_COPY_ON_READ;
|
||||
}
|
||||
|
||||
@@ -988,7 +966,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
|
||||
bytes = ROUND_UP(bytes, align);
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
|
||||
tracked_request_begin(&req, bs, offset, bytes, false);
|
||||
ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align,
|
||||
use_local_qiov ? &local_qiov : qiov,
|
||||
flags);
|
||||
@@ -1023,15 +1001,6 @@ int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov, 0);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_readv_no_serialising(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
trace_bdrv_co_readv_no_serialising(bs, sector_num, nb_sectors);
|
||||
|
||||
return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov,
|
||||
BDRV_REQ_NO_SERIALISING);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
@@ -1158,13 +1127,13 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
|
||||
if (ret < 0) {
|
||||
/* Do nothing, write notifier decided to fail this request */
|
||||
} else if (flags & BDRV_REQ_ZERO_WRITE) {
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_ZERO);
|
||||
ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors, flags);
|
||||
} else {
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV);
|
||||
ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_DONE);
|
||||
|
||||
if (ret == 0 && !bs->enable_write_cache) {
|
||||
ret = bdrv_co_flush(bs);
|
||||
@@ -1172,9 +1141,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
|
||||
|
||||
bdrv_set_dirty(bs, sector_num, nb_sectors);
|
||||
|
||||
if (bs->wr_highest_offset < offset + bytes) {
|
||||
bs->wr_highest_offset = offset + bytes;
|
||||
}
|
||||
block_acct_highest_sector(&bs->stats, sector_num, nb_sectors);
|
||||
|
||||
if (ret >= 0) {
|
||||
bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors);
|
||||
@@ -1215,13 +1182,13 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
|
||||
/* RMW the unaligned part before head. */
|
||||
mark_request_serialising(req, align);
|
||||
wait_serialising_requests(req);
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
||||
ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align,
|
||||
align, &local_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
|
||||
|
||||
memset(buf + head_padding_bytes, 0, zero_bytes);
|
||||
ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align,
|
||||
@@ -1253,13 +1220,13 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
|
||||
/* RMW the unaligned part after tail. */
|
||||
mark_request_serialising(req, align);
|
||||
wait_serialising_requests(req);
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
||||
ret = bdrv_aligned_preadv(bs, req, offset, align,
|
||||
align, &local_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
|
||||
|
||||
memset(buf, 0, bytes);
|
||||
ret = bdrv_aligned_pwritev(bs, req, offset, align,
|
||||
@@ -1309,7 +1276,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
||||
* Pad qiov with the read parts and be sure to have a tracked request not
|
||||
* only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle.
|
||||
*/
|
||||
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE);
|
||||
tracked_request_begin(&req, bs, offset, bytes, true);
|
||||
|
||||
if (!qiov) {
|
||||
ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req);
|
||||
@@ -1330,13 +1297,13 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
||||
};
|
||||
qemu_iovec_init_external(&head_qiov, &head_iov, 1);
|
||||
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
||||
ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align,
|
||||
align, &head_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
|
||||
|
||||
qemu_iovec_init(&local_qiov, qiov->niov + 2);
|
||||
qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
|
||||
@@ -1364,13 +1331,13 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
||||
};
|
||||
qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
|
||||
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
||||
ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align,
|
||||
align, &tail_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
|
||||
|
||||
if (!use_local_qiov) {
|
||||
qemu_iovec_init(&local_qiov, qiov->niov + 1);
|
||||
@@ -1519,7 +1486,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
|
||||
if (ret & BDRV_BLOCK_RAW) {
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
||||
return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS,
|
||||
return bdrv_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, pnum);
|
||||
}
|
||||
|
||||
@@ -1528,8 +1495,8 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
} else {
|
||||
if (bdrv_unallocated_blocks_are_zero(bs)) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
} else if (bs->backing) {
|
||||
BlockDriverState *bs2 = bs->backing->bs;
|
||||
} else if (bs->backing_hd) {
|
||||
BlockDriverState *bs2 = bs->backing_hd;
|
||||
int64_t nb_sectors2 = bdrv_nb_sectors(bs2);
|
||||
if (nb_sectors2 >= 0 && sector_num >= nb_sectors2) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
@@ -1542,7 +1509,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||
(ret & BDRV_BLOCK_OFFSET_VALID)) {
|
||||
int file_pnum;
|
||||
|
||||
ret2 = bdrv_co_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS,
|
||||
ret2 = bdrv_co_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, &file_pnum);
|
||||
if (ret2 >= 0) {
|
||||
/* Ignore errors. This is just providing extra information, it
|
||||
@@ -1574,7 +1541,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs,
|
||||
int64_t ret = 0;
|
||||
|
||||
assert(bs != base);
|
||||
for (p = bs; p != base; p = backing_bs(p)) {
|
||||
for (p = bs; p != base; p = p->backing_hd) {
|
||||
ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum);
|
||||
if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) {
|
||||
break;
|
||||
@@ -1637,7 +1604,7 @@ int64_t bdrv_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
return bdrv_get_block_status_above(bs, backing_bs(bs),
|
||||
return bdrv_get_block_status_above(bs, bs->backing_hd,
|
||||
sector_num, nb_sectors, pnum);
|
||||
}
|
||||
|
||||
@@ -1695,7 +1662,7 @@ int bdrv_is_allocated_above(BlockDriverState *top,
|
||||
n = pnum_inter;
|
||||
}
|
||||
|
||||
intermediate = backing_bs(intermediate);
|
||||
intermediate = intermediate->backing_hd;
|
||||
}
|
||||
|
||||
*pnum = n;
|
||||
@@ -1746,7 +1713,7 @@ int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
|
||||
} else if (drv->bdrv_save_vmstate) {
|
||||
return drv->bdrv_save_vmstate(bs, qiov, pos);
|
||||
} else if (bs->file) {
|
||||
return bdrv_writev_vmstate(bs->file->bs, qiov, pos);
|
||||
return bdrv_writev_vmstate(bs->file, qiov, pos);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
@@ -1761,7 +1728,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
if (drv->bdrv_load_vmstate)
|
||||
return drv->bdrv_load_vmstate(bs, buf, pos, size);
|
||||
if (bs->file)
|
||||
return bdrv_load_vmstate(bs->file->bs, buf, pos, size);
|
||||
return bdrv_load_vmstate(bs->file, buf, pos, size);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -1926,10 +1893,7 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
|
||||
}
|
||||
}
|
||||
|
||||
if (bs->blk) {
|
||||
block_acct_merge_done(blk_get_stats(bs->blk), BLOCK_ACCT_WRITE,
|
||||
num_reqs - outidx - 1);
|
||||
}
|
||||
block_acct_merge_done(&bs->stats, BLOCK_ACCT_WRITE, num_reqs - outidx - 1);
|
||||
|
||||
return outidx + 1;
|
||||
}
|
||||
@@ -2244,7 +2208,7 @@ void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
|
||||
{
|
||||
BlockAIOCB *acb;
|
||||
|
||||
acb = g_malloc(aiocb_info->aiocb_size);
|
||||
acb = g_slice_alloc(aiocb_info->aiocb_size);
|
||||
acb->aiocb_info = aiocb_info;
|
||||
acb->bs = bs;
|
||||
acb->cb = cb;
|
||||
@@ -2264,7 +2228,7 @@ void qemu_aio_unref(void *p)
|
||||
BlockAIOCB *acb = p;
|
||||
assert(acb->refcnt > 0);
|
||||
if (--acb->refcnt == 0) {
|
||||
g_free(acb);
|
||||
g_slice_free1(acb->aiocb_info->aiocb_size, acb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2334,20 +2298,18 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
|
||||
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
int ret;
|
||||
BdrvTrackedRequest req;
|
||||
|
||||
if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) ||
|
||||
bdrv_is_sg(bs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, 0, 0, BDRV_TRACKED_FLUSH);
|
||||
/* Write back cached data to the OS even with cache=unsafe */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS);
|
||||
if (bs->drv->bdrv_co_flush_to_os) {
|
||||
ret = bs->drv->bdrv_co_flush_to_os(bs);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2387,17 +2349,14 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
ret = 0;
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH
|
||||
* in the case of cache=unsafe, so there are no useless flushes.
|
||||
*/
|
||||
flush_parent:
|
||||
ret = bs->file ? bdrv_co_flush(bs->file->bs) : 0;
|
||||
out:
|
||||
tracked_request_end(&req);
|
||||
return ret;
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
int bdrv_flush(BlockDriverState *bs)
|
||||
@@ -2440,7 +2399,6 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque)
|
||||
int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
BdrvTrackedRequest req;
|
||||
int max_discard, ret;
|
||||
|
||||
if (!bs->drv) {
|
||||
@@ -2463,8 +2421,6 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, sector_num, nb_sectors,
|
||||
BDRV_TRACKED_DISCARD);
|
||||
bdrv_set_dirty(bs, sector_num, nb_sectors);
|
||||
|
||||
max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS);
|
||||
@@ -2498,24 +2454,20 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
if (acb == NULL) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
return -EIO;
|
||||
} else {
|
||||
qemu_coroutine_yield();
|
||||
ret = co.ret;
|
||||
}
|
||||
}
|
||||
if (ret && ret != -ENOTSUP) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
sector_num += num;
|
||||
nb_sectors -= num;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
tracked_request_end(&req);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
||||
@@ -2544,109 +2496,26 @@ int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
||||
return rwco.ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
CoroutineIOCompletion *co;
|
||||
QEMUBH *bh;
|
||||
} BdrvIoctlCompletionData;
|
||||
|
||||
static void bdrv_ioctl_bh_cb(void *opaque)
|
||||
{
|
||||
BdrvIoctlCompletionData *data = opaque;
|
||||
|
||||
bdrv_co_io_em_complete(data->co, -ENOTSUP);
|
||||
qemu_bh_delete(data->bh);
|
||||
}
|
||||
|
||||
static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
BdrvTrackedRequest tracked_req;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
BlockAIOCB *acb;
|
||||
|
||||
tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL);
|
||||
if (!drv || !drv->bdrv_aio_ioctl) {
|
||||
co.ret = -ENOTSUP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co);
|
||||
if (!acb) {
|
||||
BdrvIoctlCompletionData *data = g_new(BdrvIoctlCompletionData, 1);
|
||||
data->bh = aio_bh_new(bdrv_get_aio_context(bs),
|
||||
bdrv_ioctl_bh_cb, data);
|
||||
data->co = &co;
|
||||
qemu_bh_schedule(data->bh);
|
||||
}
|
||||
qemu_coroutine_yield();
|
||||
out:
|
||||
tracked_request_end(&tracked_req);
|
||||
return co.ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BlockDriverState *bs;
|
||||
int req;
|
||||
void *buf;
|
||||
int ret;
|
||||
} BdrvIoctlCoData;
|
||||
|
||||
static void coroutine_fn bdrv_co_ioctl_entry(void *opaque)
|
||||
{
|
||||
BdrvIoctlCoData *data = opaque;
|
||||
data->ret = bdrv_co_do_ioctl(data->bs, data->req, data->buf);
|
||||
}
|
||||
|
||||
/* needed for generic scsi interface */
|
||||
|
||||
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
BdrvIoctlCoData data = {
|
||||
.bs = bs,
|
||||
.req = req,
|
||||
.buf = buf,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_co_ioctl_entry(&data);
|
||||
} else {
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
}
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
return data.ret;
|
||||
}
|
||||
|
||||
static void coroutine_fn bdrv_co_aio_ioctl_entry(void *opaque)
|
||||
{
|
||||
BlockAIOCBCoroutine *acb = opaque;
|
||||
acb->req.error = bdrv_co_do_ioctl(acb->common.bs,
|
||||
acb->req.req, acb->req.buf);
|
||||
bdrv_co_complete(acb);
|
||||
if (drv && drv->bdrv_ioctl)
|
||||
return drv->bdrv_ioctl(bs, req, buf);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BlockAIOCBCoroutine *acb = qemu_aio_get(&bdrv_em_co_aiocb_info,
|
||||
bs, cb, opaque);
|
||||
Coroutine *co;
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
acb->need_bh = true;
|
||||
acb->req.error = -EINPROGRESS;
|
||||
acb->req.req = req;
|
||||
acb->req.buf = buf;
|
||||
co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
bdrv_co_maybe_schedule_bh(acb);
|
||||
return &acb->common;
|
||||
if (drv && drv->bdrv_aio_ioctl)
|
||||
return drv->bdrv_aio_ioctl(bs, req, buf, cb, opaque);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *qemu_blockalign(BlockDriverState *bs, size_t size)
|
||||
@@ -2715,7 +2584,7 @@ void bdrv_io_plug(BlockDriverState *bs)
|
||||
if (drv && drv->bdrv_io_plug) {
|
||||
drv->bdrv_io_plug(bs);
|
||||
} else if (bs->file) {
|
||||
bdrv_io_plug(bs->file->bs);
|
||||
bdrv_io_plug(bs->file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2725,7 +2594,7 @@ void bdrv_io_unplug(BlockDriverState *bs)
|
||||
if (drv && drv->bdrv_io_unplug) {
|
||||
drv->bdrv_io_unplug(bs);
|
||||
} else if (bs->file) {
|
||||
bdrv_io_unplug(bs->file->bs);
|
||||
bdrv_io_unplug(bs->file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2735,24 +2604,7 @@ void bdrv_flush_io_queue(BlockDriverState *bs)
|
||||
if (drv && drv->bdrv_flush_io_queue) {
|
||||
drv->bdrv_flush_io_queue(bs);
|
||||
} else if (bs->file) {
|
||||
bdrv_flush_io_queue(bs->file->bs);
|
||||
bdrv_flush_io_queue(bs->file);
|
||||
}
|
||||
bdrv_start_throttled_reqs(bs);
|
||||
}
|
||||
|
||||
void bdrv_drained_begin(BlockDriverState *bs)
|
||||
{
|
||||
if (!bs->quiesce_counter++) {
|
||||
aio_disable_external(bdrv_get_aio_context(bs));
|
||||
}
|
||||
bdrv_drain(bs);
|
||||
}
|
||||
|
||||
void bdrv_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
assert(bs->quiesce_counter > 0);
|
||||
if (--bs->quiesce_counter > 0) {
|
||||
return;
|
||||
}
|
||||
aio_enable_external(bdrv_get_aio_context(bs));
|
||||
}
|
||||
+60
-115
@@ -84,7 +84,6 @@ typedef struct IscsiTask {
|
||||
IscsiLun *iscsilun;
|
||||
QEMUTimer retry_timer;
|
||||
bool force_next_flush;
|
||||
int err_code;
|
||||
} IscsiTask;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
@@ -97,7 +96,6 @@ typedef struct IscsiAIOCB {
|
||||
int status;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
int ret;
|
||||
#ifdef __linux__
|
||||
sg_io_hdr_t *ioh;
|
||||
#endif
|
||||
@@ -171,70 +169,19 @@ static inline unsigned exp_random(double mean)
|
||||
return -mean * log((double)rand() / RAND_MAX);
|
||||
}
|
||||
|
||||
/* SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST was introduced in
|
||||
* libiscsi 1.10.0, together with other constants we need. Use it as
|
||||
* a hint that we have to define them ourselves if needed, to keep the
|
||||
* minimum required libiscsi version at 1.9.0. We use an ASCQ macro for
|
||||
* the test because SCSI_STATUS_* is an enum.
|
||||
*
|
||||
* To guard against future changes where SCSI_SENSE_ASCQ_* also becomes
|
||||
* an enum, check against the LIBISCSI_API_VERSION macro, which was
|
||||
* introduced in 1.11.0. If it is present, there is no need to define
|
||||
* anything.
|
||||
*/
|
||||
#if !defined(SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST) && \
|
||||
!defined(LIBISCSI_API_VERSION)
|
||||
#define SCSI_STATUS_TASK_SET_FULL 0x28
|
||||
#define SCSI_STATUS_TIMEOUT 0x0f000002
|
||||
#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x2600
|
||||
#define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00
|
||||
/* SCSI_STATUS_TASK_SET_FULL and SCSI_STATUS_TIMEOUT were introduced
|
||||
* in libiscsi 1.10.0 as part of an enum. The LIBISCSI_API_VERSION
|
||||
* macro was introduced in 1.11.0. So use the API_VERSION macro as
|
||||
* a hint that the macros are defined and define them ourselves
|
||||
* otherwise to keep the required libiscsi version at 1.9.0 */
|
||||
#if !defined(LIBISCSI_API_VERSION)
|
||||
#define QEMU_SCSI_STATUS_TASK_SET_FULL 0x28
|
||||
#define QEMU_SCSI_STATUS_TIMEOUT 0x0f000002
|
||||
#else
|
||||
#define QEMU_SCSI_STATUS_TASK_SET_FULL SCSI_STATUS_TASK_SET_FULL
|
||||
#define QEMU_SCSI_STATUS_TIMEOUT SCSI_STATUS_TIMEOUT
|
||||
#endif
|
||||
|
||||
static int iscsi_translate_sense(struct scsi_sense *sense)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (sense->key) {
|
||||
case SCSI_SENSE_NOT_READY:
|
||||
return -EBUSY;
|
||||
case SCSI_SENSE_DATA_PROTECTION:
|
||||
return -EACCES;
|
||||
case SCSI_SENSE_COMMAND_ABORTED:
|
||||
return -ECANCELED;
|
||||
case SCSI_SENSE_ILLEGAL_REQUEST:
|
||||
/* Parse ASCQ */
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
switch (sense->ascq) {
|
||||
case SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR:
|
||||
case SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE:
|
||||
case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB:
|
||||
case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE:
|
||||
ret = -ENOSPC;
|
||||
break;
|
||||
case SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT:
|
||||
case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED:
|
||||
case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN:
|
||||
ret = -ENOMEDIUM;
|
||||
break;
|
||||
case SCSI_SENSE_ASCQ_WRITE_PROTECTED:
|
||||
ret = -EACCES;
|
||||
break;
|
||||
default:
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
@@ -256,11 +203,11 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
goto out;
|
||||
}
|
||||
if (status == SCSI_STATUS_BUSY ||
|
||||
status == SCSI_STATUS_TIMEOUT ||
|
||||
status == SCSI_STATUS_TASK_SET_FULL) {
|
||||
status == QEMU_SCSI_STATUS_TIMEOUT ||
|
||||
status == QEMU_SCSI_STATUS_TASK_SET_FULL) {
|
||||
unsigned retry_time =
|
||||
exp_random(iscsi_retry_times[iTask->retries - 1]);
|
||||
if (status == SCSI_STATUS_TIMEOUT) {
|
||||
if (status == QEMU_SCSI_STATUS_TIMEOUT) {
|
||||
/* make sure the request is rescheduled AFTER the
|
||||
* reconnect is initiated */
|
||||
retry_time = EVENT_INTERVAL * 2;
|
||||
@@ -279,7 +226,6 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
return;
|
||||
}
|
||||
}
|
||||
iTask->err_code = iscsi_translate_sense(&task->sense);
|
||||
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
||||
} else {
|
||||
iTask->iscsilun->force_next_flush |= iTask->force_next_flush;
|
||||
@@ -345,8 +291,8 @@ iscsi_set_events(IscsiLun *iscsilun)
|
||||
int ev = iscsi_which_events(iscsi);
|
||||
|
||||
if (ev != iscsilun->events) {
|
||||
aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsi),
|
||||
false,
|
||||
aio_set_fd_handler(iscsilun->aio_context,
|
||||
iscsi_get_fd(iscsi),
|
||||
(ev & POLLIN) ? iscsi_process_read : NULL,
|
||||
(ev & POLLOUT) ? iscsi_process_write : NULL,
|
||||
iscsilun);
|
||||
@@ -509,7 +455,7 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
|
||||
@@ -698,7 +644,7 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -737,7 +683,7 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -757,7 +703,7 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
||||
if (status < 0) {
|
||||
error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = iscsi_translate_sense(&acb->task->sense);
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
acb->ioh->driver_status = 0;
|
||||
@@ -780,38 +726,6 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
||||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static void iscsi_ioctl_bh_completion(void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static void iscsi_ioctl_handle_emulated(IscsiAIOCB *acb, int req, void *buf)
|
||||
{
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int ret = 0;
|
||||
|
||||
switch (req) {
|
||||
case SG_GET_VERSION_NUM:
|
||||
*(int *)buf = 30000;
|
||||
break;
|
||||
case SG_GET_SCSI_ID:
|
||||
((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
assert(!acb->bh);
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(bs),
|
||||
iscsi_ioctl_bh_completion, acb);
|
||||
acb->ret = ret;
|
||||
qemu_bh_schedule(acb->bh);
|
||||
}
|
||||
|
||||
static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
@@ -821,6 +735,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
struct iscsi_data data;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
assert(req == SG_IO);
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
@@ -829,11 +745,6 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
acb->buf = NULL;
|
||||
acb->ioh = buf;
|
||||
|
||||
if (req != SG_IO) {
|
||||
iscsi_ioctl_handle_emulated(acb, req, buf);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
||||
@@ -898,6 +809,38 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void ioctl_cb(void *opaque, int status)
|
||||
{
|
||||
int *p_status = opaque;
|
||||
*p_status = status;
|
||||
}
|
||||
|
||||
static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int status;
|
||||
|
||||
switch (req) {
|
||||
case SG_GET_VERSION_NUM:
|
||||
*(int *)buf = 30000;
|
||||
break;
|
||||
case SG_GET_SCSI_ID:
|
||||
((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
|
||||
break;
|
||||
case SG_IO:
|
||||
status = -EINPROGRESS;
|
||||
iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status);
|
||||
|
||||
while (status == -EINPROGRESS) {
|
||||
aio_poll(iscsilun->aio_context, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int64_t
|
||||
@@ -962,7 +905,7 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
|
||||
@@ -1056,7 +999,7 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (flags & BDRV_REQ_MAY_UNMAP) {
|
||||
@@ -1337,8 +1280,9 @@ static void iscsi_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi),
|
||||
false, NULL, NULL, NULL);
|
||||
aio_set_fd_handler(iscsilun->aio_context,
|
||||
iscsi_get_fd(iscsilun->iscsi),
|
||||
NULL, NULL, NULL);
|
||||
iscsilun->events = 0;
|
||||
|
||||
if (iscsilun->nop_timer) {
|
||||
@@ -1828,6 +1772,7 @@ static BlockDriver bdrv_iscsi = {
|
||||
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||
|
||||
#ifdef __linux__
|
||||
.bdrv_ioctl = iscsi_ioctl,
|
||||
.bdrv_aio_ioctl = iscsi_aio_ioctl,
|
||||
#endif
|
||||
|
||||
|
||||
+2
-3
@@ -287,7 +287,7 @@ void laio_detach_aio_context(void *s_, AioContext *old_context)
|
||||
{
|
||||
struct qemu_laio_state *s = s_;
|
||||
|
||||
aio_set_event_notifier(old_context, &s->e, false, NULL);
|
||||
aio_set_event_notifier(old_context, &s->e, NULL);
|
||||
qemu_bh_delete(s->completion_bh);
|
||||
}
|
||||
|
||||
@@ -296,8 +296,7 @@ void laio_attach_aio_context(void *s_, AioContext *new_context)
|
||||
struct qemu_laio_state *s = s_;
|
||||
|
||||
s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s);
|
||||
aio_set_event_notifier(new_context, &s->e, false,
|
||||
qemu_laio_completion_cb);
|
||||
aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb);
|
||||
}
|
||||
|
||||
void *laio_init(void)
|
||||
|
||||
+23
-42
@@ -14,7 +14,6 @@
|
||||
#include "trace.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "qemu/bitmap.h"
|
||||
@@ -114,7 +113,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
}
|
||||
|
||||
qemu_iovec_destroy(&op->qiov);
|
||||
g_free(op);
|
||||
g_slice_free(MirrorOp, op);
|
||||
|
||||
if (s->waiting_for_io) {
|
||||
qemu_coroutine_enter(s->common.co, NULL);
|
||||
@@ -247,10 +246,6 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
|
||||
break;
|
||||
}
|
||||
if (IOV_MAX < nb_chunks + added_chunks) {
|
||||
trace_mirror_break_iov_max(s, nb_chunks, added_chunks);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We have enough free space to copy these sectors. */
|
||||
bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks);
|
||||
@@ -265,7 +260,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
} while (delay_ns == 0 && next_sector < end);
|
||||
|
||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||
op = g_new(MirrorOp, 1);
|
||||
op = g_slice_new(MirrorOp);
|
||||
op->s = s;
|
||||
op->sector_num = sector_num;
|
||||
op->nb_sectors = nb_sectors;
|
||||
@@ -354,11 +349,6 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
MirrorExitData *data = opaque;
|
||||
AioContext *replace_aio_context = NULL;
|
||||
BlockDriverState *src = s->common.bs;
|
||||
|
||||
/* Make sure that the source BDS doesn't go away before we called
|
||||
* block_job_completed(). */
|
||||
bdrv_ref(src);
|
||||
|
||||
if (s->to_replace) {
|
||||
replace_aio_context = bdrv_get_aio_context(s->to_replace);
|
||||
@@ -373,7 +363,14 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
|
||||
bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
|
||||
}
|
||||
bdrv_replace_in_backing_chain(to_replace, s->target);
|
||||
bdrv_swap(s->target, to_replace);
|
||||
if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
|
||||
/* drop the bs loop chain formed by the swap: break the loop then
|
||||
* trigger the unref from the top one */
|
||||
BlockDriverState *p = s->base->backing_hd;
|
||||
bdrv_set_backing_hd(s->base, NULL);
|
||||
bdrv_unref(p);
|
||||
}
|
||||
}
|
||||
if (s->to_replace) {
|
||||
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
|
||||
@@ -384,12 +381,9 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
aio_context_release(replace_aio_context);
|
||||
}
|
||||
g_free(s->replaces);
|
||||
bdrv_op_unblock_all(s->target, s->common.blocker);
|
||||
bdrv_unref(s->target);
|
||||
block_job_completed(&s->common, data->ret);
|
||||
g_free(data);
|
||||
bdrv_drained_end(src);
|
||||
bdrv_unref(src);
|
||||
}
|
||||
|
||||
static void coroutine_fn mirror_run(void *opaque)
|
||||
@@ -433,7 +427,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) {
|
||||
if (backing_filename[0] && !s->target->backing_hd) {
|
||||
ret = bdrv_get_info(s->target, &bdi);
|
||||
if (ret < 0) {
|
||||
goto immediate_exit;
|
||||
@@ -457,8 +451,6 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
if (!s->is_none_mode) {
|
||||
/* First part, loop on the sectors and initialize the dirty bitmap. */
|
||||
BlockDriverState *base = s->base;
|
||||
bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(s->target);
|
||||
|
||||
for (sector_num = 0; sector_num < end; ) {
|
||||
/* Just to make sure we are not exceeding int limit. */
|
||||
int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS,
|
||||
@@ -481,7 +473,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
|
||||
assert(n > 0);
|
||||
if (ret == 1 || mark_all_dirty) {
|
||||
if (ret == 1) {
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n);
|
||||
}
|
||||
sector_num += n;
|
||||
@@ -602,15 +594,10 @@ immediate_exit:
|
||||
g_free(s->cow_bitmap);
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||
if (s->target->blk) {
|
||||
blk_iostatus_disable(s->target->blk);
|
||||
}
|
||||
bdrv_iostatus_disable(s->target);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
/* Before we switch to target in mirror_exit, make sure data doesn't
|
||||
* change. */
|
||||
bdrv_drained_begin(s->common.bs);
|
||||
block_job_defer_to_main_loop(&s->common, mirror_exit, data);
|
||||
}
|
||||
|
||||
@@ -629,9 +616,7 @@ static void mirror_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
|
||||
if (s->target->blk) {
|
||||
blk_iostatus_reset(s->target->blk);
|
||||
}
|
||||
bdrv_iostatus_reset(s->target);
|
||||
}
|
||||
|
||||
static void mirror_complete(BlockJob *job, Error **errp)
|
||||
@@ -646,7 +631,8 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
return;
|
||||
}
|
||||
if (!s->synced) {
|
||||
error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
|
||||
error_setg(errp, QERR_BLOCK_JOB_NOT_READY,
|
||||
bdrv_get_device_name(job->bs));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -654,9 +640,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
if (s->replaces) {
|
||||
AioContext *replace_aio_context;
|
||||
|
||||
s->to_replace = bdrv_find_node(s->replaces);
|
||||
s->to_replace = check_to_replace_node(s->replaces, &local_err);
|
||||
if (!s->to_replace) {
|
||||
error_setg(errp, "Node name '%s' not found", s->replaces);
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -714,7 +700,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error");
|
||||
return;
|
||||
}
|
||||
@@ -746,17 +732,12 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
||||
if (!s->dirty_bitmap) {
|
||||
g_free(s->replaces);
|
||||
block_job_unref(&s->common);
|
||||
block_job_release(bs);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_op_block_all(s->target, s->common.blocker);
|
||||
|
||||
bdrv_set_enable_write_cache(s->target, true);
|
||||
if (s->target->blk) {
|
||||
blk_set_on_error(s->target->blk, on_target_error, on_target_error);
|
||||
blk_iostatus_enable(s->target->blk);
|
||||
}
|
||||
bdrv_set_on_error(s->target, on_target_error, on_target_error);
|
||||
bdrv_iostatus_enable(s->target);
|
||||
s->common.co = qemu_coroutine_create(mirror_run);
|
||||
trace_mirror_start(bs, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
@@ -779,7 +760,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
return;
|
||||
}
|
||||
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
||||
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, unmap, cb, opaque, errp,
|
||||
|
||||
+4
-6
@@ -124,7 +124,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
s->send_coroutine = qemu_coroutine_self();
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_set_fd_handler(aio_context, s->sock, false,
|
||||
aio_set_fd_handler(aio_context, s->sock,
|
||||
nbd_reply_ready, nbd_restart_write, bs);
|
||||
if (qiov) {
|
||||
if (!s->is_unix) {
|
||||
@@ -144,8 +144,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
} else {
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
}
|
||||
aio_set_fd_handler(aio_context, s->sock, false,
|
||||
nbd_reply_ready, NULL, bs);
|
||||
aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, bs);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return rc;
|
||||
@@ -349,15 +348,14 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs),
|
||||
nbd_get_client_session(bs)->sock,
|
||||
false, NULL, NULL, NULL);
|
||||
nbd_get_client_session(bs)->sock, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock,
|
||||
false, nbd_reply_ready, NULL, bs);
|
||||
nbd_reply_ready, NULL, bs);
|
||||
}
|
||||
|
||||
void nbd_client_close(BlockDriverState *bs)
|
||||
|
||||
+34
-37
@@ -43,6 +43,7 @@
|
||||
|
||||
typedef struct BDRVNBDState {
|
||||
NbdClientSession client;
|
||||
QemuOpts *socket_opts;
|
||||
} BDRVNBDState;
|
||||
|
||||
static int nbd_parse_uri(const char *filename, QDict *options)
|
||||
@@ -189,10 +190,10 @@ out:
|
||||
g_free(file);
|
||||
}
|
||||
|
||||
static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
||||
Error **errp)
|
||||
static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
||||
Error **errp)
|
||||
{
|
||||
SocketAddress *saddr;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
|
||||
if (qdict_haskey(options, "path")) {
|
||||
@@ -200,37 +201,28 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
||||
} else {
|
||||
error_setg(errp, "one of path and host must be specified.");
|
||||
}
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
saddr = g_new0(SocketAddress, 1);
|
||||
s->client.is_unix = qdict_haskey(options, "path");
|
||||
s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
|
||||
&error_abort);
|
||||
|
||||
if (qdict_haskey(options, "path")) {
|
||||
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
||||
saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
||||
saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
||||
qdict_del(options, "path");
|
||||
} else {
|
||||
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
||||
saddr->u.inet = g_new0(InetSocketAddress, 1);
|
||||
saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
|
||||
if (!qdict_get_try_str(options, "port")) {
|
||||
saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
||||
} else {
|
||||
saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
|
||||
}
|
||||
qdict_del(options, "host");
|
||||
qdict_del(options, "port");
|
||||
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
|
||||
if (!qemu_opt_get(s->socket_opts, "port")) {
|
||||
qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
*export = g_strdup(qdict_get_try_str(options, "export"));
|
||||
if (*export) {
|
||||
qdict_del(options, "export");
|
||||
}
|
||||
|
||||
return saddr;
|
||||
}
|
||||
|
||||
NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
|
||||
@@ -239,24 +231,26 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
|
||||
return &s->client;
|
||||
}
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs,
|
||||
SocketAddress *saddr,
|
||||
Error **errp)
|
||||
static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
int sock;
|
||||
|
||||
sock = socket_connect(saddr, errp, NULL, NULL);
|
||||
if (s->client.is_unix) {
|
||||
sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL);
|
||||
} else {
|
||||
sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL);
|
||||
if (sock >= 0) {
|
||||
socket_set_nodelay(sock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to establish connection */
|
||||
if (sock < 0) {
|
||||
logout("Failed to establish connection to NBD server\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!s->client.is_unix) {
|
||||
socket_set_nodelay(sock);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
@@ -266,19 +260,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
char *export = NULL;
|
||||
int result, sock;
|
||||
SocketAddress *saddr;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Pop the config into our state object. Exit if invalid. */
|
||||
saddr = nbd_config(s, options, &export, errp);
|
||||
if (!saddr) {
|
||||
nbd_config(s, options, &export, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* establish TCP connection, return error if it fails
|
||||
* TODO: Configurable retry-until-timeout behaviour.
|
||||
*/
|
||||
sock = nbd_establish_connection(bs, saddr, errp);
|
||||
qapi_free_SocketAddress(saddr);
|
||||
sock = nbd_establish_connection(bs, errp);
|
||||
if (sock < 0) {
|
||||
g_free(export);
|
||||
return sock;
|
||||
@@ -321,6 +315,9 @@ static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
static void nbd_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
qemu_opts_del(s->socket_opts);
|
||||
nbd_client_close(bs);
|
||||
}
|
||||
|
||||
|
||||
+10
-43
@@ -43,7 +43,6 @@ typedef struct NFSClient {
|
||||
int events;
|
||||
bool has_zero_init;
|
||||
AioContext *aio_context;
|
||||
blkcnt_t st_blocks;
|
||||
} NFSClient;
|
||||
|
||||
typedef struct NFSRPC {
|
||||
@@ -63,10 +62,11 @@ 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),
|
||||
false,
|
||||
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);
|
||||
(ev & POLLOUT) ? nfs_process_write : NULL,
|
||||
client);
|
||||
|
||||
}
|
||||
client->events = ev;
|
||||
@@ -241,8 +241,9 @@ static void nfs_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
|
||||
false, NULL, NULL, NULL);
|
||||
aio_set_fd_handler(client->aio_context,
|
||||
nfs_get_fd(client->context),
|
||||
NULL, NULL, NULL);
|
||||
client->events = 0;
|
||||
}
|
||||
|
||||
@@ -261,8 +262,9 @@ static void nfs_client_close(NFSClient *client)
|
||||
if (client->fh) {
|
||||
nfs_close(client->context, client->fh);
|
||||
}
|
||||
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
|
||||
false, NULL, NULL, NULL);
|
||||
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));
|
||||
@@ -372,7 +374,6 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||
}
|
||||
|
||||
ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
|
||||
client->st_blocks = st.st_blocks;
|
||||
client->has_zero_init = S_ISREG(st.st_mode);
|
||||
goto out;
|
||||
fail:
|
||||
@@ -463,11 +464,6 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
|
||||
NFSRPC task = {0};
|
||||
struct stat st;
|
||||
|
||||
if (bdrv_is_read_only(bs) &&
|
||||
!(bs->open_flags & BDRV_O_NOCACHE)) {
|
||||
return client->st_blocks * 512;
|
||||
}
|
||||
|
||||
task.st = &st;
|
||||
if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb,
|
||||
&task) != 0) {
|
||||
@@ -488,34 +484,6 @@ static int nfs_file_truncate(BlockDriverState *bs, int64_t offset)
|
||||
return nfs_ftruncate(client->context, client->fh, offset);
|
||||
}
|
||||
|
||||
/* Note that this will not re-establish a connection with the NFS server
|
||||
* - it is effectively a NOP. */
|
||||
static int nfs_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
NFSClient *client = state->bs->opaque;
|
||||
struct stat st;
|
||||
int ret = 0;
|
||||
|
||||
if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) {
|
||||
error_setg(errp, "Cannot open a read-only mount as read-write");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* Update cache for read-only reopens */
|
||||
if (!(state->flags & BDRV_O_RDWR)) {
|
||||
ret = nfs_fstat(client->context, client->fh, &st);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to fstat file: %s",
|
||||
nfs_get_error(client->context));
|
||||
return ret;
|
||||
}
|
||||
client->st_blocks = st.st_blocks;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_nfs = {
|
||||
.format_name = "nfs",
|
||||
.protocol_name = "nfs",
|
||||
@@ -531,7 +499,6 @@ static BlockDriver bdrv_nfs = {
|
||||
.bdrv_file_open = nfs_file_open,
|
||||
.bdrv_close = nfs_file_close,
|
||||
.bdrv_create = nfs_file_create,
|
||||
.bdrv_reopen_prepare = nfs_reopen_prepare,
|
||||
|
||||
.bdrv_co_readv = nfs_co_readv,
|
||||
.bdrv_co_writev = nfs_co_writev,
|
||||
|
||||
+20
-22
@@ -202,13 +202,13 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
to_allocate = (sector_num + *pnum + s->tracks - 1) / s->tracks - idx;
|
||||
space = to_allocate * s->tracks;
|
||||
if (s->data_end + space > bdrv_getlength(bs->file->bs) >> BDRV_SECTOR_BITS) {
|
||||
if (s->data_end + space > bdrv_getlength(bs->file) >> BDRV_SECTOR_BITS) {
|
||||
int ret;
|
||||
space += s->prealloc_size;
|
||||
if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) {
|
||||
ret = bdrv_write_zeroes(bs->file->bs, s->data_end, space, 0);
|
||||
ret = bdrv_write_zeroes(bs->file, s->data_end, space, 0);
|
||||
} else {
|
||||
ret = bdrv_truncate(bs->file->bs,
|
||||
ret = bdrv_truncate(bs->file,
|
||||
(s->data_end + space) << BDRV_SECTOR_BITS);
|
||||
}
|
||||
if (ret < 0) {
|
||||
@@ -220,7 +220,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
||||
s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier);
|
||||
s->data_end += s->tracks;
|
||||
bitmap_set(s->bat_dirty_bmap,
|
||||
bat_entry_off(idx + i) / s->bat_dirty_block, 1);
|
||||
bat_entry_off(idx) / s->bat_dirty_block, 1);
|
||||
}
|
||||
|
||||
return bat2sect(s, idx) + sector_num % s->tracks;
|
||||
@@ -244,8 +244,7 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs)
|
||||
if (off + to_write > s->header_size) {
|
||||
to_write = s->header_size - off;
|
||||
}
|
||||
ret = bdrv_pwrite(bs->file->bs, off, (uint8_t *)s->header + off,
|
||||
to_write);
|
||||
ret = bdrv_pwrite(bs->file, off, (uint8_t *)s->header + off, to_write);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
@@ -304,7 +303,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
|
||||
|
||||
ret = bdrv_co_writev(bs->file->bs, position, n, &hd_qiov);
|
||||
ret = bdrv_co_writev(bs->file, position, n, &hd_qiov);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -344,7 +343,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
|
||||
|
||||
ret = bdrv_co_readv(bs->file->bs, position, n, &hd_qiov);
|
||||
ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -370,7 +369,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
bool flush_bat = false;
|
||||
int cluster_size = s->tracks << BDRV_SECTOR_BITS;
|
||||
|
||||
size = bdrv_getlength(bs->file->bs);
|
||||
size = bdrv_getlength(bs->file);
|
||||
if (size < 0) {
|
||||
res->check_errors++;
|
||||
return size;
|
||||
@@ -425,7 +424,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
}
|
||||
|
||||
if (flush_bat) {
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, 0, s->header, s->header_size);
|
||||
ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size);
|
||||
if (ret < 0) {
|
||||
res->check_errors++;
|
||||
return ret;
|
||||
@@ -441,7 +440,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
size - res->image_end_offset);
|
||||
res->leaks += count;
|
||||
if (fix & BDRV_FIX_LEAKS) {
|
||||
ret = bdrv_truncate(bs->file->bs, res->image_end_offset);
|
||||
ret = bdrv_truncate(bs->file, res->image_end_offset);
|
||||
if (ret < 0) {
|
||||
res->check_errors++;
|
||||
return ret;
|
||||
@@ -477,7 +476,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
file = NULL;
|
||||
ret = bdrv_open(&file, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
@@ -547,13 +546,12 @@ static int parallels_probe(const uint8_t *buf, int buf_size,
|
||||
static int parallels_update_header(BlockDriverState *bs)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
unsigned size = MAX(bdrv_opt_mem_align(bs->file->bs),
|
||||
sizeof(ParallelsHeader));
|
||||
unsigned size = MAX(bdrv_opt_mem_align(bs->file), sizeof(ParallelsHeader));
|
||||
|
||||
if (size > s->header_size) {
|
||||
size = s->header_size;
|
||||
}
|
||||
return bdrv_pwrite_sync(bs->file->bs, 0, s->header, size);
|
||||
return bdrv_pwrite_sync(bs->file, 0, s->header, size);
|
||||
}
|
||||
|
||||
static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
@@ -566,7 +564,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error *local_err = NULL;
|
||||
char *buf;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, &ph, sizeof(ph));
|
||||
ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -605,8 +603,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
size = bat_entry_off(s->bat_size);
|
||||
s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs));
|
||||
s->header = qemu_try_blockalign(bs->file->bs, s->header_size);
|
||||
s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file));
|
||||
s->header = qemu_try_blockalign(bs->file, s->header_size);
|
||||
if (s->header == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
@@ -621,7 +619,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->header_size = size;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, s->header, s->header_size);
|
||||
ret = bdrv_pread(bs->file, 0, s->header, s->header_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -665,8 +663,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (local_err != NULL) {
|
||||
goto fail_options;
|
||||
}
|
||||
if (!bdrv_has_zero_init(bs->file->bs) ||
|
||||
bdrv_truncate(bs->file->bs, bdrv_getlength(bs->file->bs)) != 0) {
|
||||
if (!bdrv_has_zero_init(bs->file) ||
|
||||
bdrv_truncate(bs->file, bdrv_getlength(bs->file)) != 0) {
|
||||
s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
|
||||
}
|
||||
|
||||
@@ -709,7 +707,7 @@ static void parallels_close(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
if (bs->open_flags & BDRV_O_RDWR) {
|
||||
bdrv_truncate(bs->file->bs, s->data_end << BDRV_SECTOR_BITS);
|
||||
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
g_free(s->bat_dirty_bmap);
|
||||
|
||||
+27
-80
@@ -64,7 +64,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
|
||||
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||
info->detect_zeroes = bs->detect_zeroes;
|
||||
|
||||
if (bs->throttle_state) {
|
||||
if (bs->io_limits_enabled) {
|
||||
ThrottleConfig cfg;
|
||||
|
||||
throttle_group_get_config(bs, &cfg);
|
||||
@@ -110,8 +110,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
|
||||
qapi_free_BlockDeviceInfo(info);
|
||||
return NULL;
|
||||
}
|
||||
if (bs0->drv && bs0->backing) {
|
||||
bs0 = bs0->backing->bs;
|
||||
if (bs0->drv && bs0->backing_hd) {
|
||||
bs0 = bs0->backing_hd;
|
||||
(*p_image_info)->has_backing_image = true;
|
||||
p_image_info = &((*p_image_info)->backing_image);
|
||||
} else {
|
||||
@@ -301,17 +301,17 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
info->tray_open = blk_dev_is_tray_open(blk);
|
||||
}
|
||||
|
||||
if (blk_iostatus_is_enabled(blk)) {
|
||||
if (bdrv_iostatus_is_enabled(bs)) {
|
||||
info->has_io_status = true;
|
||||
info->io_status = blk_iostatus(blk);
|
||||
info->io_status = bs->iostatus;
|
||||
}
|
||||
|
||||
if (bs && !QLIST_EMPTY(&bs->dirty_bitmaps)) {
|
||||
if (!QLIST_EMPTY(&bs->dirty_bitmaps)) {
|
||||
info->has_dirty_bitmaps = true;
|
||||
info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs);
|
||||
}
|
||||
|
||||
if (bs && bs->drv) {
|
||||
if (bs->drv) {
|
||||
info->has_inserted = true;
|
||||
info->inserted = bdrv_block_device_info(bs, errp);
|
||||
if (info->inserted == NULL) {
|
||||
@@ -344,82 +344,27 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
||||
}
|
||||
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
if (bs->blk) {
|
||||
BlockAcctStats *stats = blk_get_stats(bs->blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
|
||||
s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (s->stats->has_idle_time_ns) {
|
||||
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
|
||||
}
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
|
||||
dev_stats->avg_rd_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||
s->stats->rd_bytes = bs->stats.nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = bs->stats.nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = bs->stats.nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = bs->stats.nr_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_merged = bs->stats.merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = bs->stats.merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->wr_highest_offset =
|
||||
bs->stats.wr_highest_sector * BDRV_SECTOR_SIZE;
|
||||
s->stats->flush_operations = bs->stats.nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
s->parent = bdrv_query_stats(bs->file->bs, query_backing);
|
||||
s->parent = bdrv_query_stats(bs->file, query_backing);
|
||||
}
|
||||
|
||||
if (query_backing && bs->backing) {
|
||||
if (query_backing && bs->backing_hd) {
|
||||
s->has_backing = true;
|
||||
s->backing = bdrv_query_stats(bs->backing->bs, query_backing);
|
||||
s->backing = bdrv_query_stats(bs->backing_hd, query_backing);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -436,9 +381,7 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
bdrv_query_info(blk, &info->value, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
g_free(info);
|
||||
qapi_free_BlockInfoList(head);
|
||||
return NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
*p_next = info;
|
||||
@@ -446,6 +389,10 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
}
|
||||
|
||||
return head;
|
||||
|
||||
err:
|
||||
qapi_free_BlockInfoList(head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
|
||||
+23
-26
@@ -100,7 +100,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret;
|
||||
QCowHeader header;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header));
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->l1_table_offset, s->l1_table,
|
||||
ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -205,7 +205,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
||||
s->l2_cache =
|
||||
qemu_try_blockalign(bs->file->bs,
|
||||
qemu_try_blockalign(bs->file,
|
||||
s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
if (s->l2_cache == NULL) {
|
||||
error_setg(errp, "Could not allocate L2 table cache");
|
||||
@@ -224,7 +224,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
ret = bdrv_pread(bs->file->bs, header.backing_file_offset,
|
||||
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
||||
bs->backing_file, len);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -369,13 +369,13 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new l2 entry */
|
||||
l2_offset = bdrv_getlength(bs->file->bs);
|
||||
l2_offset = bdrv_getlength(bs->file);
|
||||
/* round to cluster size */
|
||||
l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
|
||||
/* update the L1 entry */
|
||||
s->l1_table[l1_index] = l2_offset;
|
||||
tmp = cpu_to_be64(l2_offset);
|
||||
if (bdrv_pwrite_sync(bs->file->bs,
|
||||
if (bdrv_pwrite_sync(bs->file,
|
||||
s->l1_table_offset + l1_index * sizeof(tmp),
|
||||
&tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
@@ -405,12 +405,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
l2_table = s->l2_cache + (min_index << s->l2_bits);
|
||||
if (new_l2_table) {
|
||||
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
|
||||
if (bdrv_pwrite_sync(bs->file->bs, l2_offset, l2_table,
|
||||
if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
|
||||
s->l2_size * sizeof(uint64_t)) < 0)
|
||||
return 0;
|
||||
} else {
|
||||
if (bdrv_pread(bs->file->bs, l2_offset, l2_table,
|
||||
s->l2_size * sizeof(uint64_t)) !=
|
||||
if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
}
|
||||
@@ -431,21 +430,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
overwritten */
|
||||
if (decompress_cluster(bs, cluster_offset) < 0)
|
||||
return 0;
|
||||
cluster_offset = bdrv_getlength(bs->file->bs);
|
||||
cluster_offset = bdrv_getlength(bs->file);
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
/* write the cluster content */
|
||||
if (bdrv_pwrite(bs->file->bs, cluster_offset, s->cluster_cache,
|
||||
s->cluster_size) !=
|
||||
if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) !=
|
||||
s->cluster_size)
|
||||
return -1;
|
||||
} else {
|
||||
cluster_offset = bdrv_getlength(bs->file->bs);
|
||||
cluster_offset = bdrv_getlength(bs->file);
|
||||
if (allocate == 1) {
|
||||
/* round to cluster size */
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
bdrv_truncate(bs->file->bs, cluster_offset + s->cluster_size);
|
||||
bdrv_truncate(bs->file, cluster_offset + s->cluster_size);
|
||||
/* if encrypted, we must initialize the cluster
|
||||
content which won't be written */
|
||||
if (bs->encrypted &&
|
||||
@@ -465,8 +463,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_pwrite(bs->file->bs,
|
||||
cluster_offset + i * 512,
|
||||
if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
|
||||
s->cluster_data, 512) != 512)
|
||||
return -1;
|
||||
}
|
||||
@@ -480,7 +477,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_be64(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
if (bdrv_pwrite_sync(bs->file->bs, l2_offset + l2_index * sizeof(tmp),
|
||||
if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
|
||||
&tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
}
|
||||
@@ -549,7 +546,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
if (s->cluster_cache_offset != coffset) {
|
||||
csize = cluster_offset >> (63 - s->cluster_bits);
|
||||
csize &= (s->cluster_size - 1);
|
||||
ret = bdrv_pread(bs->file->bs, coffset, s->cluster_data, csize);
|
||||
ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize);
|
||||
if (ret != csize)
|
||||
return -1;
|
||||
if (decompress_buffer(s->cluster_cache, s->cluster_size,
|
||||
@@ -597,13 +594,13 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
if (!cluster_offset) {
|
||||
if (bs->backing) {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
hd_iov.iov_base = (void *)buf;
|
||||
hd_iov.iov_len = n * 512;
|
||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_readv(bs->backing->bs, sector_num,
|
||||
ret = bdrv_co_readv(bs->backing_hd, sector_num,
|
||||
n, &hd_qiov);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
@@ -628,7 +625,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
hd_iov.iov_len = n * 512;
|
||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_readv(bs->file->bs,
|
||||
ret = bdrv_co_readv(bs->file,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
n, &hd_qiov);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
@@ -730,7 +727,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
hd_iov.iov_len = n * 512;
|
||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_writev(bs->file->bs,
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
n, &hd_qiov);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
@@ -796,7 +793,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
qcow_bs = NULL;
|
||||
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
@@ -882,10 +879,10 @@ static int qcow_make_empty(BlockDriverState *bs)
|
||||
int ret;
|
||||
|
||||
memset(s->l1_table, 0, l1_length);
|
||||
if (bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, s->l1_table,
|
||||
if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table,
|
||||
l1_length) < 0)
|
||||
return -1;
|
||||
ret = bdrv_truncate(bs->file->bs, s->l1_table_offset + l1_length);
|
||||
ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -965,7 +962,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
ret = bdrv_pwrite(bs->file->bs, cluster_offset, out_buf, out_len);
|
||||
ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+12
-74
@@ -22,24 +22,16 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Needed for CONFIG_MADVISE */
|
||||
#include "config-host.h"
|
||||
|
||||
#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/osdep.h"
|
||||
#include "qcow2.h"
|
||||
#include "trace.h"
|
||||
|
||||
typedef struct Qcow2CachedTable {
|
||||
int64_t offset;
|
||||
bool dirty;
|
||||
uint64_t lru_counter;
|
||||
int ref;
|
||||
bool dirty;
|
||||
} Qcow2CachedTable;
|
||||
|
||||
struct Qcow2Cache {
|
||||
@@ -49,85 +41,34 @@ struct Qcow2Cache {
|
||||
bool depends_on_flush;
|
||||
void *table_array;
|
||||
uint64_t lru_counter;
|
||||
uint64_t cache_clean_lru_counter;
|
||||
};
|
||||
|
||||
static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs,
|
||||
Qcow2Cache *c, int table)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
return (uint8_t *) c->table_array + (size_t) table * s->cluster_size;
|
||||
}
|
||||
|
||||
static inline int qcow2_cache_get_table_idx(BlockDriverState *bs,
|
||||
Qcow2Cache *c, void *table)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array;
|
||||
int idx = table_offset / s->cluster_size;
|
||||
assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c,
|
||||
int i, int num_tables)
|
||||
{
|
||||
#if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
void *t = qcow2_cache_get_table_addr(bs, c, i);
|
||||
int align = getpagesize();
|
||||
size_t mem_size = (size_t) s->cluster_size * num_tables;
|
||||
size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t;
|
||||
size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align);
|
||||
if (length > 0) {
|
||||
qemu_madvise((uint8_t *) t + offset, length, QEMU_MADV_DONTNEED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool can_clean_entry(Qcow2Cache *c, int i)
|
||||
{
|
||||
Qcow2CachedTable *t = &c->entries[i];
|
||||
return t->ref == 0 && !t->dirty && t->offset != 0 &&
|
||||
t->lru_counter <= c->cache_clean_lru_counter;
|
||||
}
|
||||
|
||||
void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < c->size) {
|
||||
int to_clean = 0;
|
||||
|
||||
/* Skip the entries that we don't need to clean */
|
||||
while (i < c->size && !can_clean_entry(c, i)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
/* And count how many we can clean in a row */
|
||||
while (i < c->size && can_clean_entry(c, i)) {
|
||||
c->entries[i].offset = 0;
|
||||
c->entries[i].lru_counter = 0;
|
||||
i++;
|
||||
to_clean++;
|
||||
}
|
||||
|
||||
if (to_clean > 0) {
|
||||
qcow2_cache_table_release(bs, c, i - to_clean, to_clean);
|
||||
}
|
||||
}
|
||||
|
||||
c->cache_clean_lru_counter = c->lru_counter;
|
||||
}
|
||||
|
||||
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
Qcow2Cache *c;
|
||||
|
||||
c = g_new0(Qcow2Cache, 1);
|
||||
c->size = num_tables;
|
||||
c->entries = g_try_new0(Qcow2CachedTable, num_tables);
|
||||
c->table_array = qemu_try_blockalign(bs->file->bs,
|
||||
c->table_array = qemu_try_blockalign(bs->file,
|
||||
(size_t) num_tables * s->cluster_size);
|
||||
|
||||
if (!c->entries || !c->table_array) {
|
||||
@@ -172,7 +113,7 @@ static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
|
||||
|
||||
static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret = 0;
|
||||
|
||||
if (!c->entries[i].dirty || !c->entries[i].offset) {
|
||||
@@ -185,7 +126,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||
if (c->depends) {
|
||||
ret = qcow2_cache_flush_dependency(bs, c);
|
||||
} else if (c->depends_on_flush) {
|
||||
ret = bdrv_flush(bs->file->bs);
|
||||
ret = bdrv_flush(bs->file);
|
||||
if (ret >= 0) {
|
||||
c->depends_on_flush = false;
|
||||
}
|
||||
@@ -216,7 +157,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, c->entries[i].offset,
|
||||
ret = bdrv_pwrite(bs->file, c->entries[i].offset,
|
||||
qcow2_cache_get_table_addr(bs, c, i), s->cluster_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -229,7 +170,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||
|
||||
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int result = 0;
|
||||
int ret;
|
||||
int i;
|
||||
@@ -244,7 +185,7 @@ int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
ret = bdrv_flush(bs->file->bs);
|
||||
ret = bdrv_flush(bs->file);
|
||||
if (ret < 0) {
|
||||
result = ret;
|
||||
}
|
||||
@@ -296,8 +237,6 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
|
||||
c->entries[i].lru_counter = 0;
|
||||
}
|
||||
|
||||
qcow2_cache_table_release(bs, c, 0, c->size);
|
||||
|
||||
c->lru_counter = 0;
|
||||
|
||||
return 0;
|
||||
@@ -306,7 +245,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
|
||||
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
||||
uint64_t offset, void **table, bool read_from_disk)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i;
|
||||
int ret;
|
||||
int lookup_index;
|
||||
@@ -356,8 +295,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, offset,
|
||||
qcow2_cache_get_table_addr(bs, c, i),
|
||||
ret = bdrv_pread(bs->file, offset, qcow2_cache_get_table_addr(bs, c, i),
|
||||
s->cluster_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
||||
+50
-58
@@ -32,7 +32,7 @@
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
bool exact_size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int new_l1_size2, ret, i;
|
||||
uint64_t *new_l1_table;
|
||||
int64_t old_l1_table_offset, old_l1_size;
|
||||
@@ -72,7 +72,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
#endif
|
||||
|
||||
new_l1_size2 = sizeof(uint64_t) * new_l1_size;
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
new_l1_table = qemu_try_blockalign(bs->file,
|
||||
align_offset(new_l1_size2, 512));
|
||||
if (new_l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
@@ -105,8 +105,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
|
||||
for(i = 0; i < s->l1_size; i++)
|
||||
new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, new_l1_table_offset,
|
||||
new_l1_table, new_l1_size2);
|
||||
ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
for(i = 0; i < s->l1_size; i++)
|
||||
@@ -116,8 +115,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE);
|
||||
cpu_to_be32w((uint32_t*)data, new_l1_size);
|
||||
stq_be_p(data + 4, new_l1_table_offset);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_size),
|
||||
data, sizeof(data));
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -150,7 +148,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||
uint64_t **l2_table)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table);
|
||||
@@ -165,7 +163,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||
#define L1_ENTRIES_PER_SECTOR (512 / 8)
|
||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
|
||||
int l1_start_index;
|
||||
int i, ret;
|
||||
@@ -184,9 +182,8 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs,
|
||||
s->l1_table_offset + 8 * l1_start_index,
|
||||
buf, sizeof(buf));
|
||||
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index,
|
||||
buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -206,7 +203,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||
|
||||
static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t old_l2_offset;
|
||||
uint64_t *l2_table = NULL;
|
||||
int64_t l2_offset;
|
||||
@@ -312,7 +309,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||
if (!offset)
|
||||
return 0;
|
||||
|
||||
assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
|
||||
assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED);
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
||||
@@ -324,16 +321,14 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||
return i;
|
||||
}
|
||||
|
||||
static int count_contiguous_clusters_by_type(int nb_clusters,
|
||||
uint64_t *l2_table,
|
||||
int wanted_type)
|
||||
static int count_contiguous_free_clusters(int nb_clusters, uint64_t *l2_table)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
|
||||
|
||||
if (type != wanted_type) {
|
||||
if (type != QCOW2_CLUSTER_UNALLOCATED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -344,7 +339,7 @@ static int count_contiguous_clusters_by_type(int nb_clusters,
|
||||
/* The crypt function is compatible with the linux cryptoloop
|
||||
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
||||
supported */
|
||||
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
|
||||
int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, bool enc,
|
||||
Error **errp)
|
||||
@@ -392,7 +387,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
uint64_t cluster_offset,
|
||||
int n_start, int n_end)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov;
|
||||
int n, ret;
|
||||
@@ -445,8 +440,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
|
||||
ret = bdrv_co_writev(bs->file->bs, (cluster_offset >> 9) + n_start, n,
|
||||
&qiov);
|
||||
ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -475,7 +469,7 @@ out:
|
||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *cluster_offset)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int l2_index;
|
||||
uint64_t l1_index, l2_offset, *l2_table;
|
||||
int l1_bits, c;
|
||||
@@ -505,7 +499,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
*cluster_offset = 0;
|
||||
|
||||
/* seek to the l2 offset in the l1 table */
|
||||
/* seek the the l2 offset in the l1 table */
|
||||
|
||||
l1_index = offset >> l1_bits;
|
||||
if (l1_index >= s->l1_size) {
|
||||
@@ -556,14 +550,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
||||
QCOW2_CLUSTER_ZERO);
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
*cluster_offset = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
/* how many empty clusters ? */
|
||||
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
||||
QCOW2_CLUSTER_UNALLOCATED);
|
||||
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
|
||||
*cluster_offset = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
@@ -616,13 +609,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t **new_l2_table,
|
||||
int *new_l2_index)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int l2_index;
|
||||
uint64_t l1_index, l2_offset;
|
||||
uint64_t *l2_table = NULL;
|
||||
int ret;
|
||||
|
||||
/* seek to the l2 offset in the l1 table */
|
||||
/* seek the the l2 offset in the l1 table */
|
||||
|
||||
l1_index = offset >> (s->l2_bits + s->cluster_bits);
|
||||
if (l1_index >= s->l1_size) {
|
||||
@@ -690,7 +683,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
int compressed_size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret;
|
||||
uint64_t *l2_table;
|
||||
int64_t cluster_offset;
|
||||
@@ -735,7 +728,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
|
||||
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
if (r->nb_sectors == 0) {
|
||||
@@ -764,7 +757,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, *l2_table;
|
||||
uint64_t cluster_offset = m->alloc_offset;
|
||||
@@ -824,6 +817,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
|
||||
/*
|
||||
* If this was a COW, we need to decrease the refcount of the old cluster.
|
||||
* Also flush bs->file to get the right order for L2 and refcount update.
|
||||
*
|
||||
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
|
||||
* clusters), the next write will reuse them anyway.
|
||||
@@ -846,7 +840,7 @@ err:
|
||||
* write, but require COW to be performed (this includes yet unallocated space,
|
||||
* which must copy from the backing file)
|
||||
*/
|
||||
static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
|
||||
static int count_cow_clusters(BDRVQcowState *s, int nb_clusters,
|
||||
uint64_t *l2_table, int l2_index)
|
||||
{
|
||||
int i;
|
||||
@@ -892,7 +886,7 @@ out:
|
||||
static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *cur_bytes, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowL2Meta *old_alloc;
|
||||
uint64_t bytes = *cur_bytes;
|
||||
|
||||
@@ -965,7 +959,7 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||
static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index;
|
||||
uint64_t cluster_offset;
|
||||
uint64_t *l2_table;
|
||||
@@ -1073,7 +1067,7 @@ out:
|
||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, uint64_t *nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||
*host_offset, *nb_clusters);
|
||||
@@ -1121,7 +1115,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index;
|
||||
uint64_t *l2_table;
|
||||
uint64_t entry;
|
||||
@@ -1274,7 +1268,7 @@ fail:
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t start, remaining;
|
||||
uint64_t cluster_offset;
|
||||
uint64_t cur_bytes;
|
||||
@@ -1408,7 +1402,7 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||
|
||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret, csize, nb_csectors, sector_offset;
|
||||
uint64_t coffset;
|
||||
|
||||
@@ -1418,8 +1412,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
sector_offset = coffset & 511;
|
||||
csize = nb_csectors * 512 - sector_offset;
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
|
||||
ret = bdrv_read(bs->file->bs, coffset >> 9, s->cluster_data,
|
||||
nb_csectors);
|
||||
ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -1441,7 +1434,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t nb_clusters, enum qcow2_discard_type type,
|
||||
bool full_discard)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
int l2_index;
|
||||
int ret;
|
||||
@@ -1476,7 +1469,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
*/
|
||||
switch (qcow2_get_cluster_type(old_l2_entry)) {
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (full_discard || !bs->backing) {
|
||||
if (full_discard || !bs->backing_hd) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
@@ -1515,7 +1508,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t end_offset;
|
||||
uint64_t nb_clusters;
|
||||
int ret;
|
||||
@@ -1561,7 +1554,7 @@ fail:
|
||||
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
int l2_index;
|
||||
int ret;
|
||||
@@ -1598,7 +1591,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t nb_clusters;
|
||||
int ret;
|
||||
|
||||
@@ -1643,7 +1636,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
int64_t l1_entries,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bool is_active_l1 = (l1_table == s->l1_table);
|
||||
uint64_t *l2_table = NULL;
|
||||
int ret;
|
||||
@@ -1652,7 +1645,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
if (!is_active_l1) {
|
||||
/* inactive L2 tables require a buffer to be stored in when loading
|
||||
* them from disk */
|
||||
l2_table = qemu_try_blockalign(bs->file->bs, s->cluster_size);
|
||||
l2_table = qemu_try_blockalign(bs->file, s->cluster_size);
|
||||
if (l2_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -1686,8 +1679,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
(void **)&l2_table);
|
||||
} else {
|
||||
/* load inactive L2 tables from disk */
|
||||
ret = bdrv_read(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l2_table, s->cluster_sectors);
|
||||
ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l2_table, s->cluster_sectors);
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -1710,7 +1703,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
}
|
||||
|
||||
if (!preallocated) {
|
||||
if (!bs->backing) {
|
||||
if (!bs->backing_hd) {
|
||||
/* not backed; therefore we can simply deallocate the
|
||||
* cluster */
|
||||
l2_table[j] = 0;
|
||||
@@ -1761,7 +1754,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_write_zeroes(bs->file->bs, offset / BDRV_SECTOR_SIZE,
|
||||
ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
s->cluster_sectors, 0);
|
||||
if (ret < 0) {
|
||||
if (!preallocated) {
|
||||
@@ -1794,8 +1787,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_write(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l2_table, s->cluster_sectors);
|
||||
ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l2_table, s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1830,7 +1823,7 @@ fail:
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL;
|
||||
int64_t l1_entries = 0, visited_l1_entries = 0;
|
||||
int ret;
|
||||
@@ -1868,9 +1861,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
|
||||
l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
|
||||
|
||||
ret = bdrv_read(bs->file->bs,
|
||||
s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l1_table, l1_sectors);
|
||||
ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset /
|
||||
BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+54
-66
@@ -82,7 +82,7 @@ static Qcow2SetRefcountFunc *const set_refcount_funcs[] = {
|
||||
|
||||
int qcow2_refcount_init(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int refcount_table_size2, i;
|
||||
int ret;
|
||||
|
||||
@@ -101,7 +101,7 @@ int qcow2_refcount_init(BlockDriverState *bs)
|
||||
goto fail;
|
||||
}
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD);
|
||||
ret = bdrv_pread(bs->file->bs, s->refcount_table_offset,
|
||||
ret = bdrv_pread(bs->file, s->refcount_table_offset,
|
||||
s->refcount_table, refcount_table_size2);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -116,7 +116,7 @@ int qcow2_refcount_init(BlockDriverState *bs)
|
||||
|
||||
void qcow2_refcount_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
g_free(s->refcount_table);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ static int load_refcount_block(BlockDriverState *bs,
|
||||
int64_t refcount_block_offset,
|
||||
void **refcount_block)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
|
||||
@@ -231,7 +231,7 @@ static int load_refcount_block(BlockDriverState *bs,
|
||||
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||
uint64_t *refcount)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t refcount_table_index, block_index;
|
||||
int64_t refcount_block_offset;
|
||||
int ret;
|
||||
@@ -274,7 +274,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||
* Rounds the refcount table size up to avoid growing the table for each single
|
||||
* refcount block that is allocated.
|
||||
*/
|
||||
static unsigned int next_refcount_table_size(BDRVQcow2State *s,
|
||||
static unsigned int next_refcount_table_size(BDRVQcowState *s,
|
||||
unsigned int min_size)
|
||||
{
|
||||
unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1;
|
||||
@@ -290,7 +290,7 @@ static unsigned int next_refcount_table_size(BDRVQcow2State *s,
|
||||
|
||||
|
||||
/* Checks if two offsets are described by the same refcount block */
|
||||
static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a,
|
||||
static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
|
||||
uint64_t offset_b)
|
||||
{
|
||||
uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits);
|
||||
@@ -308,7 +308,7 @@ static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a,
|
||||
static int alloc_refcount_block(BlockDriverState *bs,
|
||||
int64_t cluster_index, void **refcount_block)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int refcount_table_index;
|
||||
int ret;
|
||||
|
||||
@@ -431,7 +431,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
if (refcount_table_index < s->refcount_table_size) {
|
||||
uint64_t data64 = cpu_to_be64(new_block);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs,
|
||||
ret = bdrv_pwrite_sync(bs->file,
|
||||
s->refcount_table_offset + refcount_table_index * sizeof(uint64_t),
|
||||
&data64, sizeof(data64));
|
||||
if (ret < 0) {
|
||||
@@ -535,7 +535,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
|
||||
/* Write refcount blocks to disk */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, meta_offset, new_blocks,
|
||||
ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks,
|
||||
blocks_clusters * s->cluster_size);
|
||||
g_free(new_blocks);
|
||||
new_blocks = NULL;
|
||||
@@ -549,7 +549,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, table_offset, new_table,
|
||||
ret = bdrv_pwrite_sync(bs->file, table_offset, new_table,
|
||||
table_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail_table;
|
||||
@@ -560,16 +560,12 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Hook up the new refcount table in the qcow2 header */
|
||||
struct QEMU_PACKED {
|
||||
uint64_t d64;
|
||||
uint32_t d32;
|
||||
} data;
|
||||
cpu_to_be64w(&data.d64, table_offset);
|
||||
cpu_to_be32w(&data.d32, table_clusters);
|
||||
uint8_t data[12];
|
||||
cpu_to_be64w((uint64_t*)data, table_offset);
|
||||
cpu_to_be32w((uint32_t*)(data + 8), table_clusters);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs,
|
||||
offsetof(QCowHeader, refcount_table_offset),
|
||||
&data, sizeof(data));
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset),
|
||||
data, sizeof(data));
|
||||
if (ret < 0) {
|
||||
goto fail_table;
|
||||
}
|
||||
@@ -609,7 +605,7 @@ fail_block:
|
||||
|
||||
void qcow2_process_discards(BlockDriverState *bs, int ret)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
Qcow2DiscardRegion *d, *next;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) {
|
||||
@@ -617,7 +613,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
|
||||
|
||||
/* Discard is optional, ignore the return value */
|
||||
if (ret >= 0) {
|
||||
bdrv_discard(bs->file->bs,
|
||||
bdrv_discard(bs->file,
|
||||
d->offset >> BDRV_SECTOR_BITS,
|
||||
d->bytes >> BDRV_SECTOR_BITS);
|
||||
}
|
||||
@@ -629,7 +625,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
|
||||
static void update_refcount_discard(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t length)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
Qcow2DiscardRegion *d, *p, *next;
|
||||
|
||||
QTAILQ_FOREACH(d, &s->discards, next) {
|
||||
@@ -686,7 +682,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
bool decrease,
|
||||
enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t start, last, cluster_offset;
|
||||
void *refcount_block = NULL;
|
||||
int64_t old_table_index = -1;
|
||||
@@ -797,7 +793,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
||||
uint64_t addend, bool decrease,
|
||||
enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend,
|
||||
@@ -819,7 +815,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
||||
/* return < 0 if error */
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t i, nb_clusters, refcount;
|
||||
int ret;
|
||||
|
||||
@@ -882,7 +878,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
|
||||
int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||
int64_t nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t cluster_index, refcount;
|
||||
uint64_t i;
|
||||
int ret;
|
||||
@@ -920,7 +916,7 @@ int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||
contiguous sectors. size must be <= cluster_size */
|
||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t offset;
|
||||
size_t free_in_cluster;
|
||||
int ret;
|
||||
@@ -953,17 +949,11 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
|
||||
if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) {
|
||||
offset = new_cluster;
|
||||
free_in_cluster = s->cluster_size;
|
||||
} else {
|
||||
free_in_cluster += s->cluster_size;
|
||||
}
|
||||
}
|
||||
|
||||
assert(offset);
|
||||
ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER);
|
||||
if (ret < 0) {
|
||||
offset = 0;
|
||||
}
|
||||
} while (ret == -EAGAIN);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -1002,7 +992,7 @@ void qcow2_free_clusters(BlockDriverState *bs,
|
||||
void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||
int nb_clusters, enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
switch (qcow2_get_cluster_type(l2_entry)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
@@ -1046,7 +1036,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size, int addend)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
|
||||
bool l1_allocated = false;
|
||||
int64_t old_offset, old_l2_offset;
|
||||
@@ -1072,7 +1062,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
}
|
||||
l1_allocated = true;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2);
|
||||
ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1225,8 +1215,7 @@ fail:
|
||||
cpu_to_be64s(&l1_table[i]);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, l1_table_offset,
|
||||
l1_table, l1_size2);
|
||||
ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, l1_size2);
|
||||
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
be64_to_cpus(&l1_table[i]);
|
||||
@@ -1244,7 +1233,7 @@ fail:
|
||||
/* refcount checking functions */
|
||||
|
||||
|
||||
static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries)
|
||||
static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries)
|
||||
{
|
||||
/* This assertion holds because there is no way we can address more than
|
||||
* 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because
|
||||
@@ -1267,7 +1256,7 @@ static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries)
|
||||
* refcount array buffer will be aligned to a cluster boundary, and the newly
|
||||
* allocated area will be zeroed.
|
||||
*/
|
||||
static int realloc_refcount_array(BDRVQcow2State *s, void **array,
|
||||
static int realloc_refcount_array(BDRVQcowState *s, void **array,
|
||||
int64_t *size, int64_t new_size)
|
||||
{
|
||||
int64_t old_byte_size, new_byte_size;
|
||||
@@ -1309,7 +1298,7 @@ static int realloc_refcount_array(BDRVQcow2State *s, void **array,
|
||||
/*
|
||||
* Increases the refcount for a range of clusters in a given refcount table.
|
||||
* This is used to construct a temporary refcount table out of L1 and L2 tables
|
||||
* which can be compared to the refcount table saved in the image.
|
||||
* which can be compared the the refcount table saved in the image.
|
||||
*
|
||||
* Modifies the number of errors in res.
|
||||
*/
|
||||
@@ -1319,7 +1308,7 @@ static int inc_refcounts(BlockDriverState *bs,
|
||||
int64_t *refcount_table_size,
|
||||
int64_t offset, int64_t size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t start, last, cluster_offset, k, refcount;
|
||||
int ret;
|
||||
|
||||
@@ -1372,7 +1361,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
int64_t *refcount_table_size, int64_t l2_offset,
|
||||
int flags)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table, l2_entry;
|
||||
uint64_t next_contiguous_offset = 0;
|
||||
int i, l2_size, nb_csectors, ret;
|
||||
@@ -1381,7 +1370,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
l2_size = s->l2_size * sizeof(uint64_t);
|
||||
l2_table = g_malloc(l2_size);
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, l2_size);
|
||||
ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n");
|
||||
res->check_errors++;
|
||||
@@ -1492,7 +1481,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size,
|
||||
int flags)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL, l2_offset, l1_size2;
|
||||
int i, ret;
|
||||
|
||||
@@ -1513,7 +1502,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
res->check_errors++;
|
||||
goto fail;
|
||||
}
|
||||
ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2);
|
||||
ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
|
||||
res->check_errors++;
|
||||
@@ -1569,7 +1558,7 @@ fail:
|
||||
static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size);
|
||||
int ret;
|
||||
uint64_t refcount;
|
||||
@@ -1611,7 +1600,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
}
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, l2_offset, l2_table,
|
||||
ret = bdrv_pread(bs->file, l2_offset, l2_table,
|
||||
s->l2_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: Could not read L2 table: %s\n",
|
||||
@@ -1663,8 +1652,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, l2_offset, l2_table,
|
||||
s->cluster_size);
|
||||
ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: Could not write L2 table: %s\n",
|
||||
strerror(-ret));
|
||||
@@ -1689,7 +1677,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix, bool *rebuild,
|
||||
void **refcount_table, int64_t *nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t i, size;
|
||||
int ret;
|
||||
|
||||
@@ -1719,11 +1707,11 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
goto resize_fail;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(bs->file->bs, offset + s->cluster_size);
|
||||
ret = bdrv_truncate(bs->file, offset + s->cluster_size);
|
||||
if (ret < 0) {
|
||||
goto resize_fail;
|
||||
}
|
||||
size = bdrv_getlength(bs->file->bs);
|
||||
size = bdrv_getlength(bs->file);
|
||||
if (size < 0) {
|
||||
ret = size;
|
||||
goto resize_fail;
|
||||
@@ -1792,7 +1780,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix, bool *rebuild,
|
||||
void **refcount_table, int64_t *nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t i;
|
||||
QCowSnapshot *sn;
|
||||
int ret;
|
||||
@@ -1856,7 +1844,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
int64_t *highest_cluster,
|
||||
void *refcount_table, int64_t nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t i;
|
||||
uint64_t refcount1, refcount2;
|
||||
int ret;
|
||||
@@ -1933,7 +1921,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs,
|
||||
int64_t *imrt_nb_clusters,
|
||||
int64_t *first_free_cluster)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t cluster = *first_free_cluster, i;
|
||||
bool first_gap = true;
|
||||
int contiguous_free_clusters;
|
||||
@@ -2003,7 +1991,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs,
|
||||
void **refcount_table,
|
||||
int64_t *nb_clusters)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0;
|
||||
int64_t refblock_offset, refblock_start, refblock_index;
|
||||
uint32_t reftable_size = 0;
|
||||
@@ -2097,7 +2085,7 @@ write_refblocks:
|
||||
on_disk_refblock = (void *)((char *) *refcount_table +
|
||||
refblock_index * s->cluster_size);
|
||||
|
||||
ret = bdrv_write(bs->file->bs, refblock_offset / BDRV_SECTOR_SIZE,
|
||||
ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE,
|
||||
on_disk_refblock, s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
|
||||
@@ -2146,7 +2134,7 @@ write_refblocks:
|
||||
}
|
||||
|
||||
assert(reftable_size < INT_MAX / sizeof(uint64_t));
|
||||
ret = bdrv_pwrite(bs->file->bs, reftable_offset, on_disk_reftable,
|
||||
ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable,
|
||||
reftable_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
|
||||
@@ -2158,8 +2146,8 @@ write_refblocks:
|
||||
reftable_offset);
|
||||
cpu_to_be32w(&reftable_offset_and_clusters.reftable_clusters,
|
||||
size_to_clusters(s, reftable_size * sizeof(uint64_t)));
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader,
|
||||
refcount_table_offset),
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader,
|
||||
refcount_table_offset),
|
||||
&reftable_offset_and_clusters,
|
||||
sizeof(reftable_offset_and_clusters));
|
||||
if (ret < 0) {
|
||||
@@ -2190,14 +2178,14 @@ fail:
|
||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
BdrvCheckResult pre_compare_res;
|
||||
int64_t size, highest_cluster, nb_clusters;
|
||||
void *refcount_table = NULL;
|
||||
bool rebuild = false;
|
||||
int ret;
|
||||
|
||||
size = bdrv_getlength(bs->file->bs);
|
||||
size = bdrv_getlength(bs->file);
|
||||
if (size < 0) {
|
||||
res->check_errors++;
|
||||
return size;
|
||||
@@ -2327,7 +2315,7 @@ fail:
|
||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int chk = s->overlap_check & ~ign;
|
||||
int i, j;
|
||||
|
||||
@@ -2406,7 +2394,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, l1_ofs, l1, l1_sz2);
|
||||
ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2);
|
||||
if (ret < 0) {
|
||||
g_free(l1);
|
||||
return ret;
|
||||
|
||||
+24
-26
@@ -29,7 +29,7 @@
|
||||
|
||||
void qcow2_free_snapshots(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
@@ -43,7 +43,7 @@ void qcow2_free_snapshots(BlockDriverState *bs)
|
||||
|
||||
int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshotHeader h;
|
||||
QCowSnapshotExtraData extra;
|
||||
QCowSnapshot *sn;
|
||||
@@ -64,7 +64,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
/* Read statically sized part of the snapshot header */
|
||||
offset = align_offset(offset, 8);
|
||||
ret = bdrv_pread(bs->file->bs, offset, &h, sizeof(h));
|
||||
ret = bdrv_pread(bs->file, offset, &h, sizeof(h));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
name_size = be16_to_cpu(h.name_size);
|
||||
|
||||
/* Read extra data */
|
||||
ret = bdrv_pread(bs->file->bs, offset, &extra,
|
||||
ret = bdrv_pread(bs->file, offset, &extra,
|
||||
MIN(sizeof(extra), extra_data_size));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -102,7 +102,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
|
||||
/* Read snapshot ID */
|
||||
sn->id_str = g_malloc(id_str_size + 1);
|
||||
ret = bdrv_pread(bs->file->bs, offset, sn->id_str, id_str_size);
|
||||
ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -111,7 +111,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
|
||||
/* Read snapshot name */
|
||||
sn->name = g_malloc(name_size + 1);
|
||||
ret = bdrv_pread(bs->file->bs, offset, sn->name, name_size);
|
||||
ret = bdrv_pread(bs->file, offset, sn->name, name_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ fail:
|
||||
/* add at the end of the file a new list of snapshots */
|
||||
static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
QCowSnapshotHeader h;
|
||||
QCowSnapshotExtraData extra;
|
||||
@@ -214,25 +214,25 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
h.name_size = cpu_to_be16(name_size);
|
||||
offset = align_offset(offset, 8);
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, &h, sizeof(h));
|
||||
ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += sizeof(h);
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, &extra, sizeof(extra));
|
||||
ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += sizeof(extra);
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, sn->id_str, id_str_size);
|
||||
ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += id_str_size;
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, sn->name, name_size);
|
||||
ret = bdrv_pwrite(bs->file, offset, sn->name, name_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -254,7 +254,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots);
|
||||
header_data.snapshots_offset = cpu_to_be64(snapshots_offset);
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, nb_snapshots),
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
|
||||
&header_data, sizeof(header_data));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -278,7 +278,7 @@ fail:
|
||||
static void find_new_snapshot_id(BlockDriverState *bs,
|
||||
char *id_str, int id_str_size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
int i;
|
||||
unsigned long id, id_max = 0;
|
||||
@@ -296,7 +296,7 @@ static int find_snapshot_by_id_and_name(BlockDriverState *bs,
|
||||
const char *id,
|
||||
const char *name)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
if (id && name) {
|
||||
@@ -338,7 +338,7 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs,
|
||||
/* if no id is provided, a new one is constructed */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *new_snapshot_list = NULL;
|
||||
QCowSnapshot *old_snapshot_list = NULL;
|
||||
QCowSnapshot sn1, *sn = &sn1;
|
||||
@@ -396,7 +396,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, sn->l1_table_offset, l1_table,
|
||||
ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -461,7 +461,7 @@ fail:
|
||||
/* copy the snapshot 'snapshot_name' into the current disk image */
|
||||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
int i, snapshot_index;
|
||||
int cur_l1_bytes, sn_l1_bytes;
|
||||
@@ -509,8 +509,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, sn->l1_table_offset,
|
||||
sn_l1_table, sn_l1_bytes);
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -527,7 +526,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, sn_l1_table,
|
||||
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table,
|
||||
cur_l1_bytes);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -588,7 +587,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot sn;
|
||||
int snapshot_index, ret;
|
||||
|
||||
@@ -651,7 +650,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
|
||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QEMUSnapshotInfo *sn_tab, *sn_info;
|
||||
QCowSnapshot *sn;
|
||||
int i;
|
||||
@@ -684,7 +683,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
int i, snapshot_index;
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
uint64_t *new_l1_table;
|
||||
int new_l1_bytes;
|
||||
@@ -707,14 +706,13 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
return -EFBIG;
|
||||
}
|
||||
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
new_l1_table = qemu_try_blockalign(bs->file,
|
||||
align_offset(new_l1_bytes, 512));
|
||||
if (new_l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, sn->l1_table_offset,
|
||||
new_l1_table, new_l1_bytes);
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to read l1 table for snapshot");
|
||||
qemu_vfree(new_l1_table);
|
||||
|
||||
+197
-403
File diff suppressed because it is too large.
Load diff
+14
-16
@@ -26,7 +26,7 @@
|
||||
#define BLOCK_QCOW2_H
|
||||
|
||||
#include "crypto/cipher.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/coroutine.h"
|
||||
|
||||
//#define DEBUG_ALLOC
|
||||
//#define DEBUG_ALLOC2
|
||||
@@ -96,7 +96,6 @@
|
||||
#define QCOW2_OPT_CACHE_SIZE "cache-size"
|
||||
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
|
||||
#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size"
|
||||
#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval"
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
@@ -222,7 +221,7 @@ typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array,
|
||||
typedef void Qcow2SetRefcountFunc(void *refcount_array,
|
||||
uint64_t index, uint64_t value);
|
||||
|
||||
typedef struct BDRVQcow2State {
|
||||
typedef struct BDRVQcowState {
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
@@ -240,8 +239,6 @@ typedef struct BDRVQcow2State {
|
||||
|
||||
Qcow2Cache* l2_table_cache;
|
||||
Qcow2Cache* refcount_block_cache;
|
||||
QEMUTimer *cache_clean_timer;
|
||||
unsigned cache_clean_interval;
|
||||
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
@@ -293,7 +290,9 @@ typedef struct BDRVQcow2State {
|
||||
* override) */
|
||||
char *image_backing_file;
|
||||
char *image_backing_format;
|
||||
} BDRVQcow2State;
|
||||
} BDRVQcowState;
|
||||
|
||||
struct QCowAIOCB;
|
||||
|
||||
typedef struct Qcow2COWRegion {
|
||||
/**
|
||||
@@ -403,28 +402,28 @@ typedef enum QCow2MetadataOverlap {
|
||||
|
||||
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
|
||||
|
||||
static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset)
|
||||
static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset)
|
||||
{
|
||||
return offset & ~(s->cluster_size - 1);
|
||||
}
|
||||
|
||||
static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset)
|
||||
static inline int64_t offset_into_cluster(BDRVQcowState *s, int64_t offset)
|
||||
{
|
||||
return offset & (s->cluster_size - 1);
|
||||
}
|
||||
|
||||
static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size)
|
||||
static inline uint64_t size_to_clusters(BDRVQcowState *s, uint64_t size)
|
||||
{
|
||||
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
|
||||
}
|
||||
|
||||
static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size)
|
||||
static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size)
|
||||
{
|
||||
int shift = s->cluster_bits + s->l2_bits;
|
||||
return (size + (1ULL << shift) - 1) >> shift;
|
||||
}
|
||||
|
||||
static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset)
|
||||
static inline int offset_to_l2_index(BDRVQcowState *s, int64_t offset)
|
||||
{
|
||||
return (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
}
|
||||
@@ -435,12 +434,12 @@ static inline int64_t align_offset(int64_t offset, int n)
|
||||
return offset;
|
||||
}
|
||||
|
||||
static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
|
||||
static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
|
||||
{
|
||||
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||
}
|
||||
|
||||
static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
|
||||
static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s)
|
||||
{
|
||||
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
||||
}
|
||||
@@ -459,7 +458,7 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||
}
|
||||
|
||||
/* Check whether refcounts are eager or lazy */
|
||||
static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s)
|
||||
static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
|
||||
{
|
||||
return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY);
|
||||
}
|
||||
@@ -535,7 +534,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
|
||||
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
|
||||
int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, bool enc, Error **errp);
|
||||
|
||||
@@ -582,7 +581,6 @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
|
||||
Qcow2Cache *dependency);
|
||||
void qcow2_cache_depends_on_flush(Qcow2Cache *c);
|
||||
|
||||
void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c);
|
||||
int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c);
|
||||
|
||||
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
|
||||
|
||||
+2
-2
@@ -63,7 +63,7 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size,
|
||||
|
||||
qemu_iovec_init_external(qiov, &read_table_cb->iov, 1);
|
||||
bdrv_aio_readv(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, qiov,
|
||||
bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov,
|
||||
qiov->size / BDRV_SECTOR_SIZE,
|
||||
qed_read_table_cb, read_table_cb);
|
||||
}
|
||||
@@ -152,7 +152,7 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
/* Adjust for offset into table */
|
||||
offset += start * sizeof(uint64_t);
|
||||
|
||||
bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE,
|
||||
bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&write_table_cb->qiov,
|
||||
write_table_cb->qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_write_table_cb, write_table_cb);
|
||||
|
||||
+30
-36
@@ -82,7 +82,7 @@ int qed_write_header_sync(BDRVQEDState *s)
|
||||
int ret;
|
||||
|
||||
qed_header_cpu_to_le(&s->header, &le);
|
||||
ret = bdrv_pwrite(s->bs->file->bs, 0, &le, sizeof(le));
|
||||
ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le));
|
||||
if (ret != sizeof(le)) {
|
||||
return ret;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ static void qed_write_header_read_cb(void *opaque, int ret)
|
||||
/* Update header */
|
||||
qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf);
|
||||
|
||||
bdrv_aio_writev(s->bs->file->bs, 0, &write_header_cb->qiov,
|
||||
bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov,
|
||||
write_header_cb->nsectors, qed_write_header_cb,
|
||||
write_header_cb);
|
||||
}
|
||||
@@ -152,7 +152,7 @@ static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb,
|
||||
write_header_cb->iov.iov_len = len;
|
||||
qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1);
|
||||
|
||||
bdrv_aio_readv(s->bs->file->bs, 0, &write_header_cb->qiov, nsectors,
|
||||
bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors,
|
||||
qed_write_header_read_cb, write_header_cb);
|
||||
}
|
||||
|
||||
@@ -354,6 +354,12 @@ static void qed_cancel_need_check_timer(BDRVQEDState *s)
|
||||
timer_del(s->need_check_timer);
|
||||
}
|
||||
|
||||
static void bdrv_qed_rebind(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
s->bs = bs;
|
||||
}
|
||||
|
||||
static void bdrv_qed_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
@@ -375,18 +381,6 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_qed_drain(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
/* Cancel timer and start doing I/O that were meant to happen as if it
|
||||
* fired, that way we get bdrv_drain() taking care of the ongoing requests
|
||||
* correctly. */
|
||||
qed_cancel_need_check_timer(s);
|
||||
qed_plug_allocating_write_reqs(s);
|
||||
bdrv_aio_flush(s->bs, qed_clear_need_check, s);
|
||||
}
|
||||
|
||||
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -398,7 +392,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->bs = bs;
|
||||
QSIMPLEQ_INIT(&s->allocating_write_reqs);
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, &le_header, sizeof(le_header));
|
||||
ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -422,7 +416,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Round down file size to the last cluster */
|
||||
file_size = bdrv_getlength(bs->file->bs);
|
||||
file_size = bdrv_getlength(bs->file);
|
||||
if (file_size < 0) {
|
||||
return file_size;
|
||||
}
|
||||
@@ -458,7 +452,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = qed_read_string(bs->file->bs, s->header.backing_filename_offset,
|
||||
ret = qed_read_string(bs->file, s->header.backing_filename_offset,
|
||||
s->header.backing_filename_size, bs->backing_file,
|
||||
sizeof(bs->backing_file));
|
||||
if (ret < 0) {
|
||||
@@ -477,7 +471,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* feature is no longer valid.
|
||||
*/
|
||||
if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 &&
|
||||
!bdrv_is_read_only(bs->file->bs) && !(flags & BDRV_O_INCOMING)) {
|
||||
!bdrv_is_read_only(bs->file) && !(flags & BDRV_O_INCOMING)) {
|
||||
s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK;
|
||||
|
||||
ret = qed_write_header_sync(s);
|
||||
@@ -486,7 +480,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* From here on only known autoclear feature bits are valid */
|
||||
bdrv_flush(bs->file->bs);
|
||||
bdrv_flush(bs->file);
|
||||
}
|
||||
|
||||
s->l1_table = qed_alloc_table(s);
|
||||
@@ -504,7 +498,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* potentially inconsistent images to be opened read-only. This can
|
||||
* aid data recovery from an otherwise inconsistent image.
|
||||
*/
|
||||
if (!bdrv_is_read_only(bs->file->bs) &&
|
||||
if (!bdrv_is_read_only(bs->file) &&
|
||||
!(flags & BDRV_O_INCOMING)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
@@ -547,7 +541,7 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
||||
bdrv_qed_detach_aio_context(bs);
|
||||
|
||||
/* Ensure writes reach stable storage */
|
||||
bdrv_flush(bs->file->bs);
|
||||
bdrv_flush(bs->file);
|
||||
|
||||
/* Clean shutdown, no check required on next open */
|
||||
if (s->header.features & QED_F_NEED_CHECK) {
|
||||
@@ -589,7 +583,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -778,8 +772,8 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
/* If there is a backing file, get its length. Treat the absence of a
|
||||
* backing file like a zero length backing file.
|
||||
*/
|
||||
if (s->bs->backing) {
|
||||
int64_t l = bdrv_getlength(s->bs->backing->bs);
|
||||
if (s->bs->backing_hd) {
|
||||
int64_t l = bdrv_getlength(s->bs->backing_hd);
|
||||
if (l < 0) {
|
||||
cb(opaque, l);
|
||||
return;
|
||||
@@ -808,7 +802,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
qemu_iovec_concat(*backing_qiov, qiov, 0, size);
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
bdrv_aio_readv(s->bs->backing->bs, pos / BDRV_SECTOR_SIZE,
|
||||
bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
|
||||
*backing_qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
|
||||
}
|
||||
|
||||
@@ -845,7 +839,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE);
|
||||
bdrv_aio_writev(s->bs->file->bs, copy_cb->offset / BDRV_SECTOR_SIZE,
|
||||
bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE,
|
||||
©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_copy_from_backing_file_cb, copy_cb);
|
||||
}
|
||||
@@ -1061,7 +1055,7 @@ static void qed_aio_write_flush_before_l2_update(void *opaque, int ret)
|
||||
QEDAIOCB *acb = opaque;
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
|
||||
if (!bdrv_aio_flush(s->bs->file->bs, qed_aio_write_l2_update_cb, opaque)) {
|
||||
if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update_cb, opaque)) {
|
||||
qed_aio_complete(acb, -EIO);
|
||||
}
|
||||
}
|
||||
@@ -1087,7 +1081,7 @@ static void qed_aio_write_main(void *opaque, int ret)
|
||||
if (acb->find_cluster_ret == QED_CLUSTER_FOUND) {
|
||||
next_fn = qed_aio_next_io;
|
||||
} else {
|
||||
if (s->bs->backing) {
|
||||
if (s->bs->backing_hd) {
|
||||
next_fn = qed_aio_write_flush_before_l2_update;
|
||||
} else {
|
||||
next_fn = qed_aio_write_l2_update_cb;
|
||||
@@ -1095,7 +1089,7 @@ static void qed_aio_write_main(void *opaque, int ret)
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
|
||||
bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE,
|
||||
bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
|
||||
next_fn, acb);
|
||||
}
|
||||
@@ -1145,7 +1139,7 @@ static void qed_aio_write_prefill(void *opaque, int ret)
|
||||
static bool qed_should_set_need_check(BDRVQEDState *s)
|
||||
{
|
||||
/* The flush before L2 update path ensures consistency */
|
||||
if (s->bs->backing) {
|
||||
if (s->bs->backing_hd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1327,7 +1321,7 @@ static void qed_aio_read_data(void *opaque, int ret,
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
bdrv_aio_readv(bs->file->bs, offset / BDRV_SECTOR_SIZE,
|
||||
bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_aio_next_io, acb);
|
||||
return;
|
||||
@@ -1449,7 +1443,7 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
|
||||
struct iovec iov;
|
||||
|
||||
/* Refuse if there are untouched backing file sectors */
|
||||
if (bs->backing) {
|
||||
if (bs->backing_hd) {
|
||||
if (qed_offset_into_cluster(s, sector_num * BDRV_SECTOR_SIZE) != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -1586,7 +1580,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Write new header */
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, 0, buffer, buffer_len);
|
||||
ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len);
|
||||
g_free(buffer);
|
||||
if (ret == 0) {
|
||||
memcpy(&s->header, &new_header, sizeof(new_header));
|
||||
@@ -1602,7 +1596,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
|
||||
bdrv_qed_close(bs);
|
||||
|
||||
bdrv_invalidate_cache(bs->file->bs, &local_err);
|
||||
bdrv_invalidate_cache(bs->file, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -1670,6 +1664,7 @@ static BlockDriver bdrv_qed = {
|
||||
.supports_backing = true,
|
||||
|
||||
.bdrv_probe = bdrv_qed_probe,
|
||||
.bdrv_rebind = bdrv_qed_rebind,
|
||||
.bdrv_open = bdrv_qed_open,
|
||||
.bdrv_close = bdrv_qed_close,
|
||||
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
||||
@@ -1688,7 +1683,6 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_check = bdrv_qed_check,
|
||||
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
|
||||
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
|
||||
.bdrv_drain = bdrv_qed_drain,
|
||||
};
|
||||
|
||||
static void bdrv_qed_init(void)
|
||||
|
||||
+40
-45
@@ -64,7 +64,7 @@ typedef struct QuorumVotes {
|
||||
|
||||
/* the following structure holds the state of one quorum instance */
|
||||
typedef struct BDRVQuorumState {
|
||||
BdrvChild **children; /* children BlockDriverStates */
|
||||
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.
|
||||
@@ -336,7 +336,7 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
quorum_report_bad(acb, s->children[item->index]->bs->node_name, 0);
|
||||
quorum_report_bad(acb, s->bs[item->index]->node_name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,9 +369,8 @@ static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb,
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
bdrv_aio_writev(s->children[item->index]->bs, acb->sector_num,
|
||||
acb->qiov, acb->nb_sectors, quorum_rewrite_aio_cb,
|
||||
acb);
|
||||
bdrv_aio_writev(s->bs[item->index], acb->sector_num, acb->qiov,
|
||||
acb->nb_sectors, quorum_rewrite_aio_cb, acb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,15 +639,14 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].buf = qemu_blockalign(s->children[i]->bs, acb->qiov->size);
|
||||
acb->qcrs[i].buf = qemu_blockalign(s->bs[i], acb->qiov->size);
|
||||
qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->qcrs[i].qiov, acb->qiov, acb->qcrs[i].buf);
|
||||
}
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i]->bs, acb->sector_num,
|
||||
&acb->qcrs[i].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[i]);
|
||||
bdrv_aio_readv(s->bs[i], acb->sector_num, &acb->qcrs[i].qiov,
|
||||
acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]);
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
@@ -658,15 +656,14 @@ static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
|
||||
acb->qcrs[acb->child_iter].buf =
|
||||
qemu_blockalign(s->children[acb->child_iter]->bs, acb->qiov->size);
|
||||
acb->qcrs[acb->child_iter].buf = qemu_blockalign(s->bs[acb->child_iter],
|
||||
acb->qiov->size);
|
||||
qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov,
|
||||
acb->qcrs[acb->child_iter].buf);
|
||||
acb->qcrs[acb->child_iter].aiocb =
|
||||
bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num,
|
||||
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
||||
bdrv_aio_readv(s->bs[acb->child_iter], acb->sector_num,
|
||||
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
@@ -705,8 +702,8 @@ static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].aiocb = bdrv_aio_writev(s->children[i]->bs, sector_num,
|
||||
qiov, nb_sectors, &quorum_aio_cb,
|
||||
acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov,
|
||||
nb_sectors, &quorum_aio_cb,
|
||||
&acb->qcrs[i]);
|
||||
}
|
||||
|
||||
@@ -720,12 +717,12 @@ static int64_t quorum_getlength(BlockDriverState *bs)
|
||||
int i;
|
||||
|
||||
/* check that all file have the same length */
|
||||
result = bdrv_getlength(s->children[0]->bs);
|
||||
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->children[i]->bs);
|
||||
int64_t value = bdrv_getlength(s->bs[i]);
|
||||
if (value < 0) {
|
||||
return value;
|
||||
}
|
||||
@@ -744,7 +741,7 @@ static void quorum_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_invalidate_cache(s->children[i]->bs, &local_err);
|
||||
bdrv_invalidate_cache(s->bs[i], &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -765,7 +762,7 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
||||
error_votes.compare = quorum_64bits_compare;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
result = bdrv_co_flush(s->children[i]->bs);
|
||||
result = bdrv_co_flush(s->bs[i]);
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
}
|
||||
@@ -785,7 +782,7 @@ static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs,
|
||||
bool perm = bdrv_recurse_is_first_non_filter(s->bs[i],
|
||||
candidate);
|
||||
if (perm) {
|
||||
return true;
|
||||
@@ -892,12 +889,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = parse_read_pattern(qemu_opt_get(opts, QUORUM_OPT_READ_PATTERN));
|
||||
if (ret < 0) {
|
||||
error_setg(&local_err, "Please set read-pattern as fifo or quorum");
|
||||
@@ -906,6 +897,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->read_pattern = ret;
|
||||
|
||||
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
||||
/* and validate it against s->num_children */
|
||||
ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* is the driver in blkverify mode */
|
||||
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||
s->num_children == 2 && s->threshold == 2) {
|
||||
@@ -925,8 +922,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate the children array */
|
||||
s->children = g_new0(BdrvChild *, s->num_children);
|
||||
/* allocate the children BlockDriverState array */
|
||||
s->bs = g_new0(BlockDriverState *, s->num_children);
|
||||
opened = g_new0(bool, s->num_children);
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
@@ -934,10 +931,9 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = snprintf(indexstr, 32, "children.%d", i);
|
||||
assert(ret < 32);
|
||||
|
||||
s->children[i] = bdrv_open_child(NULL, options, indexstr, bs,
|
||||
&child_format, false, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs,
|
||||
&child_format, false, &local_err);
|
||||
if (ret < 0) {
|
||||
goto close_exit;
|
||||
}
|
||||
|
||||
@@ -953,9 +949,9 @@ close_exit:
|
||||
if (!opened[i]) {
|
||||
continue;
|
||||
}
|
||||
bdrv_unref_child(bs, s->children[i]);
|
||||
bdrv_unref(s->bs[i]);
|
||||
}
|
||||
g_free(s->children);
|
||||
g_free(s->bs);
|
||||
g_free(opened);
|
||||
exit:
|
||||
qemu_opts_del(opts);
|
||||
@@ -972,10 +968,10 @@ static void quorum_close(BlockDriverState *bs)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_unref_child(bs, s->children[i]);
|
||||
bdrv_unref(s->bs[i]);
|
||||
}
|
||||
|
||||
g_free(s->children);
|
||||
g_free(s->bs);
|
||||
}
|
||||
|
||||
static void quorum_detach_aio_context(BlockDriverState *bs)
|
||||
@@ -984,7 +980,7 @@ static void quorum_detach_aio_context(BlockDriverState *bs)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_detach_aio_context(s->children[i]->bs);
|
||||
bdrv_detach_aio_context(s->bs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -995,7 +991,7 @@ static void quorum_attach_aio_context(BlockDriverState *bs,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_attach_aio_context(s->children[i]->bs, new_context);
|
||||
bdrv_attach_aio_context(s->bs[i], new_context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1007,17 +1003,16 @@ static void quorum_refresh_filename(BlockDriverState *bs)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_refresh_filename(s->children[i]->bs);
|
||||
if (!s->children[i]->bs->full_open_options) {
|
||||
bdrv_refresh_filename(s->bs[i]);
|
||||
if (!s->bs[i]->full_open_options) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
children = qlist_new();
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
QINCREF(s->children[i]->bs->full_open_options);
|
||||
qlist_append_obj(children,
|
||||
QOBJECT(s->children[i]->bs->full_open_options));
|
||||
QINCREF(s->bs[i]->full_open_options);
|
||||
qlist_append_obj(children, QOBJECT(s->bs[i]->full_open_options));
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
|
||||
+254
-49
@@ -127,6 +127,11 @@ do { \
|
||||
|
||||
#define FTYPE_FILE 0
|
||||
#define FTYPE_CD 1
|
||||
#define FTYPE_FD 2
|
||||
|
||||
/* if the FD is not accessed during that time (in ns), we try to
|
||||
reopen it to see if the disk has been changed */
|
||||
#define FD_OPEN_TIMEOUT (1000000000)
|
||||
|
||||
#define MAX_BLOCKSIZE 4096
|
||||
|
||||
@@ -136,6 +141,13 @@ typedef struct BDRVRawState {
|
||||
int open_flags;
|
||||
size_t buf_align;
|
||||
|
||||
#if defined(__linux__)
|
||||
/* linux floppy specific */
|
||||
int64_t fd_open_time;
|
||||
int64_t fd_error_time;
|
||||
int fd_got_error;
|
||||
int fd_media_changed;
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
int use_aio;
|
||||
void *aio_ctx;
|
||||
@@ -507,16 +519,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
"future QEMU versions.\n",
|
||||
bs->filename);
|
||||
}
|
||||
#else
|
||||
if (bdrv_flags & BDRV_O_NATIVE_AIO) {
|
||||
error_printf("WARNING: aio=native was specified for '%s', but "
|
||||
"is not supported in this build. Falling back to "
|
||||
"aio=threads.\n"
|
||||
" This will become an error condition in "
|
||||
"future QEMU versions.\n",
|
||||
bs->filename);
|
||||
}
|
||||
#endif /* !defined(CONFIG_LINUX_AIO) */
|
||||
#endif
|
||||
|
||||
s->has_discard = true;
|
||||
s->has_write_zeroes = true;
|
||||
@@ -623,7 +626,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->type == FTYPE_CD) {
|
||||
if (s->type == FTYPE_FD || s->type == FTYPE_CD) {
|
||||
raw_s->open_flags |= O_NONBLOCK;
|
||||
}
|
||||
|
||||
@@ -667,17 +670,11 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
|
||||
/* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
|
||||
if (raw_s->fd == -1) {
|
||||
const char *normalized_filename = state->bs->filename;
|
||||
ret = raw_normalize_devicepath(&normalized_filename);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not normalize device path");
|
||||
} else {
|
||||
assert(!(raw_s->open_flags & O_CREAT));
|
||||
raw_s->fd = qemu_open(normalized_filename, raw_s->open_flags);
|
||||
if (raw_s->fd == -1) {
|
||||
error_setg_errno(errp, errno, "Could not reopen file");
|
||||
ret = -1;
|
||||
}
|
||||
assert(!(raw_s->open_flags & O_CREAT));
|
||||
raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags);
|
||||
if (raw_s->fd == -1) {
|
||||
error_setg_errno(errp, errno, "Could not reopen file");
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +780,7 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
struct hd_geometry ioctl_geo = {0};
|
||||
uint32_t blksize;
|
||||
|
||||
/* If DASD, get its geometry */
|
||||
if (check_for_dasd(s->fd) < 0) {
|
||||
@@ -802,6 +800,12 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
|
||||
}
|
||||
geo->heads = ioctl_geo.heads;
|
||||
geo->sectors = ioctl_geo.sectors;
|
||||
if (!probe_physical_blocksize(s->fd, &blksize)) {
|
||||
/* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */
|
||||
geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE)
|
||||
/ (geo->heads * geo->sectors);
|
||||
return 0;
|
||||
}
|
||||
geo->cylinders = ioctl_geo.cylinders;
|
||||
|
||||
return 0;
|
||||
@@ -1249,7 +1253,7 @@ static int aio_worker(void *arg)
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(aiocb);
|
||||
g_slice_free(RawPosixAIOData, aiocb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1257,7 +1261,7 @@ static int paio_submit_co(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
int type)
|
||||
{
|
||||
RawPosixAIOData *acb = g_new(RawPosixAIOData, 1);
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
ThreadPool *pool;
|
||||
|
||||
acb->bs = bs;
|
||||
@@ -1282,7 +1286,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawPosixAIOData *acb = g_new(RawPosixAIOData, 1);
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
ThreadPool *pool;
|
||||
|
||||
acb->bs = bs;
|
||||
@@ -1638,7 +1642,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
@@ -1969,8 +1973,8 @@ BlockDriver bdrv_file = {
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
|
||||
static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath,
|
||||
CFIndex maxPathSize, int flags);
|
||||
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
|
||||
|
||||
kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
|
||||
{
|
||||
kern_return_t kernResult;
|
||||
@@ -1997,8 +2001,7 @@ kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
|
||||
return kernResult;
|
||||
}
|
||||
|
||||
kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath,
|
||||
CFIndex maxPathSize, int flags)
|
||||
kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
|
||||
{
|
||||
io_object_t nextMedia;
|
||||
kern_return_t kernResult = KERN_FAILURE;
|
||||
@@ -2011,9 +2014,7 @@ kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath,
|
||||
if ( bsdPathAsCFString ) {
|
||||
size_t devPathLength;
|
||||
strcpy( bsdPath, _PATH_DEV );
|
||||
if (flags & BDRV_O_NOCACHE) {
|
||||
strcat(bsdPath, "r");
|
||||
}
|
||||
strcat( bsdPath, "r" );
|
||||
devPathLength = strlen( bsdPath );
|
||||
if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
|
||||
kernResult = KERN_SUCCESS;
|
||||
@@ -2125,8 +2126,8 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int fd;
|
||||
|
||||
kernResult = FindEjectableCDMedia( &mediaIterator );
|
||||
kernResult = GetBSDPath(mediaIterator, bsdPath, sizeof(bsdPath),
|
||||
flags);
|
||||
kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
|
||||
|
||||
if ( bsdPath[ 0 ] != '\0' ) {
|
||||
strcat(bsdPath,"s0");
|
||||
/* some CDs don't have a partition 0 */
|
||||
@@ -2171,6 +2172,53 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
/* Note: we do not have a reliable method to detect if the floppy is
|
||||
present. The current method is to try to open the floppy at every
|
||||
I/O and to keep it opened during a few hundreds of ms. */
|
||||
static int fd_open(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int last_media_present;
|
||||
|
||||
if (s->type != FTYPE_FD)
|
||||
return 0;
|
||||
last_media_present = (s->fd >= 0);
|
||||
if (s->fd >= 0 &&
|
||||
(qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
|
||||
qemu_close(s->fd);
|
||||
s->fd = -1;
|
||||
DPRINTF("Floppy closed\n");
|
||||
}
|
||||
if (s->fd < 0) {
|
||||
if (s->fd_got_error &&
|
||||
(qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_error_time) < FD_OPEN_TIMEOUT) {
|
||||
DPRINTF("No floppy (open delayed)\n");
|
||||
return -EIO;
|
||||
}
|
||||
s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK);
|
||||
if (s->fd < 0) {
|
||||
s->fd_error_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
s->fd_got_error = 1;
|
||||
if (last_media_present)
|
||||
s->fd_media_changed = 1;
|
||||
DPRINTF("No floppy\n");
|
||||
return -EIO;
|
||||
}
|
||||
DPRINTF("Floppy opened\n");
|
||||
}
|
||||
if (!last_media_present)
|
||||
s->fd_media_changed = 1;
|
||||
s->fd_open_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
s->fd_got_error = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
return ioctl(s->fd, req, buf);
|
||||
}
|
||||
|
||||
static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
@@ -2183,7 +2231,7 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
||||
if (fd_open(bs) < 0)
|
||||
return NULL;
|
||||
|
||||
acb = g_new(RawPosixAIOData, 1);
|
||||
acb = g_slice_new(RawPosixAIOData);
|
||||
acb->bs = bs;
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = s->fd;
|
||||
@@ -2193,8 +2241,8 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
||||
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
||||
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
||||
}
|
||||
#endif /* linux */
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
static int fd_open(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
@@ -2204,6 +2252,14 @@ static int fd_open(BlockDriverState *bs)
|
||||
return 0;
|
||||
return -EIO;
|
||||
}
|
||||
#else /* !linux && !FreeBSD */
|
||||
|
||||
static int fd_open(BlockDriverState *bs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !linux && !FreeBSD */
|
||||
|
||||
static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
@@ -2247,22 +2303,17 @@ static int hdev_create(const char *filename, QemuOpts *opts,
|
||||
int64_t total_size = 0;
|
||||
bool has_prefix;
|
||||
|
||||
/* This function is used by both protocol block drivers and therefore either
|
||||
* of these prefixes may be given.
|
||||
/* This function is used by all three protocol block drivers and therefore
|
||||
* any of these three prefixes may be given.
|
||||
* The return value has to be stored somewhere, otherwise this is an error
|
||||
* due to -Werror=unused-value. */
|
||||
has_prefix =
|
||||
strstart(filename, "host_device:", &filename) ||
|
||||
strstart(filename, "host_cdrom:" , &filename);
|
||||
strstart(filename, "host_cdrom:" , &filename) ||
|
||||
strstart(filename, "host_floppy:", &filename);
|
||||
|
||||
(void)has_prefix;
|
||||
|
||||
ret = raw_normalize_devicepath(&filename);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not normalize device path");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
@@ -2328,10 +2379,160 @@ static BlockDriver bdrv_host_device = {
|
||||
|
||||
/* generic scsi device */
|
||||
#ifdef __linux__
|
||||
.bdrv_ioctl = hdev_ioctl,
|
||||
.bdrv_aio_ioctl = hdev_aio_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
static void floppy_parse_filename(const char *filename, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
/* The prefix is optional, just as for "file". */
|
||||
strstart(filename, "host_floppy:", &filename);
|
||||
|
||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
||||
}
|
||||
|
||||
static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
s->type = FTYPE_FD;
|
||||
|
||||
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
|
||||
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
||||
if (ret) {
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* close fd so that we can reopen it as needed */
|
||||
qemu_close(s->fd);
|
||||
s->fd = -1;
|
||||
s->fd_media_changed = 1;
|
||||
|
||||
error_report("Host floppy pass-through is deprecated");
|
||||
error_printf("Support for it will be removed in a future release.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int floppy_probe_device(const char *filename)
|
||||
{
|
||||
int fd, ret;
|
||||
int prio = 0;
|
||||
struct floppy_struct fdparam;
|
||||
struct stat st;
|
||||
|
||||
if (strstart(filename, "/dev/fd", NULL) &&
|
||||
!strstart(filename, "/dev/fdset/", NULL) &&
|
||||
!strstart(filename, "/dev/fd/", NULL)) {
|
||||
prio = 50;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = fstat(fd, &st);
|
||||
if (ret == -1 || !S_ISBLK(st.st_mode)) {
|
||||
goto outc;
|
||||
}
|
||||
|
||||
/* Attempt to detect via a floppy specific ioctl */
|
||||
ret = ioctl(fd, FDGETPRM, &fdparam);
|
||||
if (ret >= 0)
|
||||
prio = 100;
|
||||
|
||||
outc:
|
||||
qemu_close(fd);
|
||||
out:
|
||||
return prio;
|
||||
}
|
||||
|
||||
|
||||
static int floppy_is_inserted(BlockDriverState *bs)
|
||||
{
|
||||
return fd_open(bs) >= 0;
|
||||
}
|
||||
|
||||
static int floppy_media_changed(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX: we do not have a true media changed indication.
|
||||
* It does not work if the floppy is changed without trying to read it.
|
||||
*/
|
||||
fd_open(bs);
|
||||
ret = s->fd_media_changed;
|
||||
s->fd_media_changed = 0;
|
||||
DPRINTF("Floppy changed=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void floppy_eject(BlockDriverState *bs, bool eject_flag)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int fd;
|
||||
|
||||
if (s->fd >= 0) {
|
||||
qemu_close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
fd = qemu_open(bs->filename, s->open_flags | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
if (ioctl(fd, FDEJECT, 0) < 0)
|
||||
perror("FDEJECT");
|
||||
qemu_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_host_floppy = {
|
||||
.format_name = "host_floppy",
|
||||
.protocol_name = "host_floppy",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_probe_device = floppy_probe_device,
|
||||
.bdrv_parse_filename = floppy_parse_filename,
|
||||
.bdrv_file_open = floppy_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_create = hdev_create,
|
||||
.create_opts = &raw_create_opts,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_refresh_limits = raw_refresh_limits,
|
||||
.bdrv_io_plug = raw_aio_plug,
|
||||
.bdrv_io_unplug = raw_aio_unplug,
|
||||
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
.has_variable_length = true,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
|
||||
.bdrv_detach_aio_context = raw_detach_aio_context,
|
||||
.bdrv_attach_aio_context = raw_attach_aio_context,
|
||||
|
||||
/* removable device support */
|
||||
.bdrv_is_inserted = floppy_is_inserted,
|
||||
.bdrv_media_changed = floppy_media_changed,
|
||||
.bdrv_eject = floppy_eject,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
static void cdrom_parse_filename(const char *filename, QDict *options,
|
||||
Error **errp)
|
||||
@@ -2387,13 +2588,15 @@ out:
|
||||
return prio;
|
||||
}
|
||||
|
||||
static bool cdrom_is_inserted(BlockDriverState *bs)
|
||||
static int cdrom_is_inserted(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
|
||||
return ret == CDS_DISC_OK;
|
||||
if (ret == CDS_DISC_OK)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
|
||||
@@ -2460,6 +2663,7 @@ static BlockDriver bdrv_host_cdrom = {
|
||||
.bdrv_lock_medium = cdrom_lock_medium,
|
||||
|
||||
/* generic scsi device */
|
||||
.bdrv_ioctl = hdev_ioctl,
|
||||
.bdrv_aio_ioctl = hdev_aio_ioctl,
|
||||
};
|
||||
#endif /* __linux__ */
|
||||
@@ -2518,7 +2722,7 @@ static int cdrom_reopen(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool cdrom_is_inserted(BlockDriverState *bs)
|
||||
static int cdrom_is_inserted(BlockDriverState *bs)
|
||||
{
|
||||
return raw_getlength(bs) > 0;
|
||||
}
|
||||
@@ -2606,6 +2810,7 @@ static void bdrv_file_init(void)
|
||||
bdrv_register(&bdrv_file);
|
||||
bdrv_register(&bdrv_host_device);
|
||||
#ifdef __linux__
|
||||
bdrv_register(&bdrv_host_floppy);
|
||||
bdrv_register(&bdrv_host_cdrom);
|
||||
#endif
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
|
||||
+4
-4
@@ -119,9 +119,9 @@ static int aio_worker(void *arg)
|
||||
case QEMU_AIO_WRITE:
|
||||
count = handle_aiocb_rw(aiocb);
|
||||
if (count == aiocb->aio_nbytes) {
|
||||
ret = 0;
|
||||
count = 0;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
count = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case QEMU_AIO_FLUSH:
|
||||
@@ -135,7 +135,7 @@ static int aio_worker(void *arg)
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(aiocb);
|
||||
g_slice_free(RawWin32AIOData, aiocb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawWin32AIOData *acb = g_new(RawWin32AIOData, 1);
|
||||
RawWin32AIOData *acb = g_slice_new(RawWin32AIOData);
|
||||
ThreadPool *pool;
|
||||
|
||||
acb->bs = bs;
|
||||
|
||||
+30
-18
@@ -52,7 +52,7 @@ static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
return bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov);
|
||||
return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -75,7 +75,7 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = qemu_try_blockalign(bs->file->bs, 512);
|
||||
buf = qemu_try_blockalign(bs->file, 512);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
@@ -102,7 +102,7 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
ret = bdrv_co_writev(bs->file->bs, sector_num, nb_sectors, qiov);
|
||||
ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
|
||||
|
||||
fail:
|
||||
if (qiov == &local_qiov) {
|
||||
@@ -125,48 +125,58 @@ static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_write_zeroes(bs->file->bs, sector_num, nb_sectors, flags);
|
||||
return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
return bdrv_co_discard(bs->file->bs, sector_num, nb_sectors);
|
||||
return bdrv_co_discard(bs->file, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
static int64_t raw_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
return bdrv_getlength(bs->file);
|
||||
}
|
||||
|
||||
static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
return bdrv_get_info(bs->file->bs, bdi);
|
||||
return bdrv_get_info(bs->file, bdi);
|
||||
}
|
||||
|
||||
static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
bs->bl = bs->file->bs->bl;
|
||||
bs->bl = bs->file->bl;
|
||||
}
|
||||
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
return bdrv_truncate(bs->file->bs, offset);
|
||||
return bdrv_truncate(bs->file, offset);
|
||||
}
|
||||
|
||||
static int raw_is_inserted(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_is_inserted(bs->file);
|
||||
}
|
||||
|
||||
static int raw_media_changed(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_media_changed(bs->file->bs);
|
||||
return bdrv_media_changed(bs->file);
|
||||
}
|
||||
|
||||
static void raw_eject(BlockDriverState *bs, bool eject_flag)
|
||||
{
|
||||
bdrv_eject(bs->file->bs, eject_flag);
|
||||
bdrv_eject(bs->file, eject_flag);
|
||||
}
|
||||
|
||||
static void raw_lock_medium(BlockDriverState *bs, bool locked)
|
||||
{
|
||||
bdrv_lock_medium(bs->file->bs, locked);
|
||||
bdrv_lock_medium(bs->file, locked);
|
||||
}
|
||||
|
||||
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
return bdrv_ioctl(bs->file, req, buf);
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
||||
@@ -174,12 +184,12 @@ static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return bdrv_aio_ioctl(bs->file->bs, req, buf, cb, opaque);
|
||||
return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
|
||||
}
|
||||
|
||||
static int raw_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_has_zero_init(bs->file->bs);
|
||||
return bdrv_has_zero_init(bs->file);
|
||||
}
|
||||
|
||||
static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
@@ -197,7 +207,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
bs->sg = bs->file->bs->sg;
|
||||
bs->sg = bs->file->sg;
|
||||
|
||||
if (bs->probed && !bdrv_is_read_only(bs)) {
|
||||
fprintf(stderr,
|
||||
@@ -207,7 +217,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
"raw images, write operations on block 0 will be restricted.\n"
|
||||
" Specify the 'raw' format explicitly to remove the "
|
||||
"restrictions.\n",
|
||||
bs->file->bs->filename);
|
||||
bs->file->filename);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -227,12 +237,12 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
|
||||
static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
|
||||
{
|
||||
return bdrv_probe_blocksizes(bs->file->bs, bsz);
|
||||
return bdrv_probe_blocksizes(bs->file, bsz);
|
||||
}
|
||||
|
||||
static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
|
||||
{
|
||||
return bdrv_probe_geometry(bs->file->bs, geo);
|
||||
return bdrv_probe_geometry(bs->file, geo);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_raw = {
|
||||
@@ -254,9 +264,11 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_refresh_limits = &raw_refresh_limits,
|
||||
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
|
||||
.bdrv_probe_geometry = &raw_probe_geometry,
|
||||
.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,
|
||||
.bdrv_has_zero_init = &raw_has_zero_init
|
||||
|
||||
+51
-156
@@ -28,6 +28,7 @@
|
||||
#define SD_OP_READ_OBJ 0x02
|
||||
#define SD_OP_WRITE_OBJ 0x03
|
||||
/* 0x04 is used internally by Sheepdog */
|
||||
#define SD_OP_DISCARD_OBJ 0x05
|
||||
|
||||
#define SD_OP_NEW_VDI 0x11
|
||||
#define SD_OP_LOCK_VDI 0x12
|
||||
@@ -317,7 +318,7 @@ enum AIOCBState {
|
||||
AIOCB_DISCARD_OBJ,
|
||||
};
|
||||
|
||||
#define AIOCBOverlapping(x, y) \
|
||||
#define AIOCBOverwrapping(x, y) \
|
||||
(!(x->max_affect_data_idx < y->min_affect_data_idx \
|
||||
|| y->max_affect_data_idx < x->min_affect_data_idx))
|
||||
|
||||
@@ -341,15 +342,6 @@ struct SheepdogAIOCB {
|
||||
uint32_t min_affect_data_idx;
|
||||
uint32_t max_affect_data_idx;
|
||||
|
||||
/*
|
||||
* The difference between affect_data_idx and dirty_data_idx:
|
||||
* affect_data_idx represents range of index of all request types.
|
||||
* dirty_data_idx represents range of index updated by COW requests.
|
||||
* dirty_data_idx is used for updating an inode object.
|
||||
*/
|
||||
uint32_t min_dirty_data_idx;
|
||||
uint32_t max_dirty_data_idx;
|
||||
|
||||
QLIST_ENTRY(SheepdogAIOCB) aiocb_siblings;
|
||||
};
|
||||
|
||||
@@ -359,6 +351,9 @@ typedef struct BDRVSheepdogState {
|
||||
|
||||
SheepdogInode inode;
|
||||
|
||||
uint32_t min_dirty_data_idx;
|
||||
uint32_t max_dirty_data_idx;
|
||||
|
||||
char name[SD_MAX_VDI_LEN];
|
||||
bool is_snapshot;
|
||||
uint32_t cache_flags;
|
||||
@@ -378,15 +373,10 @@ typedef struct BDRVSheepdogState {
|
||||
QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head;
|
||||
QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head;
|
||||
|
||||
CoQueue overlapping_queue;
|
||||
CoQueue overwrapping_queue;
|
||||
QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head;
|
||||
} BDRVSheepdogState;
|
||||
|
||||
typedef struct BDRVSheepdogReopenState {
|
||||
int fd;
|
||||
int cache_flags;
|
||||
} BDRVSheepdogReopenState;
|
||||
|
||||
static const char * sd_strerror(int err)
|
||||
{
|
||||
int i;
|
||||
@@ -566,9 +556,6 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
acb->max_affect_data_idx = (acb->sector_num * BDRV_SECTOR_SIZE +
|
||||
acb->nb_sectors * BDRV_SECTOR_SIZE) / object_size;
|
||||
|
||||
acb->min_dirty_data_idx = UINT32_MAX;
|
||||
acb->max_dirty_data_idx = 0;
|
||||
|
||||
return acb;
|
||||
}
|
||||
|
||||
@@ -651,16 +638,14 @@ static coroutine_fn void do_co_req(void *opaque)
|
||||
unsigned int *rlen = srco->rlen;
|
||||
|
||||
co = qemu_coroutine_self();
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, false,
|
||||
NULL, restart_co_req, co);
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, NULL, restart_co_req, co);
|
||||
|
||||
ret = send_co_req(sockfd, hdr, data, wlen);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, false,
|
||||
restart_co_req, NULL, co);
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, restart_co_req, NULL, co);
|
||||
|
||||
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
|
||||
if (ret != sizeof(*hdr)) {
|
||||
@@ -685,8 +670,7 @@ static coroutine_fn void do_co_req(void *opaque)
|
||||
out:
|
||||
/* there is at most one request for this sockfd, so it is safe to
|
||||
* set each handler to NULL. */
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, false,
|
||||
NULL, NULL, NULL);
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, NULL, NULL, NULL);
|
||||
|
||||
srco->ret = ret;
|
||||
srco->finished = true;
|
||||
@@ -738,8 +722,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
|
||||
BDRVSheepdogState *s = opaque;
|
||||
AIOReq *aio_req, *next;
|
||||
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false, NULL,
|
||||
NULL, NULL);
|
||||
aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL);
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
|
||||
@@ -836,8 +819,8 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
*/
|
||||
if (rsp.result == SD_RES_SUCCESS) {
|
||||
s->inode.data_vdi_id[idx] = s->inode.vdi_id;
|
||||
acb->max_dirty_data_idx = MAX(idx, acb->max_dirty_data_idx);
|
||||
acb->min_dirty_data_idx = MIN(idx, acb->min_dirty_data_idx);
|
||||
s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx);
|
||||
s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -864,6 +847,10 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
rsp.result = SD_RES_SUCCESS;
|
||||
s->discard_supported = false;
|
||||
break;
|
||||
case SD_RES_SUCCESS:
|
||||
idx = data_oid_to_idx(aio_req->oid);
|
||||
s->inode.data_vdi_id[idx] = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -942,8 +929,7 @@ static int get_sheep_fd(BDRVSheepdogState *s, Error **errp)
|
||||
return fd;
|
||||
}
|
||||
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
co_read_response, NULL, s);
|
||||
aio_set_fd_handler(s->aio_context, fd, co_read_response, NULL, s);
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -1179,13 +1165,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
hdr.flags = SD_FLAG_CMD_WRITE | flags;
|
||||
break;
|
||||
case AIOCB_DISCARD_OBJ:
|
||||
hdr.opcode = SD_OP_WRITE_OBJ;
|
||||
hdr.flags = SD_FLAG_CMD_WRITE | flags;
|
||||
s->inode.data_vdi_id[data_oid_to_idx(oid)] = 0;
|
||||
offset = offsetof(SheepdogInode,
|
||||
data_vdi_id[data_oid_to_idx(oid)]);
|
||||
oid = vid_to_vdi_oid(s->inode.vdi_id);
|
||||
wlen = datalen = sizeof(uint32_t);
|
||||
hdr.opcode = SD_OP_DISCARD_OBJ;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1204,7 +1184,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
s->co_send = qemu_coroutine_self();
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false,
|
||||
aio_set_fd_handler(s->aio_context, s->fd,
|
||||
co_read_response, co_write_request, s);
|
||||
socket_set_cork(s->fd, 1);
|
||||
|
||||
@@ -1223,8 +1203,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
}
|
||||
out:
|
||||
socket_set_cork(s->fd, 0);
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false,
|
||||
co_read_response, NULL, s);
|
||||
aio_set_fd_handler(s->aio_context, s->fd, co_read_response, NULL, s);
|
||||
s->co_send = NULL;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
}
|
||||
@@ -1374,8 +1353,7 @@ static void sd_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false, NULL,
|
||||
NULL, NULL);
|
||||
aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void sd_attach_aio_context(BlockDriverState *bs,
|
||||
@@ -1384,8 +1362,7 @@ static void sd_attach_aio_context(BlockDriverState *bs,
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
s->aio_context = new_context;
|
||||
aio_set_fd_handler(new_context, s->fd, false,
|
||||
co_read_response, NULL, s);
|
||||
aio_set_fd_handler(new_context, s->fd, co_read_response, NULL, s);
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
@@ -1489,17 +1466,18 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
memcpy(&s->inode, buf, sizeof(s->inode));
|
||||
s->min_dirty_data_idx = UINT32_MAX;
|
||||
s->max_dirty_data_idx = 0;
|
||||
|
||||
bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE;
|
||||
pstrcpy(s->name, sizeof(s->name), vdi);
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
qemu_co_queue_init(&s->overlapping_queue);
|
||||
qemu_co_queue_init(&s->overwrapping_queue);
|
||||
qemu_opts_del(opts);
|
||||
g_free(buf);
|
||||
return 0;
|
||||
out:
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
|
||||
false, NULL, NULL, NULL);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL);
|
||||
if (s->fd >= 0) {
|
||||
closesocket(s->fd);
|
||||
}
|
||||
@@ -1508,70 +1486,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sd_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVSheepdogState *s = state->bs->opaque;
|
||||
BDRVSheepdogReopenState *re_s;
|
||||
int ret = 0;
|
||||
|
||||
re_s = state->opaque = g_new0(BDRVSheepdogReopenState, 1);
|
||||
|
||||
re_s->cache_flags = SD_FLAG_CMD_CACHE;
|
||||
if (state->flags & BDRV_O_NOCACHE) {
|
||||
re_s->cache_flags = SD_FLAG_CMD_DIRECT;
|
||||
}
|
||||
|
||||
re_s->fd = get_sheep_fd(s, errp);
|
||||
if (re_s->fd < 0) {
|
||||
ret = re_s->fd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sd_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
BDRVSheepdogReopenState *re_s = state->opaque;
|
||||
BDRVSheepdogState *s = state->bs->opaque;
|
||||
|
||||
if (s->fd) {
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false,
|
||||
NULL, NULL, NULL);
|
||||
closesocket(s->fd);
|
||||
}
|
||||
|
||||
s->fd = re_s->fd;
|
||||
s->cache_flags = re_s->cache_flags;
|
||||
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void sd_reopen_abort(BDRVReopenState *state)
|
||||
{
|
||||
BDRVSheepdogReopenState *re_s = state->opaque;
|
||||
BDRVSheepdogState *s = state->bs->opaque;
|
||||
|
||||
if (re_s == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (re_s->fd) {
|
||||
aio_set_fd_handler(s->aio_context, re_s->fd, false,
|
||||
NULL, NULL, NULL);
|
||||
closesocket(re_s->fd);
|
||||
}
|
||||
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -1640,7 +1554,7 @@ static int sd_prealloc(const char *filename, Error **errp)
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
errp);
|
||||
NULL, errp);
|
||||
if (ret < 0) {
|
||||
goto out_with_err_set;
|
||||
}
|
||||
@@ -1832,7 +1746,8 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp);
|
||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -1946,8 +1861,7 @@ static void sd_close(BlockDriverState *bs)
|
||||
error_report("%s, %s", sd_strerror(rsp->result), s->name);
|
||||
}
|
||||
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
|
||||
false, NULL, NULL, NULL);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL);
|
||||
closesocket(s->fd);
|
||||
g_free(s->host_spec);
|
||||
}
|
||||
@@ -2009,16 +1923,16 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
|
||||
AIOReq *aio_req;
|
||||
uint32_t offset, data_len, mn, mx;
|
||||
|
||||
mn = acb->min_dirty_data_idx;
|
||||
mx = acb->max_dirty_data_idx;
|
||||
mn = s->min_dirty_data_idx;
|
||||
mx = s->max_dirty_data_idx;
|
||||
if (mn <= mx) {
|
||||
/* we need to update the vdi object. */
|
||||
offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) +
|
||||
mn * sizeof(s->inode.data_vdi_id[0]);
|
||||
data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]);
|
||||
|
||||
acb->min_dirty_data_idx = UINT32_MAX;
|
||||
acb->max_dirty_data_idx = 0;
|
||||
s->min_dirty_data_idx = UINT32_MAX;
|
||||
s->max_dirty_data_idx = 0;
|
||||
|
||||
iov.iov_base = &s->inode;
|
||||
iov.iov_len = sizeof(s->inode);
|
||||
@@ -2227,9 +2141,7 @@ static int coroutine_fn sd_co_rw_vector(void *p)
|
||||
}
|
||||
|
||||
aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create,
|
||||
old_oid,
|
||||
acb->aiocb_type == AIOCB_DISCARD_OBJ ?
|
||||
0 : done);
|
||||
old_oid, done);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
|
||||
add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
|
||||
@@ -2246,12 +2158,12 @@ out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool check_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb)
|
||||
static bool check_overwrapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb)
|
||||
{
|
||||
SheepdogAIOCB *cb;
|
||||
|
||||
QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
|
||||
if (AIOCBOverlapping(aiocb, cb)) {
|
||||
if (AIOCBOverwrapping(aiocb, cb)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2280,15 +2192,15 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
acb->aiocb_type = AIOCB_WRITE_UDATA;
|
||||
|
||||
retry:
|
||||
if (check_overlapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
if (check_overwrapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overwrapping_queue);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_co_queue_restart_all(&s->overwrapping_queue);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
@@ -2296,7 +2208,7 @@ retry:
|
||||
qemu_coroutine_yield();
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_co_queue_restart_all(&s->overwrapping_queue);
|
||||
|
||||
return acb->ret;
|
||||
}
|
||||
@@ -2313,15 +2225,15 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
retry:
|
||||
if (check_overlapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
if (check_overwrapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overwrapping_queue);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_co_queue_restart_all(&s->overwrapping_queue);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
@@ -2329,7 +2241,7 @@ retry:
|
||||
qemu_coroutine_yield();
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_co_queue_restart_all(&s->overwrapping_queue);
|
||||
return acb->ret;
|
||||
}
|
||||
|
||||
@@ -2665,36 +2577,28 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
SheepdogAIOCB *acb;
|
||||
QEMUIOVector dummy;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
int ret;
|
||||
QEMUIOVector discard_iov;
|
||||
struct iovec iov;
|
||||
uint32_t zero = 0;
|
||||
|
||||
if (!s->discard_supported) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&discard_iov, 0, sizeof(discard_iov));
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov.iov_base = &zero;
|
||||
iov.iov_len = sizeof(zero);
|
||||
discard_iov.iov = &iov;
|
||||
discard_iov.niov = 1;
|
||||
acb = sd_aio_setup(bs, &discard_iov, sector_num, nb_sectors);
|
||||
acb = sd_aio_setup(bs, &dummy, sector_num, nb_sectors);
|
||||
acb->aiocb_type = AIOCB_DISCARD_OBJ;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
retry:
|
||||
if (check_overlapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
if (check_overwrapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overwrapping_queue);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_co_queue_restart_all(&s->overwrapping_queue);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
@@ -2702,7 +2606,7 @@ retry:
|
||||
qemu_coroutine_yield();
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_co_queue_restart_all(&s->overwrapping_queue);
|
||||
|
||||
return acb->ret;
|
||||
}
|
||||
@@ -2799,9 +2703,6 @@ static BlockDriver bdrv_sheepdog = {
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
@@ -2835,9 +2736,6 @@ static BlockDriver bdrv_sheepdog_tcp = {
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
@@ -2871,9 +2769,6 @@ static BlockDriver bdrv_sheepdog_unix = {
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_needs_filename = true,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
+9
-137
@@ -149,7 +149,7 @@ int bdrv_can_snapshot(BlockDriverState *bs)
|
||||
|
||||
if (!drv->bdrv_snapshot_create) {
|
||||
if (bs->file != NULL) {
|
||||
return bdrv_can_snapshot(bs->file->bs);
|
||||
return bdrv_can_snapshot(bs->file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ int bdrv_snapshot_create(BlockDriverState *bs,
|
||||
return drv->bdrv_snapshot_create(bs, sn_info);
|
||||
}
|
||||
if (bs->file) {
|
||||
return bdrv_snapshot_create(bs->file->bs, sn_info);
|
||||
return bdrv_snapshot_create(bs->file, sn_info);
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -188,10 +188,10 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
|
||||
if (bs->file) {
|
||||
drv->bdrv_close(bs);
|
||||
ret = bdrv_snapshot_goto(bs->file->bs, snapshot_id);
|
||||
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
||||
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL);
|
||||
if (open_ret < 0) {
|
||||
bdrv_unref(bs->file->bs);
|
||||
bdrv_unref(bs->file);
|
||||
bs->drv = NULL;
|
||||
return open_ret;
|
||||
}
|
||||
@@ -245,7 +245,7 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
||||
}
|
||||
if (bs->file) {
|
||||
return bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
|
||||
return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp);
|
||||
}
|
||||
error_setg(errp, "Block format '%s' used by device '%s' "
|
||||
"does not support internal snapshot deletion",
|
||||
@@ -253,9 +253,9 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
||||
const char *id_or_name,
|
||||
Error **errp)
|
||||
void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
||||
const char *id_or_name,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
@@ -270,7 +270,6 @@ int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
@@ -284,7 +283,7 @@ int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
return drv->bdrv_snapshot_list(bs, psn_info);
|
||||
}
|
||||
if (bs->file) {
|
||||
return bdrv_snapshot_list(bs->file->bs, psn_info);
|
||||
return bdrv_snapshot_list(bs->file, psn_info);
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -357,130 +356,3 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Group operations. All block drivers are involved.
|
||||
* These functions will properly handle dataplane (take aio_context_acquire
|
||||
* when appropriate for appropriate block drivers) */
|
||||
|
||||
bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs)
|
||||
{
|
||||
bool ok = true;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
while (ok && (bs = bdrv_next(bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (bdrv_is_inserted(bs) && !bdrv_is_read_only(bs)) {
|
||||
ok = bdrv_can_snapshot(bs);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
*first_bad_bs = bs;
|
||||
return ok;
|
||||
}
|
||||
|
||||
int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs,
|
||||
Error **err)
|
||||
{
|
||||
int ret = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
QEMUSnapshotInfo sn1, *snapshot = &sn1;
|
||||
|
||||
while (ret == 0 && (bs = bdrv_next(bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (bdrv_can_snapshot(bs) &&
|
||||
bdrv_snapshot_find(bs, snapshot, name) >= 0) {
|
||||
ret = bdrv_snapshot_delete_by_id_or_name(bs, name, err);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
*first_bad_bs = bs;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs)
|
||||
{
|
||||
int err = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
while (err == 0 && (bs = bdrv_next(bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (bdrv_can_snapshot(bs)) {
|
||||
err = bdrv_snapshot_goto(bs, name);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
*first_bad_bs = bs;
|
||||
return err;
|
||||
}
|
||||
|
||||
int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs)
|
||||
{
|
||||
QEMUSnapshotInfo sn;
|
||||
int err = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
while (err == 0 && (bs = bdrv_next(bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (bdrv_can_snapshot(bs)) {
|
||||
err = bdrv_snapshot_find(bs, &sn, name);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
*first_bad_bs = bs;
|
||||
return err;
|
||||
}
|
||||
|
||||
int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
|
||||
BlockDriverState *vm_state_bs,
|
||||
uint64_t vm_state_size,
|
||||
BlockDriverState **first_bad_bs)
|
||||
{
|
||||
int err = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
while (err == 0 && (bs = bdrv_next(bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (bs == vm_state_bs) {
|
||||
sn->vm_state_size = vm_state_size;
|
||||
err = bdrv_snapshot_create(bs, sn);
|
||||
} else if (bdrv_can_snapshot(bs)) {
|
||||
sn->vm_state_size = 0;
|
||||
err = bdrv_snapshot_create(bs, sn);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
*first_bad_bs = bs;
|
||||
return err;
|
||||
}
|
||||
|
||||
BlockDriverState *bdrv_all_find_vmstate_bs(void)
|
||||
{
|
||||
bool not_found = true;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
while (not_found && (bs = bdrv_next(bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
not_found = !bdrv_can_snapshot(bs);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
+6
-4
@@ -193,7 +193,7 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...)
|
||||
static int parse_uri(const char *filename, QDict *options, Error **errp)
|
||||
{
|
||||
URI *uri = NULL;
|
||||
QueryParams *qp;
|
||||
QueryParams *qp = NULL;
|
||||
int i;
|
||||
|
||||
uri = uri_parse(filename);
|
||||
@@ -249,6 +249,9 @@ static int parse_uri(const char *filename, QDict *options, Error **errp)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (qp) {
|
||||
query_params_free(qp);
|
||||
}
|
||||
if (uri) {
|
||||
uri_free(uri);
|
||||
}
|
||||
@@ -800,15 +803,14 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
|
||||
rd_handler, wr_handler);
|
||||
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
|
||||
false, rd_handler, wr_handler, co);
|
||||
rd_handler, wr_handler, co);
|
||||
}
|
||||
|
||||
static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
|
||||
BlockDriverState *bs)
|
||||
{
|
||||
DPRINTF("s->sock=%d", s->sock);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
|
||||
false, NULL, NULL, NULL);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* A non-blocking call returned EAGAIN, so yield, ensuring the
|
||||
|
||||
+32
-5
@@ -16,7 +16,6 @@
|
||||
#include "block/blockjob.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
enum {
|
||||
/*
|
||||
@@ -53,6 +52,34 @@ static int coroutine_fn stream_populate(BlockDriverState *bs,
|
||||
return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov);
|
||||
}
|
||||
|
||||
static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
||||
const char *base_id)
|
||||
{
|
||||
BlockDriverState *intermediate;
|
||||
intermediate = top->backing_hd;
|
||||
|
||||
/* Must assign before bdrv_delete() to prevent traversing dangling pointer
|
||||
* while we delete backing image instances.
|
||||
*/
|
||||
bdrv_set_backing_hd(top, base);
|
||||
|
||||
while (intermediate) {
|
||||
BlockDriverState *unused;
|
||||
|
||||
/* reached base */
|
||||
if (intermediate == base) {
|
||||
break;
|
||||
}
|
||||
|
||||
unused = intermediate;
|
||||
intermediate = intermediate->backing_hd;
|
||||
bdrv_set_backing_hd(unused, NULL);
|
||||
bdrv_unref(unused);
|
||||
}
|
||||
|
||||
bdrv_refresh_limits(top, NULL);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
bool reached_end;
|
||||
@@ -74,7 +101,7 @@ static void stream_complete(BlockJob *job, void *opaque)
|
||||
}
|
||||
}
|
||||
data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt);
|
||||
bdrv_set_backing_hd(job->bs, base);
|
||||
close_unused_images(job->bs, base, base_id);
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
@@ -94,7 +121,7 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
int n = 0;
|
||||
void *buf;
|
||||
|
||||
if (!bs->backing) {
|
||||
if (!bs->backing_hd) {
|
||||
block_job_completed(&s->common, 0);
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +166,7 @@ wait:
|
||||
} else if (ret >= 0) {
|
||||
/* Copy if allocated in the intermediate images. Limit to the
|
||||
* known-unallocated area [sector_num, sector_num+n). */
|
||||
ret = bdrv_is_allocated_above(backing_bs(bs), base,
|
||||
ret = bdrv_is_allocated_above(bs->backing_hd, base,
|
||||
sector_num, n, &n);
|
||||
|
||||
/* Finish early if end of backing file has been reached */
|
||||
@@ -223,7 +250,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "on-error");
|
||||
return;
|
||||
}
|
||||
|
||||
+38
-19
@@ -33,7 +33,8 @@
|
||||
* its own locking.
|
||||
*
|
||||
* This locking is however handled internally in this file, so it's
|
||||
* transparent to outside users.
|
||||
* mostly transparent to outside users (but see the documentation in
|
||||
* throttle_groups_lock()).
|
||||
*
|
||||
* The whole ThrottleGroup structure is private and invisible to
|
||||
* outside users, that only use it through its ThrottleState.
|
||||
@@ -75,9 +76,9 @@ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
|
||||
* created.
|
||||
*
|
||||
* @name: the name of the ThrottleGroup
|
||||
* @ret: the ThrottleState member of the ThrottleGroup
|
||||
* @ret: the ThrottleGroup
|
||||
*/
|
||||
ThrottleState *throttle_group_incref(const char *name)
|
||||
static ThrottleGroup *throttle_group_incref(const char *name)
|
||||
{
|
||||
ThrottleGroup *tg = NULL;
|
||||
ThrottleGroup *iter;
|
||||
@@ -107,7 +108,7 @@ ThrottleState *throttle_group_incref(const char *name)
|
||||
|
||||
qemu_mutex_unlock(&throttle_groups_lock);
|
||||
|
||||
return &tg->ts;
|
||||
return tg;
|
||||
}
|
||||
|
||||
/* Decrease the reference count of a ThrottleGroup.
|
||||
@@ -115,12 +116,10 @@ ThrottleState *throttle_group_incref(const char *name)
|
||||
* When the reference count reaches zero the ThrottleGroup is
|
||||
* destroyed.
|
||||
*
|
||||
* @ts: The ThrottleGroup to unref, given by its ThrottleState member
|
||||
* @tg: The ThrottleGroup to unref
|
||||
*/
|
||||
void throttle_group_unref(ThrottleState *ts)
|
||||
static void throttle_group_unref(ThrottleGroup *tg)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
|
||||
qemu_mutex_lock(&throttle_groups_lock);
|
||||
if (--tg->refcount == 0) {
|
||||
QTAILQ_REMOVE(&throttle_groups, tg, list);
|
||||
@@ -402,8 +401,7 @@ static void write_timer_cb(void *opaque)
|
||||
void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
|
||||
{
|
||||
int i;
|
||||
ThrottleState *ts = throttle_group_incref(groupname);
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
ThrottleGroup *tg = throttle_group_incref(groupname);
|
||||
int clock_type = QEMU_CLOCK_REALTIME;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
@@ -411,7 +409,7 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
|
||||
bs->throttle_state = ts;
|
||||
bs->throttle_state = &tg->ts;
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
/* If the ThrottleGroup is new set this BlockDriverState as the token */
|
||||
@@ -437,9 +435,6 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
|
||||
* list, destroying the timers and setting the throttle_state pointer
|
||||
* to NULL.
|
||||
*
|
||||
* The BlockDriverState must not have pending throttled requests, so
|
||||
* the caller has to drain them first.
|
||||
*
|
||||
* The group will be destroyed if it's empty after this operation.
|
||||
*
|
||||
* @bs: the BlockDriverState to remove
|
||||
@@ -449,10 +444,6 @@ void throttle_group_unregister_bs(BlockDriverState *bs)
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
int i;
|
||||
|
||||
assert(bs->pending_reqs[0] == 0 && bs->pending_reqs[1] == 0);
|
||||
assert(qemu_co_queue_empty(&bs->throttled_reqs[0]));
|
||||
assert(qemu_co_queue_empty(&bs->throttled_reqs[1]));
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (tg->tokens[i] == bs) {
|
||||
@@ -470,10 +461,38 @@ void throttle_group_unregister_bs(BlockDriverState *bs)
|
||||
throttle_timers_destroy(&bs->throttle_timers);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
throttle_group_unref(&tg->ts);
|
||||
throttle_group_unref(tg);
|
||||
bs->throttle_state = NULL;
|
||||
}
|
||||
|
||||
/* Acquire the lock of this throttling group.
|
||||
*
|
||||
* You won't normally need to use this. None of the functions from the
|
||||
* ThrottleGroup API require you to acquire the lock since all of them
|
||||
* deal with it internally.
|
||||
*
|
||||
* This should only be used in exceptional cases when you want to
|
||||
* access the protected fields of a BlockDriverState directly
|
||||
* (e.g. bdrv_swap()).
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of the group
|
||||
*/
|
||||
void throttle_group_lock(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Release the lock of this throttling group.
|
||||
*
|
||||
* See the comments in throttle_group_lock().
|
||||
*/
|
||||
void throttle_group_unlock(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
static void throttle_groups_init(void)
|
||||
{
|
||||
qemu_mutex_init(&throttle_groups_lock);
|
||||
|
||||
+10
-11
@@ -53,7 +53,7 @@
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/migration.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/coroutine.h"
|
||||
|
||||
#if defined(CONFIG_UUID)
|
||||
#include <uuid/uuid.h>
|
||||
@@ -399,7 +399,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
logout("\n");
|
||||
|
||||
ret = bdrv_read(bs->file->bs, 0, (uint8_t *)&header, 1);
|
||||
ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -490,14 +490,13 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
bmap_size = header.blocks_in_image * sizeof(uint32_t);
|
||||
bmap_size = DIV_ROUND_UP(bmap_size, SECTOR_SIZE);
|
||||
s->bmap = qemu_try_blockalign(bs->file->bs, bmap_size * SECTOR_SIZE);
|
||||
s->bmap = qemu_try_blockalign(bs->file, bmap_size * SECTOR_SIZE);
|
||||
if (s->bmap == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_read(bs->file->bs, s->bmap_sector, (uint8_t *)s->bmap,
|
||||
bmap_size);
|
||||
ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size);
|
||||
if (ret < 0) {
|
||||
goto fail_free_bmap;
|
||||
}
|
||||
@@ -586,7 +585,7 @@ static int vdi_co_read(BlockDriverState *bs,
|
||||
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors +
|
||||
sector_in_block;
|
||||
ret = bdrv_read(bs->file->bs, offset, buf, n_sectors);
|
||||
ret = bdrv_read(bs->file, offset, buf, n_sectors);
|
||||
}
|
||||
logout("%u sectors read\n", n_sectors);
|
||||
|
||||
@@ -654,7 +653,7 @@ static int vdi_co_write(BlockDriverState *bs,
|
||||
* acquire the lock and thus the padded cluster is written before
|
||||
* the other coroutines can write to the affected area. */
|
||||
qemu_co_mutex_lock(&s->write_lock);
|
||||
ret = bdrv_write(bs->file->bs, offset, block, s->block_sectors);
|
||||
ret = bdrv_write(bs->file, offset, block, s->block_sectors);
|
||||
qemu_co_mutex_unlock(&s->write_lock);
|
||||
} else {
|
||||
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
|
||||
@@ -670,7 +669,7 @@ static int vdi_co_write(BlockDriverState *bs,
|
||||
* that that write operation has returned (there may be other writes
|
||||
* in flight, but they do not concern this very operation). */
|
||||
qemu_co_mutex_unlock(&s->write_lock);
|
||||
ret = bdrv_write(bs->file->bs, offset, buf, n_sectors);
|
||||
ret = bdrv_write(bs->file, offset, buf, n_sectors);
|
||||
}
|
||||
|
||||
nb_sectors -= n_sectors;
|
||||
@@ -695,7 +694,7 @@ static int vdi_co_write(BlockDriverState *bs,
|
||||
assert(VDI_IS_ALLOCATED(bmap_first));
|
||||
*header = s->header;
|
||||
vdi_header_to_le(header);
|
||||
ret = bdrv_write(bs->file->bs, 0, block, 1);
|
||||
ret = bdrv_write(bs->file, 0, block, 1);
|
||||
g_free(block);
|
||||
block = NULL;
|
||||
|
||||
@@ -713,7 +712,7 @@ static int vdi_co_write(BlockDriverState *bs,
|
||||
base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE;
|
||||
logout("will write %u block map sectors starting from entry %u\n",
|
||||
n_sectors, bmap_first);
|
||||
ret = bdrv_write(bs->file->bs, offset, base, n_sectors);
|
||||
ret = bdrv_write(bs->file, offset, base, n_sectors);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -765,7 +764,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
|
||||
+12
-13
@@ -81,7 +81,7 @@ static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log,
|
||||
|
||||
offset = log->offset + read;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, offset, hdr, sizeof(VHDXLogEntryHeader));
|
||||
ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader));
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -141,7 +141,7 @@ static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log,
|
||||
}
|
||||
offset = log->offset + read;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, offset, buffer, VHDX_LOG_SECTOR_SIZE);
|
||||
ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -191,8 +191,7 @@ static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log,
|
||||
/* full */
|
||||
break;
|
||||
}
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, buffer_tmp,
|
||||
VHDX_LOG_SECTOR_SIZE);
|
||||
ret = bdrv_pwrite(bs->file, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -354,7 +353,7 @@ static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
|
||||
desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
|
||||
desc_entries = qemu_try_blockalign(bs->file->bs,
|
||||
desc_entries = qemu_try_blockalign(bs->file,
|
||||
desc_sectors * VHDX_LOG_SECTOR_SIZE);
|
||||
if (desc_entries == NULL) {
|
||||
ret = -ENOMEM;
|
||||
@@ -463,7 +462,7 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc,
|
||||
|
||||
/* count is only > 1 if we are writing zeroes */
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, file_offset, buffer,
|
||||
ret = bdrv_pwrite_sync(bs->file, file_offset, buffer,
|
||||
VHDX_LOG_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
@@ -510,7 +509,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
/* if the log shows a FlushedFileOffset larger than our current file
|
||||
* size, then that means the file has been truncated / corrupted, and
|
||||
* we must refused to open it / use it */
|
||||
if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file->bs)) {
|
||||
if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
@@ -540,12 +539,12 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (bdrv_getlength(bs->file->bs) < desc_entries->hdr.last_file_offset) {
|
||||
if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) {
|
||||
new_file_size = desc_entries->hdr.last_file_offset;
|
||||
if (new_file_size % (1024*1024)) {
|
||||
/* round up to nearest 1MB boundary */
|
||||
new_file_size = ((new_file_size >> 20) + 1) << 20;
|
||||
bdrv_truncate(bs->file->bs, new_file_size);
|
||||
bdrv_truncate(bs->file, new_file_size);
|
||||
}
|
||||
}
|
||||
qemu_vfree(desc_entries);
|
||||
@@ -909,8 +908,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
.sequence_number = s->log.sequence,
|
||||
.descriptor_count = sectors,
|
||||
.reserved = 0,
|
||||
.flushed_file_offset = bdrv_getlength(bs->file->bs),
|
||||
.last_file_offset = bdrv_getlength(bs->file->bs),
|
||||
.flushed_file_offset = bdrv_getlength(bs->file),
|
||||
.last_file_offset = bdrv_getlength(bs->file),
|
||||
};
|
||||
|
||||
new_hdr.log_guid = header->log_guid;
|
||||
@@ -941,7 +940,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
|
||||
if (i == 0 && leading_length) {
|
||||
/* partial sector at the front of the buffer */
|
||||
ret = bdrv_pread(bs->file->bs, file_offset, merged_sector,
|
||||
ret = bdrv_pread(bs->file, file_offset, merged_sector,
|
||||
VHDX_LOG_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
@@ -951,7 +950,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
sector_write = merged_sector;
|
||||
} else if (i == sectors - 1 && trailing_length) {
|
||||
/* partial sector at the end of the buffer */
|
||||
ret = bdrv_pread(bs->file->bs,
|
||||
ret = bdrv_pread(bs->file,
|
||||
file_offset,
|
||||
merged_sector + trailing_length,
|
||||
VHDX_LOG_SECTOR_SIZE - trailing_length);
|
||||
|
||||
+19
-21
@@ -375,7 +375,7 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
inactive_header->log_guid = *log_guid;
|
||||
}
|
||||
|
||||
ret = vhdx_write_header(bs->file->bs, inactive_header, header_offset, true);
|
||||
ret = vhdx_write_header(bs->file, inactive_header, header_offset, true);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -427,8 +427,7 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
/* We have to read the whole VHDX_HEADER_SIZE instead of
|
||||
* sizeof(VHDXHeader), because the checksum is over the whole
|
||||
* region */
|
||||
ret = bdrv_pread(bs->file->bs, VHDX_HEADER1_OFFSET, buffer,
|
||||
VHDX_HEADER_SIZE);
|
||||
ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, VHDX_HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -444,8 +443,7 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
}
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, VHDX_HEADER2_OFFSET, buffer,
|
||||
VHDX_HEADER_SIZE);
|
||||
ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -518,7 +516,7 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
* whole block */
|
||||
buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE);
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, VHDX_REGION_TABLE_OFFSET, buffer,
|
||||
ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer,
|
||||
VHDX_HEADER_BLOCK_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -631,7 +629,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
|
||||
buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE);
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->metadata_rt.file_offset, buffer,
|
||||
ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer,
|
||||
VHDX_METADATA_TABLE_MAX_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
@@ -734,7 +732,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs,
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->metadata_entries.file_parameters_entry.offset
|
||||
+ s->metadata_rt.file_offset,
|
||||
&s->params,
|
||||
@@ -769,7 +767,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
/* determine virtual disk size, logical sector size,
|
||||
* and phys sector size */
|
||||
|
||||
ret = bdrv_pread(bs->file->bs,
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->metadata_entries.virtual_disk_size_entry.offset
|
||||
+ s->metadata_rt.file_offset,
|
||||
&s->virtual_disk_size,
|
||||
@@ -777,7 +775,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pread(bs->file->bs,
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->metadata_entries.logical_sector_size_entry.offset
|
||||
+ s->metadata_rt.file_offset,
|
||||
&s->logical_sector_size,
|
||||
@@ -785,7 +783,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pread(bs->file->bs,
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->metadata_entries.phys_sector_size_entry.offset
|
||||
+ s->metadata_rt.file_offset,
|
||||
&s->physical_sector_size,
|
||||
@@ -908,7 +906,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
QLIST_INIT(&s->regions);
|
||||
|
||||
/* validate the file signature */
|
||||
ret = bdrv_pread(bs->file->bs, 0, &signature, sizeof(uint64_t));
|
||||
ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -961,13 +959,13 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* s->bat is freed in vhdx_close() */
|
||||
s->bat = qemu_try_blockalign(bs->file->bs, s->bat_rt.length);
|
||||
s->bat = qemu_try_blockalign(bs->file, s->bat_rt.length);
|
||||
if (s->bat == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->bat_offset, s->bat, s->bat_rt.length);
|
||||
ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1120,7 +1118,7 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
break;
|
||||
case PAYLOAD_BLOCK_FULLY_PRESENT:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_readv(bs->file->bs,
|
||||
ret = bdrv_co_readv(bs->file,
|
||||
sinfo.file_offset >> BDRV_SECTOR_BITS,
|
||||
sinfo.sectors_avail, &hd_qiov);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
@@ -1158,12 +1156,12 @@ exit:
|
||||
static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
uint64_t *new_offset)
|
||||
{
|
||||
*new_offset = bdrv_getlength(bs->file->bs);
|
||||
*new_offset = bdrv_getlength(bs->file);
|
||||
|
||||
/* per the spec, the address for a block is in units of 1MB */
|
||||
*new_offset = ROUND_UP(*new_offset, 1024 * 1024);
|
||||
|
||||
return bdrv_truncate(bs->file->bs, *new_offset + s->block_size);
|
||||
return bdrv_truncate(bs->file, *new_offset + s->block_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1262,7 +1260,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
/* Queue another write of zero buffers if the underlying file
|
||||
* does not zero-fill on file extension */
|
||||
|
||||
if (bdrv_has_zero_init(bs->file->bs) == 0) {
|
||||
if (bdrv_has_zero_init(bs->file) == 0) {
|
||||
use_zero_buffers = true;
|
||||
|
||||
/* zero fill the front, if any */
|
||||
@@ -1329,7 +1327,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
/* block exists, so we can just overwrite it */
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_writev(bs->file->bs,
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
sinfo.file_offset >> BDRV_SECTOR_BITS,
|
||||
sectors_to_write, &hd_qiov);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
@@ -1456,7 +1454,7 @@ static int vhdx_create_new_metadata(BlockDriverState *bs,
|
||||
uint32_t offset = 0;
|
||||
void *buffer = NULL;
|
||||
void *entry_buffer;
|
||||
VHDXMetadataTableHeader *md_table;
|
||||
VHDXMetadataTableHeader *md_table;;
|
||||
VHDXMetadataTableEntry *md_table_entry;
|
||||
|
||||
/* Metadata entries */
|
||||
@@ -1844,7 +1842,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
|
||||
+75
-93
@@ -87,7 +87,7 @@ typedef struct {
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct VmdkExtent {
|
||||
BdrvChild *file;
|
||||
BlockDriverState *file;
|
||||
bool flat;
|
||||
bool compressed;
|
||||
bool has_marker;
|
||||
@@ -222,7 +222,7 @@ static void vmdk_free_extents(BlockDriverState *bs)
|
||||
g_free(e->l1_backup_table);
|
||||
g_free(e->type);
|
||||
if (e->file != bs->file) {
|
||||
bdrv_unref_child(bs, e->file);
|
||||
bdrv_unref(e->file);
|
||||
}
|
||||
}
|
||||
g_free(s->extents);
|
||||
@@ -248,7 +248,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -278,7 +278,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -297,7 +297,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -308,11 +308,10 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
BlockDriverState *p_bs = bs->backing_hd;
|
||||
uint32_t cur_pcid;
|
||||
|
||||
if (!s->cid_checked && bs->backing) {
|
||||
BlockDriverState *p_bs = bs->backing->bs;
|
||||
|
||||
if (!s->cid_checked && p_bs) {
|
||||
cur_pcid = vmdk_read_cid(p_bs, 0);
|
||||
if (s->parent_cid != cur_pcid) {
|
||||
/* CID not valid */
|
||||
@@ -341,7 +340,7 @@ static int vmdk_parent_open(BlockDriverState *bs)
|
||||
int ret;
|
||||
|
||||
desc[DESC_SIZE] = '\0';
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -368,7 +367,7 @@ static int vmdk_parent_open(BlockDriverState *bs)
|
||||
/* Create and append extent to the extent array. Return the added VmdkExtent
|
||||
* address. return NULL if allocation failed. */
|
||||
static int vmdk_add_extent(BlockDriverState *bs,
|
||||
BdrvChild *file, bool flat, int64_t sectors,
|
||||
BlockDriverState *file, bool flat, int64_t sectors,
|
||||
int64_t l1_offset, int64_t l1_backup_offset,
|
||||
uint32_t l1_size,
|
||||
int l2_size, uint64_t cluster_sectors,
|
||||
@@ -393,7 +392,7 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
nb_sectors = bdrv_nb_sectors(file->bs);
|
||||
nb_sectors = bdrv_nb_sectors(file);
|
||||
if (nb_sectors < 0) {
|
||||
return nb_sectors;
|
||||
}
|
||||
@@ -440,14 +439,14 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(extent->file->bs,
|
||||
ret = bdrv_pread(extent->file,
|
||||
extent->l1_table_offset,
|
||||
extent->l1_table,
|
||||
l1_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read l1 table from extent '%s'",
|
||||
extent->file->bs->filename);
|
||||
extent->file->filename);
|
||||
goto fail_l1;
|
||||
}
|
||||
for (i = 0; i < extent->l1_size; i++) {
|
||||
@@ -460,14 +459,14 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
ret = -ENOMEM;
|
||||
goto fail_l1;
|
||||
}
|
||||
ret = bdrv_pread(extent->file->bs,
|
||||
ret = bdrv_pread(extent->file,
|
||||
extent->l1_backup_table_offset,
|
||||
extent->l1_backup_table,
|
||||
l1_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read l1 backup table from extent '%s'",
|
||||
extent->file->bs->filename);
|
||||
extent->file->filename);
|
||||
goto fail_l1b;
|
||||
}
|
||||
for (i = 0; i < extent->l1_size; i++) {
|
||||
@@ -486,7 +485,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
}
|
||||
|
||||
static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
||||
BdrvChild *file,
|
||||
BlockDriverState *file,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
@@ -494,11 +493,11 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
||||
VMDK3Header header;
|
||||
VmdkExtent *extent;
|
||||
|
||||
ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header));
|
||||
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read header from file '%s'",
|
||||
file->bs->filename);
|
||||
file->filename);
|
||||
return ret;
|
||||
}
|
||||
ret = vmdk_add_extent(bs, file, false,
|
||||
@@ -560,7 +559,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||
}
|
||||
|
||||
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
BdrvChild *file,
|
||||
BlockDriverState *file,
|
||||
int flags, QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
@@ -570,19 +569,18 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
VmdkExtent *extent;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int64_t l1_backup_offset = 0;
|
||||
bool compressed;
|
||||
|
||||
ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header));
|
||||
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read header from file '%s'",
|
||||
file->bs->filename);
|
||||
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->bs, desc_offset << 9, errp);
|
||||
char *buf = vmdk_read_desc(file, desc_offset << 9, errp);
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -622,8 +620,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
} QEMU_PACKED eos_marker;
|
||||
} QEMU_PACKED footer;
|
||||
|
||||
ret = bdrv_pread(file->bs,
|
||||
bs->file->bs->total_sectors * 512 - 1536,
|
||||
ret = bdrv_pread(file,
|
||||
bs->file->total_sectors * 512 - 1536,
|
||||
&footer, sizeof(footer));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to read footer");
|
||||
@@ -645,8 +643,6 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
header = footer.header;
|
||||
}
|
||||
|
||||
compressed =
|
||||
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;
|
||||
if (le32_to_cpu(header.version) > 3) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "VMDK version %" PRId32,
|
||||
@@ -654,8 +650,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bdrv_get_device_or_node_name(bs), "vmdk", buf);
|
||||
return -ENOTSUP;
|
||||
} else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR) &&
|
||||
!compressed) {
|
||||
} 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
|
||||
@@ -680,7 +675,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {
|
||||
l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;
|
||||
}
|
||||
if (bdrv_nb_sectors(file->bs) < le64_to_cpu(header.grain_offset)) {
|
||||
if (bdrv_nb_sectors(file) < le64_to_cpu(header.grain_offset)) {
|
||||
error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes",
|
||||
(int64_t)(le64_to_cpu(header.grain_offset)
|
||||
* BDRV_SECTOR_SIZE));
|
||||
@@ -744,7 +739,8 @@ 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, BdrvChild *file, int flags,
|
||||
static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
BlockDriverState *file, int flags,
|
||||
char *buf, QDict *options, Error **errp)
|
||||
{
|
||||
uint32_t magic;
|
||||
@@ -777,11 +773,10 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
int64_t sectors = 0;
|
||||
int64_t flat_offset;
|
||||
char *extent_path;
|
||||
BdrvChild *extent_file;
|
||||
BlockDriverState *extent_file;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent;
|
||||
char extent_opt_prefix[32];
|
||||
Error *local_err = NULL;
|
||||
|
||||
while (*p) {
|
||||
/* parse extent line in one of below formats:
|
||||
@@ -824,22 +819,22 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
!desc_file_path[0])
|
||||
{
|
||||
error_setg(errp, "Cannot use relative extent paths with VMDK "
|
||||
"descriptor file '%s'", bs->file->bs->filename);
|
||||
"descriptor file '%s'", bs->file->filename);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
extent_path = g_malloc0(PATH_MAX);
|
||||
path_combine(extent_path, PATH_MAX, desc_file_path, fname);
|
||||
extent_file = NULL;
|
||||
|
||||
ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
|
||||
assert(ret < 32);
|
||||
|
||||
extent_file = bdrv_open_child(extent_path, options, extent_opt_prefix,
|
||||
bs, &child_file, false, &local_err);
|
||||
ret = bdrv_open_image(&extent_file, extent_path, options,
|
||||
extent_opt_prefix, bs, &child_file, false, errp);
|
||||
g_free(extent_path);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* save to extents array */
|
||||
@@ -849,13 +844,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
ret = vmdk_add_extent(bs, extent_file, true, sectors,
|
||||
0, 0, 0, 0, 0, &extent, errp);
|
||||
if (ret < 0) {
|
||||
bdrv_unref_child(bs, extent_file);
|
||||
bdrv_unref(extent_file);
|
||||
return ret;
|
||||
}
|
||||
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->bs, 0, errp);
|
||||
char *buf = vmdk_read_desc(extent_file, 0, errp);
|
||||
if (!buf) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
@@ -864,13 +859,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
}
|
||||
g_free(buf);
|
||||
if (ret) {
|
||||
bdrv_unref_child(bs, extent_file);
|
||||
bdrv_unref(extent_file);
|
||||
return ret;
|
||||
}
|
||||
extent = &s->extents[s->num_extents - 1];
|
||||
} else {
|
||||
error_setg(errp, "Unsupported extent type '%s'", type);
|
||||
bdrv_unref_child(bs, extent_file);
|
||||
bdrv_unref(extent_file);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
extent->type = g_strdup(type);
|
||||
@@ -910,8 +905,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
}
|
||||
s->create_type = g_strdup(ct);
|
||||
s->desc_offset = 0;
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->bs->exact_filename, options,
|
||||
errp);
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@@ -924,7 +918,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
uint32_t magic;
|
||||
|
||||
buf = vmdk_read_desc(bs->file->bs, 0, errp);
|
||||
buf = vmdk_read_desc(bs->file, 0, errp);
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -933,8 +927,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
switch (magic) {
|
||||
case VMDK3_MAGIC:
|
||||
case VMDK4_MAGIC:
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, options,
|
||||
errp);
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp);
|
||||
s->desc_offset = 0x200;
|
||||
break;
|
||||
default:
|
||||
@@ -1011,7 +1004,7 @@ static int get_whole_cluster(BlockDriverState *bs,
|
||||
cluster_bytes = extent->cluster_sectors << BDRV_SECTOR_BITS;
|
||||
whole_grain = qemu_blockalign(bs, cluster_bytes);
|
||||
|
||||
if (!bs->backing) {
|
||||
if (!bs->backing_hd) {
|
||||
memset(whole_grain, 0, skip_start_sector << BDRV_SECTOR_BITS);
|
||||
memset(whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), 0,
|
||||
cluster_bytes - (skip_end_sector << BDRV_SECTOR_BITS));
|
||||
@@ -1020,22 +1013,22 @@ static int get_whole_cluster(BlockDriverState *bs,
|
||||
assert(skip_end_sector <= extent->cluster_sectors);
|
||||
/* we will be here if it's first write on non-exist grain(cluster).
|
||||
* try to read from parent image, if exist */
|
||||
if (bs->backing && !vmdk_is_cid_valid(bs)) {
|
||||
if (bs->backing_hd && !vmdk_is_cid_valid(bs)) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read backing data before skip range */
|
||||
if (skip_start_sector > 0) {
|
||||
if (bs->backing) {
|
||||
ret = bdrv_read(bs->backing->bs, sector_num,
|
||||
if (bs->backing_hd) {
|
||||
ret = bdrv_read(bs->backing_hd, sector_num,
|
||||
whole_grain, skip_start_sector);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ret = bdrv_write(extent->file->bs, cluster_sector_num, whole_grain,
|
||||
ret = bdrv_write(extent->file, cluster_sector_num, whole_grain,
|
||||
skip_start_sector);
|
||||
if (ret < 0) {
|
||||
ret = VMDK_ERROR;
|
||||
@@ -1044,8 +1037,8 @@ static int get_whole_cluster(BlockDriverState *bs,
|
||||
}
|
||||
/* Read backing data after skip range */
|
||||
if (skip_end_sector < extent->cluster_sectors) {
|
||||
if (bs->backing) {
|
||||
ret = bdrv_read(bs->backing->bs, sector_num + skip_end_sector,
|
||||
if (bs->backing_hd) {
|
||||
ret = bdrv_read(bs->backing_hd, sector_num + skip_end_sector,
|
||||
whole_grain + (skip_end_sector << BDRV_SECTOR_BITS),
|
||||
extent->cluster_sectors - skip_end_sector);
|
||||
if (ret < 0) {
|
||||
@@ -1053,7 +1046,7 @@ static int get_whole_cluster(BlockDriverState *bs,
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ret = bdrv_write(extent->file->bs, cluster_sector_num + skip_end_sector,
|
||||
ret = bdrv_write(extent->file, cluster_sector_num + skip_end_sector,
|
||||
whole_grain + (skip_end_sector << BDRV_SECTOR_BITS),
|
||||
extent->cluster_sectors - skip_end_sector);
|
||||
if (ret < 0) {
|
||||
@@ -1073,7 +1066,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
|
||||
offset = cpu_to_le32(offset);
|
||||
/* update L2 table */
|
||||
if (bdrv_pwrite_sync(
|
||||
extent->file->bs,
|
||||
extent->file,
|
||||
((int64_t)m_data->l2_offset * 512)
|
||||
+ (m_data->l2_index * sizeof(offset)),
|
||||
&offset, sizeof(offset)) < 0) {
|
||||
@@ -1083,7 +1076,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
|
||||
if (extent->l1_backup_table_offset != 0) {
|
||||
m_data->l2_offset = extent->l1_backup_table[m_data->l1_index];
|
||||
if (bdrv_pwrite_sync(
|
||||
extent->file->bs,
|
||||
extent->file,
|
||||
((int64_t)m_data->l2_offset * 512)
|
||||
+ (m_data->l2_index * sizeof(offset)),
|
||||
&offset, sizeof(offset)) < 0) {
|
||||
@@ -1173,7 +1166,7 @@ static int get_cluster_offset(BlockDriverState *bs,
|
||||
}
|
||||
l2_table = extent->l2_cache + (min_index * extent->l2_size);
|
||||
if (bdrv_pread(
|
||||
extent->file->bs,
|
||||
extent->file,
|
||||
(int64_t)l2_offset * 512,
|
||||
l2_table,
|
||||
extent->l2_size * sizeof(uint32_t)
|
||||
@@ -1327,16 +1320,12 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
|
||||
write_len = buf_len + sizeof(VmdkGrainMarker);
|
||||
}
|
||||
write_offset = cluster_offset + offset_in_cluster,
|
||||
ret = bdrv_pwrite(extent->file->bs, write_offset, write_buf, write_len);
|
||||
ret = bdrv_pwrite(extent->file, write_offset, write_buf, write_len);
|
||||
|
||||
write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (extent->compressed) {
|
||||
extent->next_cluster_sector = write_end_sector;
|
||||
} else {
|
||||
extent->next_cluster_sector = MAX(extent->next_cluster_sector,
|
||||
write_end_sector);
|
||||
}
|
||||
extent->next_cluster_sector = MAX(extent->next_cluster_sector,
|
||||
write_end_sector);
|
||||
|
||||
if (ret != write_len) {
|
||||
ret = ret < 0 ? ret : -EIO;
|
||||
@@ -1362,7 +1351,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
|
||||
|
||||
|
||||
if (!extent->compressed) {
|
||||
ret = bdrv_pread(extent->file->bs,
|
||||
ret = bdrv_pread(extent->file,
|
||||
cluster_offset + offset_in_cluster,
|
||||
buf, nb_sectors * 512);
|
||||
if (ret == nb_sectors * 512) {
|
||||
@@ -1376,7 +1365,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
|
||||
buf_bytes = cluster_bytes * 2;
|
||||
cluster_buf = g_malloc(buf_bytes);
|
||||
uncomp_buf = g_malloc(cluster_bytes);
|
||||
ret = bdrv_pread(extent->file->bs,
|
||||
ret = bdrv_pread(extent->file,
|
||||
cluster_offset,
|
||||
cluster_buf, buf_bytes);
|
||||
if (ret < 0) {
|
||||
@@ -1438,11 +1427,11 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
if (ret != VMDK_OK) {
|
||||
/* if not allocated, try to read from parent image, if exist */
|
||||
if (bs->backing && ret != VMDK_ZEROED) {
|
||||
if (bs->backing_hd && ret != VMDK_ZEROED) {
|
||||
if (!vmdk_is_cid_valid(bs)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = bdrv_read(bs->backing->bs, sector_num, buf, n);
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -1643,7 +1632,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
|
||||
assert(bs == NULL);
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
@@ -1658,13 +1647,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
}
|
||||
magic = cpu_to_be32(VMDK4_MAGIC);
|
||||
memset(&header, 0, sizeof(header));
|
||||
if (compress) {
|
||||
header.version = 3;
|
||||
} else if (zeroed_grain) {
|
||||
header.version = 2;
|
||||
} else {
|
||||
header.version = 1;
|
||||
}
|
||||
header.version = zeroed_grain ? 2 : 1;
|
||||
header.flags = VMDK4_FLAG_RGD | VMDK4_FLAG_NL_DETECT
|
||||
| (compress ? VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : 0)
|
||||
| (zeroed_grain ? VMDK4_FLAG_ZERO_GRAIN : 0);
|
||||
@@ -1922,7 +1905,8 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ret = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, errp);
|
||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, NULL,
|
||||
errp);
|
||||
g_free(full_backing);
|
||||
if (ret != 0) {
|
||||
goto exit;
|
||||
@@ -1993,7 +1977,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
assert(new_bs == NULL);
|
||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
@@ -2048,7 +2032,7 @@ static coroutine_fn int vmdk_co_flush(BlockDriverState *bs)
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
err = bdrv_co_flush(s->extents[i].file->bs);
|
||||
err = bdrv_co_flush(s->extents[i].file);
|
||||
if (err < 0) {
|
||||
ret = err;
|
||||
}
|
||||
@@ -2063,7 +2047,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
|
||||
int64_t r;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
||||
ret = bdrv_get_allocated_file_size(bs->file->bs);
|
||||
ret = bdrv_get_allocated_file_size(bs->file);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -2071,7 +2055,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
|
||||
if (s->extents[i].file == bs->file) {
|
||||
continue;
|
||||
}
|
||||
r = bdrv_get_allocated_file_size(s->extents[i].file->bs);
|
||||
r = bdrv_get_allocated_file_size(s->extents[i].file);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
@@ -2089,7 +2073,7 @@ static int vmdk_has_zero_init(BlockDriverState *bs)
|
||||
* return 0. */
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
if (s->extents[i].flat) {
|
||||
if (!bdrv_has_zero_init(s->extents[i].file->bs)) {
|
||||
if (!bdrv_has_zero_init(s->extents[i].file)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2102,7 +2086,7 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
|
||||
ImageInfo *info = g_new0(ImageInfo, 1);
|
||||
|
||||
*info = (ImageInfo){
|
||||
.filename = g_strdup(extent->file->bs->filename),
|
||||
.filename = g_strdup(extent->file->filename),
|
||||
.format = g_strdup(extent->type),
|
||||
.virtual_size = extent->sectors * BDRV_SECTOR_SIZE,
|
||||
.compressed = extent->compressed,
|
||||
@@ -2148,9 +2132,7 @@ static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
PRId64 "\n", sector_num);
|
||||
break;
|
||||
}
|
||||
if (ret == VMDK_OK &&
|
||||
cluster_offset >= bdrv_getlength(extent->file->bs))
|
||||
{
|
||||
if (ret == VMDK_OK && cluster_offset >= bdrv_getlength(extent->file)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: cluster offset for sector %"
|
||||
PRId64 " points after EOF\n", sector_num);
|
||||
@@ -2171,19 +2153,19 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
||||
ImageInfoList **next;
|
||||
|
||||
*spec_info = (ImageInfoSpecific){
|
||||
.type = IMAGE_INFO_SPECIFIC_KIND_VMDK,
|
||||
.kind = IMAGE_INFO_SPECIFIC_KIND_VMDK,
|
||||
{
|
||||
.vmdk = g_new0(ImageInfoSpecificVmdk, 1),
|
||||
},
|
||||
};
|
||||
|
||||
*spec_info->u.vmdk = (ImageInfoSpecificVmdk) {
|
||||
*spec_info->vmdk = (ImageInfoSpecificVmdk) {
|
||||
.create_type = g_strdup(s->create_type),
|
||||
.cid = s->cid,
|
||||
.parent_cid = s->parent_cid,
|
||||
};
|
||||
|
||||
next = &spec_info->u.vmdk->extents;
|
||||
next = &spec_info->vmdk->extents;
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
*next = g_new0(ImageInfoList, 1);
|
||||
(*next)->value = vmdk_get_extent_info(&s->extents[i]);
|
||||
@@ -2226,7 +2208,7 @@ static void vmdk_detach_aio_context(BlockDriverState *bs)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
bdrv_detach_aio_context(s->extents[i].file->bs);
|
||||
bdrv_detach_aio_context(s->extents[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2237,7 +2219,7 @@ static void vmdk_attach_aio_context(BlockDriverState *bs,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
bdrv_attach_aio_context(s->extents[i].file->bs, new_context);
|
||||
bdrv_attach_aio_context(s->extents[i].file, new_context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-19
@@ -172,14 +172,14 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int disk_type = VHD_DYNAMIC;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE);
|
||||
ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
footer = (VHDFooter *) s->footer_buf;
|
||||
if (strncmp(footer->creator, "conectix", 8)) {
|
||||
int64_t offset = bdrv_getlength(bs->file->bs);
|
||||
int64_t offset = bdrv_getlength(bs->file);
|
||||
if (offset < 0) {
|
||||
ret = offset;
|
||||
goto fail;
|
||||
@@ -189,7 +189,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* If a fixed disk, the footer is found only at the end of the file */
|
||||
ret = bdrv_pread(bs->file->bs, offset-HEADER_SIZE, s->footer_buf,
|
||||
ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf,
|
||||
HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -232,7 +232,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
ret = bdrv_pread(bs->file->bs, be64_to_cpu(footer->data_offset), buf,
|
||||
ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf,
|
||||
HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -280,7 +280,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
pagetable_size = (uint64_t) s->max_table_entries * 4;
|
||||
|
||||
s->pagetable = qemu_try_blockalign(bs->file->bs, pagetable_size);
|
||||
s->pagetable = qemu_try_blockalign(bs->file, pagetable_size);
|
||||
if (s->pagetable == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
@@ -288,8 +288,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, s->bat_offset, s->pagetable,
|
||||
pagetable_size);
|
||||
ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -309,7 +308,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
if (s->free_data_block_offset > bdrv_getlength(bs->file->bs)) {
|
||||
if (s->free_data_block_offset > bdrv_getlength(bs->file)) {
|
||||
error_setg(errp, "block-vpc: free_data_block_offset points after "
|
||||
"the end of file. The image has been truncated.");
|
||||
ret = -EINVAL;
|
||||
@@ -384,7 +383,7 @@ static inline int64_t get_sector_offset(BlockDriverState *bs,
|
||||
|
||||
s->last_bitmap_offset = bitmap_offset;
|
||||
memset(bitmap, 0xff, s->bitmap_size);
|
||||
bdrv_pwrite_sync(bs->file->bs, bitmap_offset, bitmap, s->bitmap_size);
|
||||
bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
|
||||
}
|
||||
|
||||
return block_offset;
|
||||
@@ -402,7 +401,7 @@ static int rewrite_footer(BlockDriverState* bs)
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int64_t offset = s->free_data_block_offset;
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, offset, s->footer_buf, HEADER_SIZE);
|
||||
ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -437,7 +436,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
|
||||
|
||||
// Initialize the block's bitmap
|
||||
memset(bitmap, 0xff, s->bitmap_size);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, s->free_data_block_offset, bitmap,
|
||||
ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap,
|
||||
s->bitmap_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -452,7 +451,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
|
||||
// Write BAT entry to disk
|
||||
bat_offset = s->bat_offset + (4 * index);
|
||||
bat_value = cpu_to_be32(s->pagetable[index]);
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, bat_offset, &bat_value, 4);
|
||||
ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
@@ -486,7 +485,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
return bdrv_read(bs->file->bs, sector_num, buf, nb_sectors);
|
||||
return bdrv_read(bs->file, sector_num, buf, nb_sectors);
|
||||
}
|
||||
while (nb_sectors > 0) {
|
||||
offset = get_sector_offset(bs, sector_num, 0);
|
||||
@@ -500,7 +499,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
if (offset == -1) {
|
||||
memset(buf, 0, sectors * BDRV_SECTOR_SIZE);
|
||||
} else {
|
||||
ret = bdrv_pread(bs->file->bs, offset, buf,
|
||||
ret = bdrv_pread(bs->file, offset, buf,
|
||||
sectors * BDRV_SECTOR_SIZE);
|
||||
if (ret != sectors * BDRV_SECTOR_SIZE) {
|
||||
return -1;
|
||||
@@ -535,7 +534,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
return bdrv_write(bs->file->bs, sector_num, buf, nb_sectors);
|
||||
return bdrv_write(bs->file, sector_num, buf, nb_sectors);
|
||||
}
|
||||
while (nb_sectors > 0) {
|
||||
offset = get_sector_offset(bs, sector_num, 1);
|
||||
@@ -552,8 +551,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, buf,
|
||||
sectors * BDRV_SECTOR_SIZE);
|
||||
ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE);
|
||||
if (ret != sectors * BDRV_SECTOR_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
@@ -796,7 +794,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
@@ -880,7 +878,7 @@ static int vpc_has_zero_init(BlockDriverState *bs)
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
return bdrv_has_zero_init(bs->file->bs);
|
||||
return bdrv_has_zero_init(bs->file);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
+13
-14
@@ -985,6 +985,12 @@ static BDRVVVFATState *vvv = NULL;
|
||||
static int enable_write_target(BDRVVVFATState *s, Error **errp);
|
||||
static int is_consistent(BDRVVVFATState *s);
|
||||
|
||||
static void vvfat_rebind(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
s->bs = bs;
|
||||
}
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "vvfat",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
@@ -2917,12 +2923,9 @@ static BlockDriver vvfat_write_target = {
|
||||
static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
{
|
||||
BlockDriver *bdrv_qcow = NULL;
|
||||
BlockDriverState *backing;
|
||||
QemuOpts *opts = NULL;
|
||||
int ret;
|
||||
int size = sector2cluster(s, s->sector_count);
|
||||
QDict *options;
|
||||
|
||||
s->used_clusters = calloc(size, 1);
|
||||
|
||||
array_init(&(s->commits), sizeof(commit_t));
|
||||
@@ -2953,11 +2956,9 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
}
|
||||
|
||||
s->qcow = NULL;
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str("qcow"));
|
||||
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, options,
|
||||
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
errp);
|
||||
bdrv_qcow, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -2966,13 +2967,10 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||
unlink(s->qcow_filename);
|
||||
#endif
|
||||
|
||||
backing = bdrv_new();
|
||||
bdrv_set_backing_hd(s->bs, backing);
|
||||
bdrv_unref(backing);
|
||||
|
||||
s->bs->backing->bs->drv = &vvfat_write_target;
|
||||
s->bs->backing->bs->opaque = g_new(void *, 1);
|
||||
*(void**)s->bs->backing->bs->opaque = s;
|
||||
bdrv_set_backing_hd(s->bs, bdrv_new());
|
||||
s->bs->backing_hd->drv = &vvfat_write_target;
|
||||
s->bs->backing_hd->opaque = g_new(void *, 1);
|
||||
*(void**)s->bs->backing_hd->opaque = s;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -3006,6 +3004,7 @@ static BlockDriver bdrv_vvfat = {
|
||||
.bdrv_parse_filename = vvfat_parse_filename,
|
||||
.bdrv_file_open = vvfat_open,
|
||||
.bdrv_close = vvfat_close,
|
||||
.bdrv_rebind = vvfat_rebind,
|
||||
|
||||
.bdrv_read = vvfat_co_read,
|
||||
.bdrv_write = vvfat_co_write,
|
||||
|
||||
+2
-3
@@ -174,7 +174,7 @@ 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, false, NULL);
|
||||
aio_set_event_notifier(old_context, &aio->e, NULL);
|
||||
aio->is_aio_context_attached = false;
|
||||
}
|
||||
|
||||
@@ -182,8 +182,7 @@ 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, false,
|
||||
win32_aio_completion_cb);
|
||||
aio_set_event_notifier(new_context, &aio->e, win32_aio_completion_cb);
|
||||
}
|
||||
|
||||
QEMUWin32AIOState *win32_aio_init(void)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "block/write-threshold.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
+490
-1308
File diff suppressed because it is too large.
Load diff
+40
-169
@@ -29,27 +29,13 @@
|
||||
#include "block/block.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
/* Transactional group of block jobs */
|
||||
struct BlockJobTxn {
|
||||
|
||||
/* Is this txn being cancelled? */
|
||||
bool aborting;
|
||||
|
||||
/* List of jobs */
|
||||
QLIST_HEAD(, BlockJob) jobs;
|
||||
|
||||
/* Reference count */
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
@@ -68,12 +54,10 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
|
||||
|
||||
job->driver = driver;
|
||||
job->id = g_strdup(bdrv_get_device_name(bs));
|
||||
job->bs = bs;
|
||||
job->cb = cb;
|
||||
job->opaque = opaque;
|
||||
job->busy = true;
|
||||
job->refcnt = 1;
|
||||
bs->job = job;
|
||||
|
||||
/* Only set speed when necessary to avoid NotSupported error */
|
||||
@@ -82,7 +66,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
block_job_unref(job);
|
||||
block_job_release(bs);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
@@ -90,101 +74,14 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
return job;
|
||||
}
|
||||
|
||||
void block_job_ref(BlockJob *job)
|
||||
void block_job_release(BlockDriverState *bs)
|
||||
{
|
||||
++job->refcnt;
|
||||
}
|
||||
BlockJob *job = bs->job;
|
||||
|
||||
void block_job_unref(BlockJob *job)
|
||||
{
|
||||
if (--job->refcnt == 0) {
|
||||
job->bs->job = NULL;
|
||||
bdrv_op_unblock_all(job->bs, job->blocker);
|
||||
bdrv_unref(job->bs);
|
||||
error_free(job->blocker);
|
||||
g_free(job->id);
|
||||
g_free(job);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_completed_single(BlockJob *job)
|
||||
{
|
||||
if (!job->ret) {
|
||||
if (job->driver->commit) {
|
||||
job->driver->commit(job);
|
||||
}
|
||||
} else {
|
||||
if (job->driver->abort) {
|
||||
job->driver->abort(job);
|
||||
}
|
||||
}
|
||||
job->cb(job->opaque, job->ret);
|
||||
if (job->txn) {
|
||||
block_job_txn_unref(job->txn);
|
||||
}
|
||||
block_job_unref(job);
|
||||
}
|
||||
|
||||
static void block_job_completed_txn_abort(BlockJob *job)
|
||||
{
|
||||
AioContext *ctx;
|
||||
BlockJobTxn *txn = job->txn;
|
||||
BlockJob *other_job, *next;
|
||||
|
||||
if (txn->aborting) {
|
||||
/*
|
||||
* We are cancelled by another job, which will handle everything.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
txn->aborting = true;
|
||||
/* We are the first failed job. Cancel other jobs. */
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
ctx = bdrv_get_aio_context(other_job->bs);
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
if (other_job == job || other_job->completed) {
|
||||
/* Other jobs are "effectively" cancelled by us, set the status for
|
||||
* them; this job, however, may or may not be cancelled, depending
|
||||
* on the caller, so leave it. */
|
||||
if (other_job != job) {
|
||||
other_job->cancelled = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
block_job_cancel_sync(other_job);
|
||||
assert(other_job->completed);
|
||||
}
|
||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
||||
ctx = bdrv_get_aio_context(other_job->bs);
|
||||
block_job_completed_single(other_job);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_completed_txn_success(BlockJob *job)
|
||||
{
|
||||
AioContext *ctx;
|
||||
BlockJobTxn *txn = job->txn;
|
||||
BlockJob *other_job, *next;
|
||||
/*
|
||||
* Successful completion, see if there are other running jobs in this
|
||||
* txn.
|
||||
*/
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
if (!other_job->completed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* We are the last completed job, commit the transaction. */
|
||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
||||
ctx = bdrv_get_aio_context(other_job->bs);
|
||||
aio_context_acquire(ctx);
|
||||
assert(other_job->ret == 0);
|
||||
block_job_completed_single(other_job);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
bs->job = NULL;
|
||||
bdrv_op_unblock_all(bs, job->blocker);
|
||||
error_free(job->blocker);
|
||||
g_free(job);
|
||||
}
|
||||
|
||||
void block_job_completed(BlockJob *job, int ret)
|
||||
@@ -192,16 +89,8 @@ void block_job_completed(BlockJob *job, int ret)
|
||||
BlockDriverState *bs = job->bs;
|
||||
|
||||
assert(bs->job == job);
|
||||
assert(!job->completed);
|
||||
job->completed = true;
|
||||
job->ret = ret;
|
||||
if (!job->txn) {
|
||||
block_job_completed_single(job);
|
||||
} else if (ret < 0 || block_job_is_cancelled(job)) {
|
||||
block_job_completed_txn_abort(job);
|
||||
} else {
|
||||
block_job_completed_txn_success(job);
|
||||
}
|
||||
job->cb(job->opaque, ret);
|
||||
block_job_release(bs);
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -224,7 +113,8 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
void block_job_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
if (job->pause_count || job->cancelled || !job->driver->complete) {
|
||||
error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
|
||||
error_setg(errp, QERR_BLOCK_JOB_NOT_READY,
|
||||
bdrv_get_device_name(job->bs));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -286,29 +176,43 @@ struct BlockFinishData {
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void block_job_finish_cb(void *opaque, int ret)
|
||||
{
|
||||
struct BlockFinishData *data = opaque;
|
||||
|
||||
data->cancelled = block_job_is_cancelled(data->job);
|
||||
data->ret = ret;
|
||||
data->cb(data->opaque, ret);
|
||||
}
|
||||
|
||||
static int block_job_finish_sync(BlockJob *job,
|
||||
void (*finish)(BlockJob *, Error **errp),
|
||||
Error **errp)
|
||||
{
|
||||
struct BlockFinishData data;
|
||||
BlockDriverState *bs = job->bs;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
assert(bs->job == job);
|
||||
|
||||
block_job_ref(job);
|
||||
/* Set up our own callback to store the result and chain to
|
||||
* the original callback.
|
||||
*/
|
||||
data.job = job;
|
||||
data.cb = job->cb;
|
||||
data.opaque = job->opaque;
|
||||
data.ret = -EINPROGRESS;
|
||||
job->cb = block_job_finish_cb;
|
||||
job->opaque = &data;
|
||||
finish(job, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
block_job_unref(job);
|
||||
return -EBUSY;
|
||||
}
|
||||
while (!job->completed) {
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
||||
block_job_unref(job);
|
||||
return ret;
|
||||
return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
|
||||
}
|
||||
|
||||
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
|
||||
@@ -365,7 +269,7 @@ BlockJobInfo *block_job_query(BlockJob *job)
|
||||
{
|
||||
BlockJobInfo *info = g_new0(BlockJobInfo, 1);
|
||||
info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
|
||||
info->device = g_strdup(job->id);
|
||||
info->device = g_strdup(bdrv_get_device_name(job->bs));
|
||||
info->len = job->len;
|
||||
info->busy = job->busy;
|
||||
info->paused = job->pause_count > 0;
|
||||
@@ -387,7 +291,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
|
||||
void block_job_event_cancelled(BlockJob *job)
|
||||
{
|
||||
qapi_event_send_block_job_cancelled(job->driver->job_type,
|
||||
job->id,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed,
|
||||
@@ -397,7 +301,7 @@ void block_job_event_cancelled(BlockJob *job)
|
||||
void block_job_event_completed(BlockJob *job, const char *msg)
|
||||
{
|
||||
qapi_event_send_block_job_completed(job->driver->job_type,
|
||||
job->id,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed,
|
||||
@@ -411,7 +315,7 @@ void block_job_event_ready(BlockJob *job)
|
||||
job->ready = true;
|
||||
|
||||
qapi_event_send_block_job_ready(job->driver->job_type,
|
||||
job->id,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed, &error_abort);
|
||||
@@ -440,7 +344,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
qapi_event_send_block_job_error(job->id,
|
||||
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);
|
||||
@@ -449,8 +353,8 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
job->user_paused = true;
|
||||
block_job_pause(job);
|
||||
block_job_iostatus_set_err(job, error);
|
||||
if (bs->blk && bs != job->bs) {
|
||||
blk_iostatus_set_err(bs->blk, error);
|
||||
if (bs != job->bs) {
|
||||
bdrv_iostatus_set_err(bs, error);
|
||||
}
|
||||
}
|
||||
return action;
|
||||
@@ -500,36 +404,3 @@ void block_job_defer_to_main_loop(BlockJob *job,
|
||||
|
||||
qemu_bh_schedule(data->bh);
|
||||
}
|
||||
|
||||
BlockJobTxn *block_job_txn_new(void)
|
||||
{
|
||||
BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
|
||||
QLIST_INIT(&txn->jobs);
|
||||
txn->refcnt = 1;
|
||||
return txn;
|
||||
}
|
||||
|
||||
static void block_job_txn_ref(BlockJobTxn *txn)
|
||||
{
|
||||
txn->refcnt++;
|
||||
}
|
||||
|
||||
void block_job_txn_unref(BlockJobTxn *txn)
|
||||
{
|
||||
if (txn && --txn->refcnt == 0) {
|
||||
g_free(txn);
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
|
||||
{
|
||||
if (!txn) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!job->txn);
|
||||
job->txn = txn;
|
||||
|
||||
QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
|
||||
block_job_txn_ref(txn);
|
||||
}
|
||||
+7
-2
@@ -740,7 +740,8 @@ static void padzero(abi_ulong elf_bss, abi_ulong last_bss)
|
||||
size must be known */
|
||||
if (qemu_real_host_page_size < qemu_host_page_size) {
|
||||
abi_ulong end_addr, end_addr1;
|
||||
end_addr1 = REAL_HOST_PAGE_ALIGN(elf_bss);
|
||||
end_addr1 = (elf_bss + qemu_real_host_page_size - 1) &
|
||||
~(qemu_real_host_page_size - 1);
|
||||
end_addr = HOST_PAGE_ALIGN(elf_bss);
|
||||
if (end_addr1 < end_addr) {
|
||||
mmap((void *)g2h(end_addr1), end_addr - end_addr1,
|
||||
@@ -1354,7 +1355,9 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
}
|
||||
}
|
||||
if (!bprm->p) {
|
||||
free(elf_interpreter);
|
||||
if (elf_interpreter) {
|
||||
free(elf_interpreter);
|
||||
}
|
||||
free (elf_phdata);
|
||||
close(bprm->fd);
|
||||
return -E2BIG;
|
||||
@@ -1368,6 +1371,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
info->mmap = 0;
|
||||
elf_entry = (abi_ulong) elf_ex.e_entry;
|
||||
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
/*
|
||||
* In case where user has not explicitly set the guest_base, we
|
||||
* probe here that should we set it automatically.
|
||||
@@ -1388,6 +1392,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USE_GUEST_BASE */
|
||||
|
||||
/* Do this so that we can load the interpreter, if need be. We will
|
||||
change some of these later */
|
||||
|
||||
+13
-1
@@ -35,10 +35,12 @@
|
||||
#include "qemu/envlist.h"
|
||||
|
||||
int singlestep;
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
unsigned long mmap_min_addr;
|
||||
unsigned long guest_base;
|
||||
int have_guest_base;
|
||||
unsigned long reserved_va;
|
||||
#endif
|
||||
|
||||
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
|
||||
const char *qemu_uname_release;
|
||||
@@ -108,7 +110,7 @@ void cpu_list_unlock(void)
|
||||
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return cpu_get_host_ticks();
|
||||
return cpu_get_real_ticks();
|
||||
}
|
||||
|
||||
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
@@ -680,7 +682,9 @@ static void usage(void)
|
||||
"-drop-ld-preload drop LD_PRELOAD for target process\n"
|
||||
"-E var=value sets/modifies targets environment variable(s)\n"
|
||||
"-U var unsets targets environment variable(s)\n"
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
"-B address set guest_base address to address\n"
|
||||
#endif
|
||||
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
|
||||
"\n"
|
||||
"Debug options:\n"
|
||||
@@ -826,9 +830,11 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
} else if (!strcmp(r, "B")) {
|
||||
guest_base = strtol(argv[optind++], NULL, 0);
|
||||
have_guest_base = 1;
|
||||
#endif
|
||||
} else if (!strcmp(r, "drop-ld-preload")) {
|
||||
(void) envlist_unsetenv(envlist, "LD_PRELOAD");
|
||||
} else if (!strcmp(r, "bsd")) {
|
||||
@@ -917,6 +923,7 @@ int main(int argc, char **argv)
|
||||
target_environ = envlist_to_environ(envlist, NULL);
|
||||
envlist_free(envlist);
|
||||
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
/*
|
||||
* Now that page sizes are configured in cpu_init() we can do
|
||||
* proper page alignment for guest_base.
|
||||
@@ -943,6 +950,7 @@ int main(int argc, char **argv)
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USE_GUEST_BASE */
|
||||
|
||||
if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
|
||||
printf("Error loading %s\n", filename);
|
||||
@@ -956,7 +964,9 @@ int main(int argc, char **argv)
|
||||
free(target_environ);
|
||||
|
||||
if (qemu_log_enabled()) {
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
qemu_log("guest_base 0x%lx\n", guest_base);
|
||||
#endif
|
||||
log_page_dump();
|
||||
|
||||
qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
|
||||
@@ -976,10 +986,12 @@ int main(int argc, char **argv)
|
||||
syscall_init();
|
||||
signal_init();
|
||||
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
/* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
|
||||
generating the prologue until now so that the prologue can take
|
||||
the real value of GUEST_BASE into account. */
|
||||
tcg_prologue_init(&tcg_ctx);
|
||||
#endif
|
||||
|
||||
/* build Task State */
|
||||
memset(ts, 0, sizeof(TaskState));
|
||||
|
||||
@@ -101,7 +101,9 @@ typedef struct TaskState {
|
||||
|
||||
void init_task_state(TaskState *ts);
|
||||
extern const char *qemu_uname_release;
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
extern unsigned long mmap_min_addr;
|
||||
#endif
|
||||
|
||||
/* ??? See if we can avoid exposing so much of the loader internals. */
|
||||
/*
|
||||
@@ -211,6 +213,8 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
|
||||
abi_ulong new_addr);
|
||||
int target_msync(abi_ulong start, abi_ulong len, int flags);
|
||||
extern unsigned long last_brk;
|
||||
void mmap_lock(void);
|
||||
void mmap_unlock(void);
|
||||
void cpu_list_lock(void);
|
||||
void cpu_list_unlock(void);
|
||||
#if defined(CONFIG_USE_NPTL)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ivshmem-client-obj-y = ivshmem-client.o main.o
|
||||
@@ -1,446 +0,0 @@
|
||||
/*
|
||||
* Copyright 6WIND S.A., 2014
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/queue.h"
|
||||
|
||||
#include "ivshmem-client.h"
|
||||
|
||||
/* log a message on stdout if verbose=1 */
|
||||
#define IVSHMEM_CLIENT_DEBUG(client, fmt, ...) do { \
|
||||
if ((client)->verbose) { \
|
||||
printf(fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* read message from the unix socket */
|
||||
static int
|
||||
ivshmem_client_read_one_msg(IvshmemClient *client, int64_t *index, int *fd)
|
||||
{
|
||||
int ret;
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
union {
|
||||
struct cmsghdr cmsg;
|
||||
char control[CMSG_SPACE(sizeof(int))];
|
||||
} msg_control;
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
iov[0].iov_base = index;
|
||||
iov[0].iov_len = sizeof(*index);
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &msg_control;
|
||||
msg.msg_controllen = sizeof(msg_control);
|
||||
|
||||
ret = recvmsg(client->sock_fd, &msg, 0);
|
||||
if (ret < sizeof(*index)) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "cannot read message: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "lost connection to server\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*index = GINT64_FROM_LE(*index);
|
||||
*fd = -1;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* free a peer when the server advertises a disconnection or when the
|
||||
* client is freed */
|
||||
static void
|
||||
ivshmem_client_free_peer(IvshmemClient *client, IvshmemClientPeer *peer)
|
||||
{
|
||||
unsigned vector;
|
||||
|
||||
QTAILQ_REMOVE(&client->peer_list, peer, next);
|
||||
for (vector = 0; vector < peer->vectors_count; vector++) {
|
||||
close(peer->vectors[vector]);
|
||||
}
|
||||
|
||||
g_free(peer);
|
||||
}
|
||||
|
||||
/* handle message coming from server (new peer, new vectors) */
|
||||
static int
|
||||
ivshmem_client_handle_server_msg(IvshmemClient *client)
|
||||
{
|
||||
IvshmemClientPeer *peer;
|
||||
int64_t peer_id;
|
||||
int ret, fd;
|
||||
|
||||
ret = ivshmem_client_read_one_msg(client, &peer_id, &fd);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* can return a peer or the local client */
|
||||
peer = ivshmem_client_search_peer(client, peer_id);
|
||||
|
||||
/* delete peer */
|
||||
if (fd == -1) {
|
||||
|
||||
if (peer == NULL || peer == &client->local) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "receive delete for invalid "
|
||||
"peer %" PRId64 "\n", peer_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
IVSHMEM_CLIENT_DEBUG(client, "delete peer id = %" PRId64 "\n", peer_id);
|
||||
ivshmem_client_free_peer(client, peer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new peer */
|
||||
if (peer == NULL) {
|
||||
peer = g_malloc0(sizeof(*peer));
|
||||
peer->id = peer_id;
|
||||
peer->vectors_count = 0;
|
||||
QTAILQ_INSERT_TAIL(&client->peer_list, peer, next);
|
||||
IVSHMEM_CLIENT_DEBUG(client, "new peer id = %" PRId64 "\n", peer_id);
|
||||
}
|
||||
|
||||
/* new vector */
|
||||
IVSHMEM_CLIENT_DEBUG(client, " new vector %d (fd=%d) for peer id %"
|
||||
PRId64 "\n", peer->vectors_count, fd, peer->id);
|
||||
if (peer->vectors_count >= G_N_ELEMENTS(peer->vectors)) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "Too many vectors received, failing");
|
||||
return -1;
|
||||
}
|
||||
|
||||
peer->vectors[peer->vectors_count] = fd;
|
||||
peer->vectors_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init a new ivshmem client */
|
||||
int
|
||||
ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path,
|
||||
IvshmemClientNotifCb notif_cb, void *notif_arg,
|
||||
bool verbose)
|
||||
{
|
||||
int ret;
|
||||
unsigned i;
|
||||
|
||||
memset(client, 0, sizeof(*client));
|
||||
|
||||
ret = snprintf(client->unix_sock_path, sizeof(client->unix_sock_path),
|
||||
"%s", unix_sock_path);
|
||||
|
||||
if (ret < 0 || ret >= sizeof(client->unix_sock_path)) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < IVSHMEM_CLIENT_MAX_VECTORS; i++) {
|
||||
client->local.vectors[i] = -1;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&client->peer_list);
|
||||
client->local.id = -1;
|
||||
|
||||
client->notif_cb = notif_cb;
|
||||
client->notif_arg = notif_arg;
|
||||
client->verbose = verbose;
|
||||
client->shm_fd = -1;
|
||||
client->sock_fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create and connect to the unix socket */
|
||||
int
|
||||
ivshmem_client_connect(IvshmemClient *client)
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
int fd, ret;
|
||||
int64_t tmp;
|
||||
|
||||
IVSHMEM_CLIENT_DEBUG(client, "connect to client %s\n",
|
||||
client->unix_sock_path);
|
||||
|
||||
client->sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (client->sock_fd < 0) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "cannot create socket: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sun.sun_family = AF_UNIX;
|
||||
ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s",
|
||||
client->unix_sock_path);
|
||||
if (ret < 0 || ret >= sizeof(sun.sun_path)) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
if (connect(client->sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", sun.sun_path,
|
||||
strerror(errno));
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
/* first, we expect a protocol version */
|
||||
if (ivshmem_client_read_one_msg(client, &tmp, &fd) < 0 ||
|
||||
(tmp != IVSHMEM_PROTOCOL_VERSION) || fd != -1) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "cannot read from server\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
/* then, we expect our index + a fd == -1 */
|
||||
if (ivshmem_client_read_one_msg(client, &client->local.id, &fd) < 0 ||
|
||||
client->local.id < 0 || fd != -1) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "cannot read from server (2)\n");
|
||||
goto err_close;
|
||||
}
|
||||
IVSHMEM_CLIENT_DEBUG(client, "our_id=%" PRId64 "\n", client->local.id);
|
||||
|
||||
/* now, we expect shared mem fd + a -1 index, note that shm fd
|
||||
* is not used */
|
||||
if (ivshmem_client_read_one_msg(client, &tmp, &fd) < 0 ||
|
||||
tmp != -1 || fd < 0) {
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
IVSHMEM_CLIENT_DEBUG(client, "cannot read from server (3)\n");
|
||||
goto err_close;
|
||||
}
|
||||
client->shm_fd = fd;
|
||||
IVSHMEM_CLIENT_DEBUG(client, "shm_fd=%d\n", fd);
|
||||
|
||||
return 0;
|
||||
|
||||
err_close:
|
||||
close(client->sock_fd);
|
||||
client->sock_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* close connection to the server, and free all peer structures */
|
||||
void
|
||||
ivshmem_client_close(IvshmemClient *client)
|
||||
{
|
||||
IvshmemClientPeer *peer;
|
||||
unsigned i;
|
||||
|
||||
IVSHMEM_CLIENT_DEBUG(client, "close client\n");
|
||||
|
||||
while ((peer = QTAILQ_FIRST(&client->peer_list)) != NULL) {
|
||||
ivshmem_client_free_peer(client, peer);
|
||||
}
|
||||
|
||||
close(client->shm_fd);
|
||||
client->shm_fd = -1;
|
||||
close(client->sock_fd);
|
||||
client->sock_fd = -1;
|
||||
client->local.id = -1;
|
||||
for (i = 0; i < IVSHMEM_CLIENT_MAX_VECTORS; i++) {
|
||||
close(client->local.vectors[i]);
|
||||
client->local.vectors[i] = -1;
|
||||
}
|
||||
client->local.vectors_count = 0;
|
||||
}
|
||||
|
||||
/* get the fd_set according to the unix socket and peer list */
|
||||
void
|
||||
ivshmem_client_get_fds(const IvshmemClient *client, fd_set *fds, int *maxfd)
|
||||
{
|
||||
int fd;
|
||||
unsigned vector;
|
||||
|
||||
FD_SET(client->sock_fd, fds);
|
||||
if (client->sock_fd >= *maxfd) {
|
||||
*maxfd = client->sock_fd + 1;
|
||||
}
|
||||
|
||||
for (vector = 0; vector < client->local.vectors_count; vector++) {
|
||||
fd = client->local.vectors[vector];
|
||||
FD_SET(fd, fds);
|
||||
if (fd >= *maxfd) {
|
||||
*maxfd = fd + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* handle events from eventfd: just print a message on notification */
|
||||
static int
|
||||
ivshmem_client_handle_event(IvshmemClient *client, const fd_set *cur, int maxfd)
|
||||
{
|
||||
IvshmemClientPeer *peer;
|
||||
uint64_t kick;
|
||||
unsigned i;
|
||||
int ret;
|
||||
|
||||
peer = &client->local;
|
||||
|
||||
for (i = 0; i < peer->vectors_count; i++) {
|
||||
if (peer->vectors[i] >= maxfd || !FD_ISSET(peer->vectors[i], cur)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = read(peer->vectors[i], &kick, sizeof(kick));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != sizeof(kick)) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "invalid read size = %d\n", ret);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
IVSHMEM_CLIENT_DEBUG(client, "received event on fd %d vector %d: %"
|
||||
PRIu64 "\n", peer->vectors[i], i, kick);
|
||||
if (client->notif_cb != NULL) {
|
||||
client->notif_cb(client, peer, i, client->notif_arg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read and handle new messages on the given fd_set */
|
||||
int
|
||||
ivshmem_client_handle_fds(IvshmemClient *client, fd_set *fds, int maxfd)
|
||||
{
|
||||
if (client->sock_fd < maxfd && FD_ISSET(client->sock_fd, fds) &&
|
||||
ivshmem_client_handle_server_msg(client) < 0 && errno != EINTR) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "ivshmem_client_handle_server_msg() "
|
||||
"failed\n");
|
||||
return -1;
|
||||
} else if (ivshmem_client_handle_event(client, fds, maxfd) < 0 &&
|
||||
errno != EINTR) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "ivshmem_client_handle_event() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send a notification on a vector of a peer */
|
||||
int
|
||||
ivshmem_client_notify(const IvshmemClient *client,
|
||||
const IvshmemClientPeer *peer, unsigned vector)
|
||||
{
|
||||
uint64_t kick;
|
||||
int fd;
|
||||
|
||||
if (vector >= peer->vectors_count) {
|
||||
IVSHMEM_CLIENT_DEBUG(client, "invalid vector %u on peer %" PRId64 "\n",
|
||||
vector, peer->id);
|
||||
return -1;
|
||||
}
|
||||
fd = peer->vectors[vector];
|
||||
IVSHMEM_CLIENT_DEBUG(client, "notify peer %" PRId64
|
||||
" on vector %d, fd %d\n", peer->id, vector, fd);
|
||||
|
||||
kick = 1;
|
||||
if (write(fd, &kick, sizeof(kick)) != sizeof(kick)) {
|
||||
fprintf(stderr, "could not write to %d: %s\n", peer->vectors[vector],
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send a notification to all vectors of a peer */
|
||||
int
|
||||
ivshmem_client_notify_all_vects(const IvshmemClient *client,
|
||||
const IvshmemClientPeer *peer)
|
||||
{
|
||||
unsigned vector;
|
||||
int ret = 0;
|
||||
|
||||
for (vector = 0; vector < peer->vectors_count; vector++) {
|
||||
if (ivshmem_client_notify(client, peer, vector) < 0) {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* send a notification to all peers */
|
||||
int
|
||||
ivshmem_client_notify_broadcast(const IvshmemClient *client)
|
||||
{
|
||||
IvshmemClientPeer *peer;
|
||||
int ret = 0;
|
||||
|
||||
QTAILQ_FOREACH(peer, &client->peer_list, next) {
|
||||
if (ivshmem_client_notify_all_vects(client, peer) < 0) {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* lookup peer from its id */
|
||||
IvshmemClientPeer *
|
||||
ivshmem_client_search_peer(IvshmemClient *client, int64_t peer_id)
|
||||
{
|
||||
IvshmemClientPeer *peer;
|
||||
|
||||
if (peer_id == client->local.id) {
|
||||
return &client->local;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(peer, &client->peer_list, next) {
|
||||
if (peer->id == peer_id) {
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* dump our info, the list of peers their vectors on stdout */
|
||||
void
|
||||
ivshmem_client_dump(const IvshmemClient *client)
|
||||
{
|
||||
const IvshmemClientPeer *peer;
|
||||
unsigned vector;
|
||||
|
||||
/* dump local infos */
|
||||
peer = &client->local;
|
||||
printf("our_id = %" PRId64 "\n", peer->id);
|
||||
for (vector = 0; vector < peer->vectors_count; vector++) {
|
||||
printf(" vector %d is enabled (fd=%d)\n", vector,
|
||||
peer->vectors[vector]);
|
||||
}
|
||||
|
||||
/* dump peers */
|
||||
QTAILQ_FOREACH(peer, &client->peer_list, next) {
|
||||
printf("peer_id = %" PRId64 "\n", peer->id);
|
||||
|
||||
for (vector = 0; vector < peer->vectors_count; vector++) {
|
||||
printf(" vector %d is enabled (fd=%d)\n", vector,
|
||||
peer->vectors[vector]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright 6WIND S.A., 2014
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef _IVSHMEM_CLIENT_H_
|
||||
#define _IVSHMEM_CLIENT_H_
|
||||
|
||||
/**
|
||||
* This file provides helper to implement an ivshmem client. It is used
|
||||
* on the host to ask QEMU to send an interrupt to an ivshmem PCI device in a
|
||||
* guest. QEMU also implements an ivshmem client similar to this one, they both
|
||||
* connect to an ivshmem server.
|
||||
*
|
||||
* A standalone ivshmem client based on this file is provided for debug/test
|
||||
* purposes.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "qemu/queue.h"
|
||||
#include "hw/misc/ivshmem.h"
|
||||
|
||||
/**
|
||||
* Maximum number of notification vectors supported by the client
|
||||
*/
|
||||
#define IVSHMEM_CLIENT_MAX_VECTORS 64
|
||||
|
||||
/**
|
||||
* Structure storing a peer
|
||||
*
|
||||
* Each time a client connects to an ivshmem server, it is advertised to
|
||||
* all connected clients through the unix socket. When our ivshmem
|
||||
* client receives a notification, it creates a IvshmemClientPeer
|
||||
* structure to store the infos of this peer.
|
||||
*
|
||||
* This structure is also used to store the information of our own
|
||||
* client in (IvshmemClient)->local.
|
||||
*/
|
||||
typedef struct IvshmemClientPeer {
|
||||
QTAILQ_ENTRY(IvshmemClientPeer) next; /**< next in list*/
|
||||
int64_t id; /**< the id of the peer */
|
||||
int vectors[IVSHMEM_CLIENT_MAX_VECTORS]; /**< one fd per vector */
|
||||
unsigned vectors_count; /**< number of vectors */
|
||||
} IvshmemClientPeer;
|
||||
QTAILQ_HEAD(IvshmemClientPeerList, IvshmemClientPeer);
|
||||
|
||||
typedef struct IvshmemClientPeerList IvshmemClientPeerList;
|
||||
typedef struct IvshmemClient IvshmemClient;
|
||||
|
||||
/**
|
||||
* Typedef of callback function used when our IvshmemClient receives a
|
||||
* notification from a peer.
|
||||
*/
|
||||
typedef void (*IvshmemClientNotifCb)(
|
||||
const IvshmemClient *client,
|
||||
const IvshmemClientPeer *peer,
|
||||
unsigned vect, void *arg);
|
||||
|
||||
/**
|
||||
* Structure describing an ivshmem client
|
||||
*
|
||||
* This structure stores all information related to our client: the name
|
||||
* of the server unix socket, the list of peers advertised by the
|
||||
* server, our own client information, and a pointer the notification
|
||||
* callback function used when we receive a notification from a peer.
|
||||
*/
|
||||
struct IvshmemClient {
|
||||
char unix_sock_path[PATH_MAX]; /**< path to unix sock */
|
||||
int sock_fd; /**< unix sock filedesc */
|
||||
int shm_fd; /**< shm file descriptor */
|
||||
|
||||
IvshmemClientPeerList peer_list; /**< list of peers */
|
||||
IvshmemClientPeer local; /**< our own infos */
|
||||
|
||||
IvshmemClientNotifCb notif_cb; /**< notification callback */
|
||||
void *notif_arg; /**< notification argument */
|
||||
|
||||
bool verbose; /**< true to enable debug */
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize an ivshmem client
|
||||
*
|
||||
* @client: A pointer to an uninitialized IvshmemClient structure
|
||||
* @unix_sock_path: The pointer to the unix socket file name
|
||||
* @notif_cb: If not NULL, the pointer to the function to be called when
|
||||
* our IvshmemClient receives a notification from a peer
|
||||
* @notif_arg: Opaque pointer given as-is to the notification callback
|
||||
* function
|
||||
* @verbose: True to enable debug
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path,
|
||||
IvshmemClientNotifCb notif_cb, void *notif_arg,
|
||||
bool verbose);
|
||||
|
||||
/**
|
||||
* Connect to the server
|
||||
*
|
||||
* Connect to the server unix socket, and read the first initial
|
||||
* messages sent by the server, giving the ID of the client and the file
|
||||
* descriptor of the shared memory.
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int ivshmem_client_connect(IvshmemClient *client);
|
||||
|
||||
/**
|
||||
* Close connection to the server and free all peer structures
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
*/
|
||||
void ivshmem_client_close(IvshmemClient *client);
|
||||
|
||||
/**
|
||||
* Fill a fd_set with file descriptors to be monitored
|
||||
*
|
||||
* This function will fill a fd_set with all file descriptors
|
||||
* that must be polled (unix server socket and peers eventfd). The
|
||||
* function will not initialize the fd_set, it is up to the caller
|
||||
* to do this.
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
* @fds: The fd_set to be updated
|
||||
* @maxfd: Must be set to the max file descriptor + 1 in fd_set. This value is
|
||||
* updated if this function adds a greater fd in fd_set.
|
||||
*/
|
||||
void ivshmem_client_get_fds(const IvshmemClient *client, fd_set *fds,
|
||||
int *maxfd);
|
||||
|
||||
/**
|
||||
* Read and handle new messages
|
||||
*
|
||||
* Given a fd_set filled by select(), handle incoming messages from
|
||||
* server or peers.
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
* @fds: The fd_set containing the file descriptors to be checked. Note
|
||||
* that file descriptors that are not related to our client are
|
||||
* ignored.
|
||||
* @maxfd: The maximum fd in fd_set, plus one.
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int ivshmem_client_handle_fds(IvshmemClient *client, fd_set *fds, int maxfd);
|
||||
|
||||
/**
|
||||
* Send a notification to a vector of a peer
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
* @peer: The peer to be notified
|
||||
* @vector: The number of the vector
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int ivshmem_client_notify(const IvshmemClient *client,
|
||||
const IvshmemClientPeer *peer, unsigned vector);
|
||||
|
||||
/**
|
||||
* Send a notification to all vectors of a peer
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
* @peer: The peer to be notified
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error (at least one
|
||||
* notification failed)
|
||||
*/
|
||||
int ivshmem_client_notify_all_vects(const IvshmemClient *client,
|
||||
const IvshmemClientPeer *peer);
|
||||
|
||||
/**
|
||||
* Broadcat a notification to all vectors of all peers
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error (at least one
|
||||
* notification failed)
|
||||
*/
|
||||
int ivshmem_client_notify_broadcast(const IvshmemClient *client);
|
||||
|
||||
/**
|
||||
* Search a peer from its identifier
|
||||
*
|
||||
* Return the peer structure from its peer_id. If the given peer_id is
|
||||
* the local id, the function returns the local peer structure.
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
* @peer_id: The identifier of the peer structure
|
||||
*
|
||||
* Returns: The peer structure, or NULL if not found
|
||||
*/
|
||||
IvshmemClientPeer *
|
||||
ivshmem_client_search_peer(IvshmemClient *client, int64_t peer_id);
|
||||
|
||||
/**
|
||||
* Dump information of this ivshmem client on stdout
|
||||
*
|
||||
* Dump the id and the vectors of the given ivshmem client and the list
|
||||
* of its peers and their vectors on stdout.
|
||||
*
|
||||
* @client: The ivshmem client
|
||||
*/
|
||||
void ivshmem_client_dump(const IvshmemClient *client);
|
||||
|
||||
#endif /* _IVSHMEM_CLIENT_H_ */
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright 6WIND S.A., 2014
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
#include "ivshmem-client.h"
|
||||
|
||||
#define IVSHMEM_CLIENT_DEFAULT_VERBOSE 0
|
||||
#define IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket"
|
||||
|
||||
typedef struct IvshmemClientArgs {
|
||||
bool verbose;
|
||||
const char *unix_sock_path;
|
||||
} IvshmemClientArgs;
|
||||
|
||||
/* show ivshmem_client_usage and exit with given error code */
|
||||
static void
|
||||
ivshmem_client_usage(const char *name, int code)
|
||||
{
|
||||
fprintf(stderr, "%s [opts]\n", name);
|
||||
fprintf(stderr, " -h: show this help\n");
|
||||
fprintf(stderr, " -v: verbose mode\n");
|
||||
fprintf(stderr, " -S <unix_sock_path>: path to the unix socket\n"
|
||||
" to connect to.\n"
|
||||
" default=%s\n", IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH);
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* parse the program arguments, exit on error */
|
||||
static void
|
||||
ivshmem_client_parse_args(IvshmemClientArgs *args, int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
"h" /* help */
|
||||
"v" /* verbose */
|
||||
"S:" /* unix_sock_path */
|
||||
)) != -1) {
|
||||
|
||||
switch (c) {
|
||||
case 'h': /* help */
|
||||
ivshmem_client_usage(argv[0], 0);
|
||||
break;
|
||||
|
||||
case 'v': /* verbose */
|
||||
args->verbose = 1;
|
||||
break;
|
||||
|
||||
case 'S': /* unix_sock_path */
|
||||
args->unix_sock_path = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
ivshmem_client_usage(argv[0], 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* show command line help */
|
||||
static void
|
||||
ivshmem_client_cmdline_help(void)
|
||||
{
|
||||
printf("dump: dump peers (including us)\n"
|
||||
"int <peer> <vector>: notify one vector on a peer\n"
|
||||
"int <peer> all: notify all vectors of a peer\n"
|
||||
"int all: notify all vectors of all peers (excepting us)\n");
|
||||
}
|
||||
|
||||
/* read stdin and handle commands */
|
||||
static int
|
||||
ivshmem_client_handle_stdin_command(IvshmemClient *client)
|
||||
{
|
||||
IvshmemClientPeer *peer;
|
||||
char buf[128];
|
||||
char *s, *token;
|
||||
int ret;
|
||||
int peer_id, vector;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ret = read(0, buf, sizeof(buf) - 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = buf;
|
||||
while ((token = strsep(&s, "\n\r;")) != NULL) {
|
||||
if (!strcmp(token, "")) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(token, "?")) {
|
||||
ivshmem_client_cmdline_help();
|
||||
}
|
||||
if (!strcmp(token, "help")) {
|
||||
ivshmem_client_cmdline_help();
|
||||
} else if (!strcmp(token, "dump")) {
|
||||
ivshmem_client_dump(client);
|
||||
} else if (!strcmp(token, "int all")) {
|
||||
ivshmem_client_notify_broadcast(client);
|
||||
} else if (sscanf(token, "int %d %d", &peer_id, &vector) == 2) {
|
||||
peer = ivshmem_client_search_peer(client, peer_id);
|
||||
if (peer == NULL) {
|
||||
printf("cannot find peer_id = %d\n", peer_id);
|
||||
continue;
|
||||
}
|
||||
ivshmem_client_notify(client, peer, vector);
|
||||
} else if (sscanf(token, "int %d all", &peer_id) == 1) {
|
||||
peer = ivshmem_client_search_peer(client, peer_id);
|
||||
if (peer == NULL) {
|
||||
printf("cannot find peer_id = %d\n", peer_id);
|
||||
continue;
|
||||
}
|
||||
ivshmem_client_notify_all_vects(client, peer);
|
||||
} else {
|
||||
printf("invalid command, type help\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("cmd> ");
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* listen on stdin (command line), on unix socket (notifications of new
|
||||
* and dead peers), and on eventfd (IRQ request) */
|
||||
static int
|
||||
ivshmem_client_poll_events(IvshmemClient *client)
|
||||
{
|
||||
fd_set fds;
|
||||
int ret, maxfd;
|
||||
|
||||
while (1) {
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds); /* add stdin in fd_set */
|
||||
maxfd = 1;
|
||||
|
||||
ivshmem_client_get_fds(client, &fds, &maxfd);
|
||||
|
||||
ret = select(maxfd, &fds, NULL, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "select error: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FD_ISSET(0, &fds) &&
|
||||
ivshmem_client_handle_stdin_command(client) < 0 && errno != EINTR) {
|
||||
fprintf(stderr, "ivshmem_client_handle_stdin_command() failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ivshmem_client_handle_fds(client, &fds, maxfd) < 0) {
|
||||
fprintf(stderr, "ivshmem_client_handle_fds() failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* callback when we receive a notification (just display it) */
|
||||
static void
|
||||
ivshmem_client_notification_cb(const IvshmemClient *client,
|
||||
const IvshmemClientPeer *peer,
|
||||
unsigned vect, void *arg)
|
||||
{
|
||||
(void)client;
|
||||
(void)arg;
|
||||
printf("receive notification from peer_id=%" PRId64 " vector=%u\n",
|
||||
peer->id, vect);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sigaction sa;
|
||||
IvshmemClient client;
|
||||
IvshmemClientArgs args = {
|
||||
.verbose = IVSHMEM_CLIENT_DEFAULT_VERBOSE,
|
||||
.unix_sock_path = IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH,
|
||||
};
|
||||
|
||||
/* parse arguments, will exit on error */
|
||||
ivshmem_client_parse_args(&args, argc, argv);
|
||||
|
||||
/* Ignore SIGPIPE, see this link for more info:
|
||||
* http://www.mail-archive.com/libevent-users@monkey.org/msg01606.html */
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = 0;
|
||||
if (sigemptyset(&sa.sa_mask) == -1 ||
|
||||
sigaction(SIGPIPE, &sa, 0) == -1) {
|
||||
perror("failed to ignore SIGPIPE; sigaction");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ivshmem_client_cmdline_help();
|
||||
printf("cmd> ");
|
||||
fflush(stdout);
|
||||
|
||||
if (ivshmem_client_init(&client, args.unix_sock_path,
|
||||
ivshmem_client_notification_cb, NULL,
|
||||
args.verbose) < 0) {
|
||||
fprintf(stderr, "cannot init client\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (ivshmem_client_connect(&client) < 0) {
|
||||
fprintf(stderr, "cannot connect to server, retry in 1 second\n");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stdout, "listen on server socket %d\n", client.sock_fd);
|
||||
|
||||
if (ivshmem_client_poll_events(&client) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* disconnected from server, reset all peers */
|
||||
fprintf(stdout, "disconnected from server\n");
|
||||
|
||||
ivshmem_client_close(&client);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
ivshmem-server-obj-y = ivshmem-server.o main.o
|
||||
@@ -1,493 +0,0 @@
|
||||
/*
|
||||
* Copyright 6WIND S.A., 2014
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#ifdef CONFIG_LINUX
|
||||
#include <sys/vfs.h>
|
||||
#endif
|
||||
|
||||
#include "ivshmem-server.h"
|
||||
|
||||
/* log a message on stdout if verbose=1 */
|
||||
#define IVSHMEM_SERVER_DEBUG(server, fmt, ...) do { \
|
||||
if ((server)->verbose) { \
|
||||
printf(fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/** maximum size of a huge page, used by ivshmem_server_ftruncate() */
|
||||
#define IVSHMEM_SERVER_MAX_HUGEPAGE_SIZE (1024 * 1024 * 1024)
|
||||
|
||||
/** default listen backlog (number of sockets not accepted) */
|
||||
#define IVSHMEM_SERVER_LISTEN_BACKLOG 10
|
||||
|
||||
/* send message to a client unix socket */
|
||||
static int
|
||||
ivshmem_server_send_one_msg(int sock_fd, int64_t peer_id, int fd)
|
||||
{
|
||||
int ret;
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
union {
|
||||
struct cmsghdr cmsg;
|
||||
char control[CMSG_SPACE(sizeof(int))];
|
||||
} msg_control;
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
peer_id = GINT64_TO_LE(peer_id);
|
||||
iov[0].iov_base = &peer_id;
|
||||
iov[0].iov_len = sizeof(peer_id);
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
/* if fd is specified, add it in a cmsg */
|
||||
if (fd >= 0) {
|
||||
memset(&msg_control, 0, sizeof(msg_control));
|
||||
msg.msg_control = &msg_control;
|
||||
msg.msg_controllen = sizeof(msg_control);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
||||
}
|
||||
|
||||
ret = sendmsg(sock_fd, &msg, 0);
|
||||
if (ret <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* free a peer when the server advertises a disconnection or when the
|
||||
* server is freed */
|
||||
static void
|
||||
ivshmem_server_free_peer(IvshmemServer *server, IvshmemServerPeer *peer)
|
||||
{
|
||||
unsigned vector;
|
||||
IvshmemServerPeer *other_peer;
|
||||
|
||||
IVSHMEM_SERVER_DEBUG(server, "free peer %" PRId64 "\n", peer->id);
|
||||
close(peer->sock_fd);
|
||||
QTAILQ_REMOVE(&server->peer_list, peer, next);
|
||||
|
||||
/* advertise the deletion to other peers */
|
||||
QTAILQ_FOREACH(other_peer, &server->peer_list, next) {
|
||||
ivshmem_server_send_one_msg(other_peer->sock_fd, peer->id, -1);
|
||||
}
|
||||
|
||||
for (vector = 0; vector < peer->vectors_count; vector++) {
|
||||
event_notifier_cleanup(&peer->vectors[vector]);
|
||||
}
|
||||
|
||||
g_free(peer);
|
||||
}
|
||||
|
||||
/* send the peer id and the shm_fd just after a new client connection */
|
||||
static int
|
||||
ivshmem_server_send_initial_info(IvshmemServer *server, IvshmemServerPeer *peer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* send our protocol version first */
|
||||
ret = ivshmem_server_send_one_msg(peer->sock_fd, IVSHMEM_PROTOCOL_VERSION,
|
||||
-1);
|
||||
if (ret < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot send version: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send the peer id to the client */
|
||||
ret = ivshmem_server_send_one_msg(peer->sock_fd, peer->id, -1);
|
||||
if (ret < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot send peer id: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send the shm_fd */
|
||||
ret = ivshmem_server_send_one_msg(peer->sock_fd, -1, server->shm_fd);
|
||||
if (ret < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot send shm fd: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle message on listening unix socket (new client connection) */
|
||||
static int
|
||||
ivshmem_server_handle_new_conn(IvshmemServer *server)
|
||||
{
|
||||
IvshmemServerPeer *peer, *other_peer;
|
||||
struct sockaddr_un unaddr;
|
||||
socklen_t unaddr_len;
|
||||
int newfd;
|
||||
unsigned i;
|
||||
|
||||
/* accept the incoming connection */
|
||||
unaddr_len = sizeof(unaddr);
|
||||
newfd = qemu_accept(server->sock_fd,
|
||||
(struct sockaddr *)&unaddr, &unaddr_len);
|
||||
|
||||
if (newfd < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot accept() %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_set_nonblock(newfd);
|
||||
IVSHMEM_SERVER_DEBUG(server, "accept()=%d\n", newfd);
|
||||
|
||||
/* allocate new structure for this peer */
|
||||
peer = g_malloc0(sizeof(*peer));
|
||||
peer->sock_fd = newfd;
|
||||
|
||||
/* get an unused peer id */
|
||||
/* XXX: this could use id allocation such as Linux IDA, or simply
|
||||
* a free-list */
|
||||
for (i = 0; i < G_MAXUINT16; i++) {
|
||||
if (ivshmem_server_search_peer(server, server->cur_id) == NULL) {
|
||||
break;
|
||||
}
|
||||
server->cur_id++;
|
||||
}
|
||||
if (i == G_MAXUINT16) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot allocate new client id\n");
|
||||
close(newfd);
|
||||
g_free(peer);
|
||||
return -1;
|
||||
}
|
||||
peer->id = server->cur_id++;
|
||||
|
||||
/* create eventfd, one per vector */
|
||||
peer->vectors_count = server->n_vectors;
|
||||
for (i = 0; i < peer->vectors_count; i++) {
|
||||
if (event_notifier_init(&peer->vectors[i], FALSE) < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot create eventfd\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* send peer id and shm fd */
|
||||
if (ivshmem_server_send_initial_info(server, peer) < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot send initial info\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* advertise the new peer to others */
|
||||
QTAILQ_FOREACH(other_peer, &server->peer_list, next) {
|
||||
for (i = 0; i < peer->vectors_count; i++) {
|
||||
ivshmem_server_send_one_msg(other_peer->sock_fd, peer->id,
|
||||
peer->vectors[i].wfd);
|
||||
}
|
||||
}
|
||||
|
||||
/* advertise the other peers to the new one */
|
||||
QTAILQ_FOREACH(other_peer, &server->peer_list, next) {
|
||||
for (i = 0; i < peer->vectors_count; i++) {
|
||||
ivshmem_server_send_one_msg(peer->sock_fd, other_peer->id,
|
||||
other_peer->vectors[i].wfd);
|
||||
}
|
||||
}
|
||||
|
||||
/* advertise the new peer to itself */
|
||||
for (i = 0; i < peer->vectors_count; i++) {
|
||||
ivshmem_server_send_one_msg(peer->sock_fd, peer->id,
|
||||
event_notifier_get_fd(&peer->vectors[i]));
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&server->peer_list, peer, next);
|
||||
IVSHMEM_SERVER_DEBUG(server, "new peer id = %" PRId64 "\n",
|
||||
peer->id);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
while (i--) {
|
||||
event_notifier_cleanup(&peer->vectors[i]);
|
||||
}
|
||||
close(newfd);
|
||||
g_free(peer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Try to ftruncate a file to next power of 2 of shmsize.
|
||||
* If it fails; all power of 2 above shmsize are tested until
|
||||
* we reach the maximum huge page size. This is useful
|
||||
* if the shm file is in a hugetlbfs that cannot be truncated to the
|
||||
* shm_size value. */
|
||||
static int
|
||||
ivshmem_server_ftruncate(int fd, unsigned shmsize)
|
||||
{
|
||||
int ret;
|
||||
struct stat mapstat;
|
||||
|
||||
/* align shmsize to next power of 2 */
|
||||
shmsize = pow2ceil(shmsize);
|
||||
|
||||
if (fstat(fd, &mapstat) != -1 && mapstat.st_size == shmsize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (shmsize <= IVSHMEM_SERVER_MAX_HUGEPAGE_SIZE) {
|
||||
ret = ftruncate(fd, shmsize);
|
||||
if (ret == 0) {
|
||||
return ret;
|
||||
}
|
||||
shmsize *= 2;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init a new ivshmem server */
|
||||
int
|
||||
ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path,
|
||||
const char *shm_path, size_t shm_size, unsigned n_vectors,
|
||||
bool verbose)
|
||||
{
|
||||
int ret;
|
||||
|
||||
memset(server, 0, sizeof(*server));
|
||||
server->verbose = verbose;
|
||||
|
||||
ret = snprintf(server->unix_sock_path, sizeof(server->unix_sock_path),
|
||||
"%s", unix_sock_path);
|
||||
if (ret < 0 || ret >= sizeof(server->unix_sock_path)) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n");
|
||||
return -1;
|
||||
}
|
||||
ret = snprintf(server->shm_path, sizeof(server->shm_path),
|
||||
"%s", shm_path);
|
||||
if (ret < 0 || ret >= sizeof(server->shm_path)) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "could not copy shm path\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
server->shm_size = shm_size;
|
||||
server->n_vectors = n_vectors;
|
||||
|
||||
QTAILQ_INIT(&server->peer_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
|
||||
#define HUGETLBFS_MAGIC 0x958458f6
|
||||
|
||||
static long gethugepagesize(const char *path)
|
||||
{
|
||||
struct statfs fs;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = statfs(path, &fs);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
|
||||
if (ret != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fs.f_type != HUGETLBFS_MAGIC) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fs.f_bsize;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* open shm, create and bind to the unix socket */
|
||||
int
|
||||
ivshmem_server_start(IvshmemServer *server)
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
int shm_fd, sock_fd, ret;
|
||||
|
||||
/* open shm file */
|
||||
#ifdef CONFIG_LINUX
|
||||
long hpagesize;
|
||||
|
||||
hpagesize = gethugepagesize(server->shm_path);
|
||||
if (hpagesize < 0 && errno != ENOENT) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot stat shm file %s: %s\n",
|
||||
server->shm_path, strerror(errno));
|
||||
}
|
||||
|
||||
if (hpagesize > 0) {
|
||||
gchar *filename = g_strdup_printf("%s/ivshmem.XXXXXX", server->shm_path);
|
||||
IVSHMEM_SERVER_DEBUG(server, "Using hugepages: %s\n", server->shm_path);
|
||||
shm_fd = mkstemp(filename);
|
||||
unlink(filename);
|
||||
g_free(filename);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
IVSHMEM_SERVER_DEBUG(server, "Using POSIX shared memory: %s\n",
|
||||
server->shm_path);
|
||||
shm_fd = shm_open(server->shm_path, O_CREAT|O_RDWR, S_IRWXU);
|
||||
}
|
||||
|
||||
if (shm_fd < 0) {
|
||||
fprintf(stderr, "cannot open shm file %s: %s\n", server->shm_path,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (ivshmem_server_ftruncate(shm_fd, server->shm_size) < 0) {
|
||||
fprintf(stderr, "ftruncate(%s) failed: %s\n", server->shm_path,
|
||||
strerror(errno));
|
||||
goto err_close_shm;
|
||||
}
|
||||
|
||||
IVSHMEM_SERVER_DEBUG(server, "create & bind socket %s\n",
|
||||
server->unix_sock_path);
|
||||
|
||||
/* create the unix listening socket */
|
||||
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot create socket: %s\n",
|
||||
strerror(errno));
|
||||
goto err_close_shm;
|
||||
}
|
||||
|
||||
sun.sun_family = AF_UNIX;
|
||||
ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s",
|
||||
server->unix_sock_path);
|
||||
if (ret < 0 || ret >= sizeof(sun.sun_path)) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n");
|
||||
goto err_close_sock;
|
||||
}
|
||||
if (bind(sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", sun.sun_path,
|
||||
strerror(errno));
|
||||
goto err_close_sock;
|
||||
}
|
||||
|
||||
if (listen(sock_fd, IVSHMEM_SERVER_LISTEN_BACKLOG) < 0) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "listen() failed: %s\n", strerror(errno));
|
||||
goto err_close_sock;
|
||||
}
|
||||
|
||||
server->sock_fd = sock_fd;
|
||||
server->shm_fd = shm_fd;
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_sock:
|
||||
close(sock_fd);
|
||||
err_close_shm:
|
||||
close(shm_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* close connections to clients, the unix socket and the shm fd */
|
||||
void
|
||||
ivshmem_server_close(IvshmemServer *server)
|
||||
{
|
||||
IvshmemServerPeer *peer, *npeer;
|
||||
|
||||
IVSHMEM_SERVER_DEBUG(server, "close server\n");
|
||||
|
||||
QTAILQ_FOREACH_SAFE(peer, &server->peer_list, next, npeer) {
|
||||
ivshmem_server_free_peer(server, peer);
|
||||
}
|
||||
|
||||
unlink(server->unix_sock_path);
|
||||
close(server->sock_fd);
|
||||
close(server->shm_fd);
|
||||
server->sock_fd = -1;
|
||||
server->shm_fd = -1;
|
||||
}
|
||||
|
||||
/* get the fd_set according to the unix socket and the peer list */
|
||||
void
|
||||
ivshmem_server_get_fds(const IvshmemServer *server, fd_set *fds, int *maxfd)
|
||||
{
|
||||
IvshmemServerPeer *peer;
|
||||
|
||||
if (server->sock_fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
FD_SET(server->sock_fd, fds);
|
||||
if (server->sock_fd >= *maxfd) {
|
||||
*maxfd = server->sock_fd + 1;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(peer, &server->peer_list, next) {
|
||||
FD_SET(peer->sock_fd, fds);
|
||||
if (peer->sock_fd >= *maxfd) {
|
||||
*maxfd = peer->sock_fd + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* process incoming messages on the sockets in fd_set */
|
||||
int
|
||||
ivshmem_server_handle_fds(IvshmemServer *server, fd_set *fds, int maxfd)
|
||||
{
|
||||
IvshmemServerPeer *peer, *peer_next;
|
||||
|
||||
if (server->sock_fd < maxfd && FD_ISSET(server->sock_fd, fds) &&
|
||||
ivshmem_server_handle_new_conn(server) < 0 && errno != EINTR) {
|
||||
IVSHMEM_SERVER_DEBUG(server, "ivshmem_server_handle_new_conn() "
|
||||
"failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH_SAFE(peer, &server->peer_list, next, peer_next) {
|
||||
/* any message from a peer socket result in a close() */
|
||||
IVSHMEM_SERVER_DEBUG(server, "peer->sock_fd=%d\n", peer->sock_fd);
|
||||
if (peer->sock_fd < maxfd && FD_ISSET(peer->sock_fd, fds)) {
|
||||
ivshmem_server_free_peer(server, peer);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup peer from its id */
|
||||
IvshmemServerPeer *
|
||||
ivshmem_server_search_peer(IvshmemServer *server, int64_t peer_id)
|
||||
{
|
||||
IvshmemServerPeer *peer;
|
||||
|
||||
QTAILQ_FOREACH(peer, &server->peer_list, next) {
|
||||
if (peer->id == peer_id) {
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* dump our info, the list of peers their vectors on stdout */
|
||||
void
|
||||
ivshmem_server_dump(const IvshmemServer *server)
|
||||
{
|
||||
const IvshmemServerPeer *peer;
|
||||
unsigned vector;
|
||||
|
||||
/* dump peers */
|
||||
QTAILQ_FOREACH(peer, &server->peer_list, next) {
|
||||
printf("peer_id = %" PRId64 "\n", peer->id);
|
||||
|
||||
for (vector = 0; vector < peer->vectors_count; vector++) {
|
||||
printf(" vector %d is enabled (fd=%d)\n", vector,
|
||||
event_notifier_get_fd(&peer->vectors[vector]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 6WIND S.A., 2014
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef _IVSHMEM_SERVER_H_
|
||||
#define _IVSHMEM_SERVER_H_
|
||||
|
||||
/**
|
||||
* The ivshmem server is a daemon that creates a unix socket in listen
|
||||
* mode. The ivshmem clients (qemu or ivshmem-client) connect to this
|
||||
* unix socket. For each client, the server will create some eventfd
|
||||
* (see EVENTFD(2)), one per vector. These fd are transmitted to all
|
||||
* clients using the SCM_RIGHTS cmsg message. Therefore, each client is
|
||||
* able to send a notification to another client without beeing
|
||||
* "profixied" by the server.
|
||||
*
|
||||
* We use this mechanism to send interruptions between guests.
|
||||
* qemu is able to transform an event on a eventfd into a PCI MSI-x
|
||||
* interruption in the guest.
|
||||
*
|
||||
* The ivshmem server is also able to share the file descriptor
|
||||
* associated to the ivshmem shared memory.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/select.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "hw/misc/ivshmem.h"
|
||||
|
||||
/**
|
||||
* Maximum number of notification vectors supported by the server
|
||||
*/
|
||||
#define IVSHMEM_SERVER_MAX_VECTORS 64
|
||||
|
||||
/**
|
||||
* Structure storing a peer
|
||||
*
|
||||
* Each time a client connects to an ivshmem server, a new
|
||||
* IvshmemServerPeer structure is created. This peer and all its
|
||||
* vectors are advertised to all connected clients through the connected
|
||||
* unix sockets.
|
||||
*/
|
||||
typedef struct IvshmemServerPeer {
|
||||
QTAILQ_ENTRY(IvshmemServerPeer) next; /**< next in list*/
|
||||
int sock_fd; /**< connected unix sock */
|
||||
int64_t id; /**< the id of the peer */
|
||||
EventNotifier vectors[IVSHMEM_SERVER_MAX_VECTORS]; /**< one per vector */
|
||||
unsigned vectors_count; /**< number of vectors */
|
||||
} IvshmemServerPeer;
|
||||
QTAILQ_HEAD(IvshmemServerPeerList, IvshmemServerPeer);
|
||||
|
||||
typedef struct IvshmemServerPeerList IvshmemServerPeerList;
|
||||
|
||||
/**
|
||||
* Structure describing an ivshmem server
|
||||
*
|
||||
* This structure stores all information related to our server: the name
|
||||
* of the server unix socket and the list of connected peers.
|
||||
*/
|
||||
typedef struct IvshmemServer {
|
||||
char unix_sock_path[PATH_MAX]; /**< path to unix socket */
|
||||
int sock_fd; /**< unix sock file descriptor */
|
||||
char shm_path[PATH_MAX]; /**< path to shm */
|
||||
size_t shm_size; /**< size of shm */
|
||||
int shm_fd; /**< shm file descriptor */
|
||||
unsigned n_vectors; /**< number of vectors */
|
||||
uint16_t cur_id; /**< id to be given to next client */
|
||||
bool verbose; /**< true in verbose mode */
|
||||
IvshmemServerPeerList peer_list; /**< list of peers */
|
||||
} IvshmemServer;
|
||||
|
||||
/**
|
||||
* Initialize an ivshmem server
|
||||
*
|
||||
* @server: A pointer to an uninitialized IvshmemServer structure
|
||||
* @unix_sock_path: The pointer to the unix socket file name
|
||||
* @shm_path: Path to the shared memory. The path corresponds to a POSIX
|
||||
* shm name or a hugetlbfs mount point.
|
||||
* @shm_size: Size of shared memory
|
||||
* @n_vectors: Number of interrupt vectors per client
|
||||
* @verbose: True to enable verbose mode
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int
|
||||
ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path,
|
||||
const char *shm_path, size_t shm_size, unsigned n_vectors,
|
||||
bool verbose);
|
||||
|
||||
/**
|
||||
* Open the shm, then create and bind to the unix socket
|
||||
*
|
||||
* @server: The pointer to the initialized IvshmemServer structure
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int ivshmem_server_start(IvshmemServer *server);
|
||||
|
||||
/**
|
||||
* Close the server
|
||||
*
|
||||
* Close connections to all clients, close the unix socket and the
|
||||
* shared memory file descriptor. The structure remains initialized, so
|
||||
* it is possible to call ivshmem_server_start() again after a call to
|
||||
* ivshmem_server_close().
|
||||
*
|
||||
* @server: The ivshmem server
|
||||
*/
|
||||
void ivshmem_server_close(IvshmemServer *server);
|
||||
|
||||
/**
|
||||
* Fill a fd_set with file descriptors to be monitored
|
||||
*
|
||||
* This function will fill a fd_set with all file descriptors that must
|
||||
* be polled (unix server socket and peers unix socket). The function
|
||||
* will not initialize the fd_set, it is up to the caller to do it.
|
||||
*
|
||||
* @server: The ivshmem server
|
||||
* @fds: The fd_set to be updated
|
||||
* @maxfd: Must be set to the max file descriptor + 1 in fd_set. This value is
|
||||
* updated if this function adds a greater fd in fd_set.
|
||||
*/
|
||||
void
|
||||
ivshmem_server_get_fds(const IvshmemServer *server, fd_set *fds, int *maxfd);
|
||||
|
||||
/**
|
||||
* Read and handle new messages
|
||||
*
|
||||
* Given a fd_set (for instance filled by a call to select()), handle
|
||||
* incoming messages from peers.
|
||||
*
|
||||
* @server: The ivshmem server
|
||||
* @fds: The fd_set containing the file descriptors to be checked. Note that
|
||||
* file descriptors that are not related to our server are ignored.
|
||||
* @maxfd: The maximum fd in fd_set, plus one.
|
||||
*
|
||||
* Returns: 0 on success, or a negative value on error
|
||||
*/
|
||||
int ivshmem_server_handle_fds(IvshmemServer *server, fd_set *fds, int maxfd);
|
||||
|
||||
/**
|
||||
* Search a peer from its identifier
|
||||
*
|
||||
* @server: The ivshmem server
|
||||
* @peer_id: The identifier of the peer structure
|
||||
*
|
||||
* Returns: The peer structure, or NULL if not found
|
||||
*/
|
||||
IvshmemServerPeer *
|
||||
ivshmem_server_search_peer(IvshmemServer *server, int64_t peer_id);
|
||||
|
||||
/**
|
||||
* Dump information of this ivshmem server and its peers on stdout
|
||||
*
|
||||
* @server: The ivshmem server
|
||||
*/
|
||||
void ivshmem_server_dump(const IvshmemServer *server);
|
||||
|
||||
#endif /* _IVSHMEM_SERVER_H_ */
|
||||
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Copyright 6WIND S.A., 2014
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
#include "ivshmem-server.h"
|
||||
|
||||
#define IVSHMEM_SERVER_DEFAULT_VERBOSE 0
|
||||
#define IVSHMEM_SERVER_DEFAULT_FOREGROUND 0
|
||||
#define IVSHMEM_SERVER_DEFAULT_PID_FILE "/var/run/ivshmem-server.pid"
|
||||
#define IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket"
|
||||
#define IVSHMEM_SERVER_DEFAULT_SHM_PATH "ivshmem"
|
||||
#define IVSHMEM_SERVER_DEFAULT_SHM_SIZE (4*1024*1024)
|
||||
#define IVSHMEM_SERVER_DEFAULT_N_VECTORS 1
|
||||
|
||||
/* used to quit on signal SIGTERM */
|
||||
static int ivshmem_server_quit;
|
||||
|
||||
/* arguments given by the user */
|
||||
typedef struct IvshmemServerArgs {
|
||||
bool verbose;
|
||||
bool foreground;
|
||||
const char *pid_file;
|
||||
const char *unix_socket_path;
|
||||
const char *shm_path;
|
||||
uint64_t shm_size;
|
||||
unsigned n_vectors;
|
||||
} IvshmemServerArgs;
|
||||
|
||||
/* show ivshmem_server_usage and exit with given error code */
|
||||
static void
|
||||
ivshmem_server_usage(const char *name, int code)
|
||||
{
|
||||
fprintf(stderr, "%s [opts]\n", name);
|
||||
fprintf(stderr, " -h: show this help\n");
|
||||
fprintf(stderr, " -v: verbose mode\n");
|
||||
fprintf(stderr, " -F: foreground mode (default is to daemonize)\n");
|
||||
fprintf(stderr, " -p <pid_file>: path to the PID file (used in daemon\n"
|
||||
" mode only).\n"
|
||||
" Default=%s\n", IVSHMEM_SERVER_DEFAULT_SHM_PATH);
|
||||
fprintf(stderr, " -S <unix_socket_path>: path to the unix socket\n"
|
||||
" to listen to.\n"
|
||||
" Default=%s\n", IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH);
|
||||
fprintf(stderr, " -m <shm_path>: path to the shared memory.\n"
|
||||
" The path corresponds to a POSIX shm name or a\n"
|
||||
" hugetlbfs mount point.\n"
|
||||
" default=%s\n", IVSHMEM_SERVER_DEFAULT_SHM_PATH);
|
||||
fprintf(stderr, " -l <size>: size of shared memory in bytes. The suffix\n"
|
||||
" K, M and G can be used (ex: 1K means 1024).\n"
|
||||
" default=%u\n", IVSHMEM_SERVER_DEFAULT_SHM_SIZE);
|
||||
fprintf(stderr, " -n <n_vects>: number of vectors.\n"
|
||||
" default=%u\n", IVSHMEM_SERVER_DEFAULT_N_VECTORS);
|
||||
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* parse the program arguments, exit on error */
|
||||
static void
|
||||
ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
unsigned long long v;
|
||||
Error *errp = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
"h" /* help */
|
||||
"v" /* verbose */
|
||||
"F" /* foreground */
|
||||
"p:" /* pid_file */
|
||||
"S:" /* unix_socket_path */
|
||||
"m:" /* shm_path */
|
||||
"l:" /* shm_size */
|
||||
"n:" /* n_vectors */
|
||||
)) != -1) {
|
||||
|
||||
switch (c) {
|
||||
case 'h': /* help */
|
||||
ivshmem_server_usage(argv[0], 0);
|
||||
break;
|
||||
|
||||
case 'v': /* verbose */
|
||||
args->verbose = 1;
|
||||
break;
|
||||
|
||||
case 'F': /* foreground */
|
||||
args->foreground = 1;
|
||||
break;
|
||||
|
||||
case 'p': /* pid_file */
|
||||
args->pid_file = optarg;
|
||||
break;
|
||||
|
||||
case 'S': /* unix_socket_path */
|
||||
args->unix_socket_path = optarg;
|
||||
break;
|
||||
|
||||
case 'm': /* shm_path */
|
||||
args->shm_path = optarg;
|
||||
break;
|
||||
|
||||
case 'l': /* shm_size */
|
||||
parse_option_size("shm_size", optarg, &args->shm_size, &errp);
|
||||
if (errp) {
|
||||
fprintf(stderr, "cannot parse shm size: %s\n",
|
||||
error_get_pretty(errp));
|
||||
error_free(errp);
|
||||
ivshmem_server_usage(argv[0], 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n': /* n_vectors */
|
||||
if (parse_uint_full(optarg, &v, 0) < 0) {
|
||||
fprintf(stderr, "cannot parse n_vectors\n");
|
||||
ivshmem_server_usage(argv[0], 1);
|
||||
}
|
||||
args->n_vectors = v;
|
||||
break;
|
||||
|
||||
default:
|
||||
ivshmem_server_usage(argv[0], 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (args->n_vectors > IVSHMEM_SERVER_MAX_VECTORS) {
|
||||
fprintf(stderr, "too many requested vectors (max is %d)\n",
|
||||
IVSHMEM_SERVER_MAX_VECTORS);
|
||||
ivshmem_server_usage(argv[0], 1);
|
||||
}
|
||||
|
||||
if (args->verbose == 1 && args->foreground == 0) {
|
||||
fprintf(stderr, "cannot use verbose in daemon mode\n");
|
||||
ivshmem_server_usage(argv[0], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* wait for events on listening server unix socket and connected client
|
||||
* sockets */
|
||||
static int
|
||||
ivshmem_server_poll_events(IvshmemServer *server)
|
||||
{
|
||||
fd_set fds;
|
||||
int ret = 0, maxfd;
|
||||
|
||||
while (!ivshmem_server_quit) {
|
||||
|
||||
FD_ZERO(&fds);
|
||||
maxfd = 0;
|
||||
ivshmem_server_get_fds(server, &fds, &maxfd);
|
||||
|
||||
ret = select(maxfd, &fds, NULL, NULL, NULL);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "select error: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) {
|
||||
fprintf(stderr, "ivshmem_server_handle_fds() failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
ivshmem_server_quit_cb(int signum)
|
||||
{
|
||||
ivshmem_server_quit = 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
IvshmemServer server;
|
||||
struct sigaction sa, sa_quit;
|
||||
IvshmemServerArgs args = {
|
||||
.verbose = IVSHMEM_SERVER_DEFAULT_VERBOSE,
|
||||
.foreground = IVSHMEM_SERVER_DEFAULT_FOREGROUND,
|
||||
.pid_file = IVSHMEM_SERVER_DEFAULT_PID_FILE,
|
||||
.unix_socket_path = IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH,
|
||||
.shm_path = IVSHMEM_SERVER_DEFAULT_SHM_PATH,
|
||||
.shm_size = IVSHMEM_SERVER_DEFAULT_SHM_SIZE,
|
||||
.n_vectors = IVSHMEM_SERVER_DEFAULT_N_VECTORS,
|
||||
};
|
||||
int ret = 1;
|
||||
|
||||
/* parse arguments, will exit on error */
|
||||
ivshmem_server_parse_args(&args, argc, argv);
|
||||
|
||||
/* Ignore SIGPIPE, see this link for more info:
|
||||
* http://www.mail-archive.com/libevent-users@monkey.org/msg01606.html */
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = 0;
|
||||
if (sigemptyset(&sa.sa_mask) == -1 ||
|
||||
sigaction(SIGPIPE, &sa, 0) == -1) {
|
||||
perror("failed to ignore SIGPIPE; sigaction");
|
||||
goto err;
|
||||
}
|
||||
|
||||
sa_quit.sa_handler = ivshmem_server_quit_cb;
|
||||
sa_quit.sa_flags = 0;
|
||||
if (sigemptyset(&sa_quit.sa_mask) == -1 ||
|
||||
sigaction(SIGTERM, &sa_quit, 0) == -1) {
|
||||
perror("failed to add SIGTERM handler; sigaction");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* init the ivshms structure */
|
||||
if (ivshmem_server_init(&server, args.unix_socket_path, args.shm_path,
|
||||
args.shm_size, args.n_vectors, args.verbose) < 0) {
|
||||
fprintf(stderr, "cannot init server\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* start the ivshmem server (open shm & unix socket) */
|
||||
if (ivshmem_server_start(&server) < 0) {
|
||||
fprintf(stderr, "cannot bind\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* daemonize if asked to */
|
||||
if (!args.foreground) {
|
||||
FILE *fp;
|
||||
|
||||
if (qemu_daemon(1, 1) < 0) {
|
||||
fprintf(stderr, "cannot daemonize: %s\n", strerror(errno));
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
/* write pid file */
|
||||
fp = fopen(args.pid_file, "w");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "cannot write pid file: %s\n", strerror(errno));
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
fprintf(fp, "%d\n", (int) getpid());
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
ivshmem_server_poll_events(&server);
|
||||
fprintf(stdout, "server disconnected\n");
|
||||
ret = 0;
|
||||
|
||||
err_close:
|
||||
ivshmem_server_close(&server);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/coroutine_int.h"
|
||||
#include "block/coroutine_int.h"
|
||||
|
||||
typedef struct {
|
||||
Coroutine base;
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/coroutine_int.h"
|
||||
#include "block/coroutine_int.h"
|
||||
|
||||
typedef struct {
|
||||
Coroutine base;
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <stdint.h>
|
||||
#include <ucontext.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/coroutine_int.h"
|
||||
#include "block/coroutine_int.h"
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
#include <valgrind/valgrind.h>
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/coroutine_int.h"
|
||||
#include "block/coroutine_int.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* emulator main execution loop
|
||||
*
|
||||
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "exec/memory-internal.h"
|
||||
|
||||
bool exit_request;
|
||||
CPUState *tcg_current_cpu;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
/* XXX: restore cpu registers saved in host registers */
|
||||
|
||||
cpu->exception_index = -1;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_reloading_memory_map(void)
|
||||
{
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
/* The guest can in theory prolong the RCU critical section as long
|
||||
* as it feels like. The major problem with this is that because it
|
||||
* can do multiple reconfigurations of the memory map within the
|
||||
* critical section, we could potentially accumulate an unbounded
|
||||
* collection of memory data structures awaiting reclamation.
|
||||
*
|
||||
* Because the only thing we're currently protecting with RCU is the
|
||||
* memory data structures, it's sufficient to break the critical section
|
||||
* in this callback, which we know will get called every time the
|
||||
* memory map is rearranged.
|
||||
*
|
||||
* (If we add anything else in the system that uses RCU to protect
|
||||
* its data structures, we will need to implement some other mechanism
|
||||
* to force TCG CPUs to exit the critical section, at which point this
|
||||
* part of this callback might become unnecessary.)
|
||||
*
|
||||
* This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which
|
||||
* only protects cpu->as->dispatch. Since we know our caller is about
|
||||
* to reload it, it's safe to split the critical section.
|
||||
*/
|
||||
rcu_read_unlock();
|
||||
rcu_read_lock();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
cpu->current_tb = NULL;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
||||
{
|
||||
if (pc) {
|
||||
cpu_restore_state(cpu, pc);
|
||||
}
|
||||
cpu->current_tb = NULL;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
+119
-137
@@ -25,12 +25,9 @@
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/memory-internal.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/i386/apic.h"
|
||||
#endif
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
/* -icount align implementation. */
|
||||
|
||||
@@ -131,6 +128,52 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
|
||||
}
|
||||
#endif /* CONFIG USER ONLY */
|
||||
|
||||
void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
cpu->current_tb = NULL;
|
||||
siglongjmp(cpu->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)
|
||||
{
|
||||
/* XXX: restore cpu registers saved in host registers */
|
||||
|
||||
cpu->exception_index = -1;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_reload_memory_map(CPUState *cpu)
|
||||
{
|
||||
AddressSpaceDispatch *d;
|
||||
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
/* Do not let the guest prolong the critical section as much as it
|
||||
* as it desires.
|
||||
*
|
||||
* Currently, this is prevented by the I/O thread's periodinc kicking
|
||||
* of the VCPU thread (iothread_requesting_mutex, qemu_cpu_kick_thread)
|
||||
* but this will go away once TCG's execution moves out of the global
|
||||
* mutex.
|
||||
*
|
||||
* This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which
|
||||
* only protects cpu->as->dispatch. Since we reload it below, we can
|
||||
* split the critical section.
|
||||
*/
|
||||
rcu_read_unlock();
|
||||
rcu_read_lock();
|
||||
}
|
||||
|
||||
/* The CPU and TLB are protected by the iothread lock. */
|
||||
d = atomic_rcu_read(&cpu->as->dispatch);
|
||||
cpu->memory_dispatch = d;
|
||||
tlb_flush(cpu, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Execute a TB, and fix up the CPU state afterwards if necessary */
|
||||
static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
|
||||
{
|
||||
@@ -153,7 +196,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
|
||||
cpu->can_do_io = !use_icount;
|
||||
cpu->can_do_io = 0;
|
||||
next_tb = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
cpu->can_do_io = 1;
|
||||
trace_exec_tb_exit((void *) (next_tb & ~TB_EXIT_MASK),
|
||||
@@ -185,19 +228,22 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
|
||||
/* Execute the code without caching the generated code. An interpreter
|
||||
could be used if available. */
|
||||
static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
||||
TranslationBlock *orig_tb, bool ignore_icount)
|
||||
TranslationBlock *orig_tb)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
target_ulong pc = orig_tb->pc;
|
||||
target_ulong cs_base = orig_tb->cs_base;
|
||||
uint64_t flags = orig_tb->flags;
|
||||
|
||||
/* Should never happen.
|
||||
We only end up here when an existing TB is too long. */
|
||||
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,
|
||||
max_cycles | CF_NOCACHE
|
||||
| (ignore_icount ? CF_IGNORE_ICOUNT : 0));
|
||||
tb->orig_tb = tcg_ctx.tb_ctx.tb_invalidated_flag ? NULL : orig_tb;
|
||||
/* tb_gen_code can flush our orig_tb, invalidate it now */
|
||||
tb_phys_invalidate(orig_tb, -1);
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags,
|
||||
max_cycles | CF_NOCACHE);
|
||||
cpu->current_tb = tb;
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, tb->pc);
|
||||
@@ -207,10 +253,10 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
||||
tb_free(tb);
|
||||
}
|
||||
|
||||
static TranslationBlock *tb_find_physical(CPUState *cpu,
|
||||
target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
uint64_t flags)
|
||||
static TranslationBlock *tb_find_slow(CPUState *cpu,
|
||||
target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
uint64_t flags)
|
||||
{
|
||||
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
|
||||
TranslationBlock *tb, **ptb1;
|
||||
@@ -227,9 +273,8 @@ static TranslationBlock *tb_find_physical(CPUState *cpu,
|
||||
ptb1 = &tcg_ctx.tb_ctx.tb_phys_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb1;
|
||||
if (!tb) {
|
||||
return NULL;
|
||||
}
|
||||
if (!tb)
|
||||
goto not_found;
|
||||
if (tb->pc == pc &&
|
||||
tb->page_addr[0] == phys_page1 &&
|
||||
tb->cs_base == cs_base &&
|
||||
@@ -241,59 +286,25 @@ static TranslationBlock *tb_find_physical(CPUState *cpu,
|
||||
virt_page2 = (pc & TARGET_PAGE_MASK) +
|
||||
TARGET_PAGE_SIZE;
|
||||
phys_page2 = get_page_addr_code(env, virt_page2);
|
||||
if (tb->page_addr[1] == phys_page2) {
|
||||
break;
|
||||
}
|
||||
if (tb->page_addr[1] == phys_page2)
|
||||
goto found;
|
||||
} else {
|
||||
break;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
ptb1 = &tb->phys_hash_next;
|
||||
}
|
||||
|
||||
/* Move the TB to the head of the list */
|
||||
*ptb1 = tb->phys_hash_next;
|
||||
tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h];
|
||||
tcg_ctx.tb_ctx.tb_phys_hash[h] = tb;
|
||||
return tb;
|
||||
}
|
||||
|
||||
static TranslationBlock *tb_find_slow(CPUState *cpu,
|
||||
target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
uint64_t flags)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
|
||||
tb = tb_find_physical(cpu, pc, cs_base, flags);
|
||||
if (tb) {
|
||||
goto found;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* mmap_lock is needed by tb_gen_code, and mmap_lock must be
|
||||
* taken outside tb_lock. Since we're momentarily dropping
|
||||
* tb_lock, there's a chance that our desired tb has been
|
||||
* translated.
|
||||
*/
|
||||
tb_unlock();
|
||||
mmap_lock();
|
||||
tb_lock();
|
||||
tb = tb_find_physical(cpu, pc, cs_base, flags);
|
||||
if (tb) {
|
||||
mmap_unlock();
|
||||
goto found;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if no translated code available, then translate it now */
|
||||
not_found:
|
||||
/* if no translated code available, then translate it now */
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, 0);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
mmap_unlock();
|
||||
#endif
|
||||
|
||||
found:
|
||||
found:
|
||||
/* Move the last found TB to the head of the list */
|
||||
if (likely(*ptb1)) {
|
||||
*ptb1 = tb->phys_hash_next;
|
||||
tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h];
|
||||
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;
|
||||
return tb;
|
||||
@@ -334,6 +345,8 @@ static void cpu_handle_debug_exception(CPUState *cpu)
|
||||
|
||||
/* main execution loop */
|
||||
|
||||
volatile sig_atomic_t exit_request;
|
||||
|
||||
int cpu_exec(CPUState *cpu)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
@@ -347,29 +360,30 @@ int cpu_exec(CPUState *cpu)
|
||||
uintptr_t next_tb;
|
||||
SyncClocks sc;
|
||||
|
||||
/* replay_interrupt may need current_cpu */
|
||||
current_cpu = cpu;
|
||||
/* This must be volatile so it is not trashed by longjmp() */
|
||||
volatile bool have_tb_lock = false;
|
||||
|
||||
if (cpu->halted) {
|
||||
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
|
||||
if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
|
||||
&& replay_interrupt()) {
|
||||
apic_poll_irq(x86_cpu->apic_state);
|
||||
cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
|
||||
}
|
||||
#endif
|
||||
if (!cpu_has_work(cpu)) {
|
||||
current_cpu = NULL;
|
||||
return EXCP_HALTED;
|
||||
}
|
||||
|
||||
cpu->halted = 0;
|
||||
}
|
||||
|
||||
atomic_mb_set(&tcg_current_cpu, cpu);
|
||||
current_cpu = cpu;
|
||||
|
||||
/* As long as current_cpu is null, up to the assignment just above,
|
||||
* requests by other threads to exit the execution loop are expected to
|
||||
* be issued using the exit_request global. We must make sure that our
|
||||
* evaluation of the global value is performed past the current_cpu
|
||||
* value transition point, which requires a memory barrier as well as
|
||||
* an instruction scheduling constraint on modern architectures. */
|
||||
smp_mb();
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (unlikely(atomic_mb_read(&exit_request))) {
|
||||
if (unlikely(exit_request)) {
|
||||
cpu->exit_request = 1;
|
||||
}
|
||||
|
||||
@@ -407,22 +421,10 @@ int cpu_exec(CPUState *cpu)
|
||||
cpu->exception_index = -1;
|
||||
break;
|
||||
#else
|
||||
if (replay_exception()) {
|
||||
cc->do_interrupt(cpu);
|
||||
cpu->exception_index = -1;
|
||||
} else if (!replay_has_interrupt()) {
|
||||
/* give a chance to iothread in replay mode */
|
||||
ret = EXCP_INTERRUPT;
|
||||
break;
|
||||
}
|
||||
cc->do_interrupt(cpu);
|
||||
cpu->exception_index = -1;
|
||||
#endif
|
||||
}
|
||||
} else if (replay_has_exception()
|
||||
&& cpu->icount_decr.u16.low + cpu->icount_extra == 0) {
|
||||
/* try to cause an exception pending in the log */
|
||||
cpu_exec_nocache(cpu, 1, tb_find_fast(cpu), true);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
next_tb = 0; /* force lookup of first TB */
|
||||
@@ -438,40 +440,30 @@ int cpu_exec(CPUState *cpu)
|
||||
cpu->exception_index = EXCP_DEBUG;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
if (replay_mode == REPLAY_MODE_PLAY
|
||||
&& !replay_has_interrupt()) {
|
||||
/* Do nothing */
|
||||
} else if (interrupt_request & CPU_INTERRUPT_HALT) {
|
||||
replay_interrupt();
|
||||
if (interrupt_request & CPU_INTERRUPT_HALT) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
||||
cpu->halted = 1;
|
||||
cpu->exception_index = EXCP_HLT;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
else if (interrupt_request & CPU_INTERRUPT_INIT) {
|
||||
replay_interrupt();
|
||||
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
|
||||
else if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
replay_interrupt();
|
||||
if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
cpu_reset(cpu);
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#endif
|
||||
/* The target hook has 3 exit conditions:
|
||||
False when the interrupt isn't processed,
|
||||
True when it is, and we should restart on a new TB,
|
||||
and via longjmp via cpu_loop_exit. */
|
||||
else {
|
||||
replay_interrupt();
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
next_tb = 0;
|
||||
}
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
next_tb = 0;
|
||||
}
|
||||
/* Don't use the cached interrupt_request value,
|
||||
do_interrupt may have updated the EXITTB flag. */
|
||||
@@ -482,13 +474,13 @@ int cpu_exec(CPUState *cpu)
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
if (unlikely(cpu->exit_request
|
||||
|| replay_has_interrupt())) {
|
||||
if (unlikely(cpu->exit_request)) {
|
||||
cpu->exit_request = 0;
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
tb_lock();
|
||||
spin_lock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
have_tb_lock = true;
|
||||
tb = tb_find_fast(cpu);
|
||||
/* Note: we do it here to avoid a gcc bug on Mac OS X when
|
||||
doing it in tb_find_slow */
|
||||
@@ -506,19 +498,24 @@ int cpu_exec(CPUState *cpu)
|
||||
/* see if we can patch the calling TB. When the TB
|
||||
spans two pages, we cannot safely do a direct
|
||||
jump. */
|
||||
if (next_tb != 0 && tb->page_addr[1] == -1
|
||||
&& !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
if (next_tb != 0 && tb->page_addr[1] == -1) {
|
||||
tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK),
|
||||
next_tb & TB_EXIT_MASK, tb);
|
||||
}
|
||||
tb_unlock();
|
||||
have_tb_lock = false;
|
||||
spin_unlock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
|
||||
/* cpu_interrupt might be called while translating the
|
||||
TB, but before it is linked into a potentially
|
||||
infinite loop and becomes env->current_tb. Avoid
|
||||
starting execution if there is a pending interrupt. */
|
||||
cpu->current_tb = tb;
|
||||
barrier();
|
||||
if (likely(!cpu->exit_request)) {
|
||||
trace_exec_tb(tb, tb->pc);
|
||||
tc_ptr = tb->tc_ptr;
|
||||
/* execute the generated code */
|
||||
cpu->current_tb = tb;
|
||||
next_tb = cpu_tb_exec(cpu, tc_ptr);
|
||||
cpu->current_tb = NULL;
|
||||
switch (next_tb & TB_EXIT_MASK) {
|
||||
case TB_EXIT_REQUESTED:
|
||||
/* Something asked us to stop executing
|
||||
@@ -526,12 +523,8 @@ int cpu_exec(CPUState *cpu)
|
||||
* loop. Whatever requested the exit will also
|
||||
* have set something else (eg exit_request or
|
||||
* interrupt_request) which we will handle
|
||||
* next time around the loop. But we need to
|
||||
* ensure the tcg_exit_req read in generated code
|
||||
* comes before the next read of cpu->exit_request
|
||||
* or cpu->interrupt_request.
|
||||
* next time around the loop.
|
||||
*/
|
||||
smp_rmb();
|
||||
next_tb = 0;
|
||||
break;
|
||||
case TB_EXIT_ICOUNT_EXPIRED:
|
||||
@@ -548,7 +541,7 @@ int cpu_exec(CPUState *cpu)
|
||||
if (insns_left > 0) {
|
||||
/* Execute remaining instructions. */
|
||||
tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
|
||||
cpu_exec_nocache(cpu, insns_left, tb, false);
|
||||
cpu_exec_nocache(cpu, insns_left, tb);
|
||||
align_clocks(&sc, cpu);
|
||||
}
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
@@ -561,6 +554,7 @@ int cpu_exec(CPUState *cpu)
|
||||
break;
|
||||
}
|
||||
}
|
||||
cpu->current_tb = NULL;
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
align_clocks(&sc, cpu);
|
||||
@@ -568,28 +562,19 @@ int cpu_exec(CPUState *cpu)
|
||||
only be set by a memory fault) */
|
||||
} /* for(;;) */
|
||||
} else {
|
||||
#if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6)
|
||||
/* Some compilers wrongly smash all local variables after
|
||||
* siglongjmp. There were bug reports for gcc 4.5.0 and clang.
|
||||
* Reload essential local variables here for those compilers.
|
||||
* Newer versions of gcc would complain about this code (-Wclobbered). */
|
||||
/* Reload env after longjmp - the compiler may have smashed all
|
||||
* local variables as longjmp is marked 'noreturn'. */
|
||||
cpu = current_cpu;
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
cpu->can_do_io = 1;
|
||||
#ifdef TARGET_I386
|
||||
x86_cpu = X86_CPU(cpu);
|
||||
env = &x86_cpu->env;
|
||||
#endif
|
||||
#else /* buggy compiler */
|
||||
/* Assert that the compiler does not smash local variables. */
|
||||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
#ifdef TARGET_I386
|
||||
g_assert(x86_cpu == X86_CPU(cpu));
|
||||
g_assert(env == &x86_cpu->env);
|
||||
#endif
|
||||
#endif /* buggy compiler */
|
||||
cpu->can_do_io = 1;
|
||||
tb_lock_reset();
|
||||
if (have_tb_lock) {
|
||||
spin_unlock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
have_tb_lock = false;
|
||||
}
|
||||
}
|
||||
} /* for(;;) */
|
||||
|
||||
@@ -598,8 +583,5 @@ int cpu_exec(CPUState *cpu)
|
||||
|
||||
/* fail safe : never use current_cpu outside cpu_exec() */
|
||||
current_cpu = NULL;
|
||||
|
||||
/* Does not need atomic_mb_set because a spurious wakeup is okay. */
|
||||
atomic_set(&tcg_current_cpu, NULL);
|
||||
return ret;
|
||||
}
|
||||
@@ -42,7 +42,6 @@
|
||||
#include "qemu/seqlock.h"
|
||||
#include "qapi-event.h"
|
||||
#include "hw/nmi.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "qemu/compatfd.h"
|
||||
@@ -70,14 +69,6 @@ static CPUState *next_cpu;
|
||||
int64_t max_delay;
|
||||
int64_t max_advance;
|
||||
|
||||
/* vcpu throttling controls */
|
||||
static QEMUTimer *throttle_timer;
|
||||
static unsigned int throttle_percentage;
|
||||
|
||||
#define CPU_THROTTLE_PCT_MIN 1
|
||||
#define CPU_THROTTLE_PCT_MAX 99
|
||||
#define CPU_THROTTLE_TIMESLICE_NS 10000000
|
||||
|
||||
bool cpu_is_stopped(CPUState *cpu)
|
||||
{
|
||||
return cpu->stopped || !runstate_is_running();
|
||||
@@ -154,7 +145,7 @@ int64_t cpu_get_icount_raw(void)
|
||||
|
||||
icount = timers_state.qemu_icount;
|
||||
if (cpu) {
|
||||
if (!cpu->can_do_io) {
|
||||
if (!cpu_can_do_io(cpu)) {
|
||||
fprintf(stderr, "Bad icount read\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -200,7 +191,7 @@ int64_t cpu_get_ticks(void)
|
||||
|
||||
ticks = timers_state.cpu_ticks_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
ticks += cpu_get_host_ticks();
|
||||
ticks += cpu_get_real_ticks();
|
||||
}
|
||||
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
@@ -248,7 +239,7 @@ void cpu_enable_ticks(void)
|
||||
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock);
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
|
||||
timers_state.cpu_ticks_offset -= cpu_get_real_ticks();
|
||||
timers_state.cpu_clock_offset -= get_clock();
|
||||
timers_state.cpu_ticks_enabled = 1;
|
||||
}
|
||||
@@ -264,7 +255,7 @@ void cpu_disable_ticks(void)
|
||||
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock);
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset += cpu_get_host_ticks();
|
||||
timers_state.cpu_ticks_offset += cpu_get_real_ticks();
|
||||
timers_state.cpu_clock_offset = cpu_get_clock_locked();
|
||||
timers_state.cpu_ticks_enabled = 0;
|
||||
}
|
||||
@@ -335,7 +326,7 @@ static int64_t qemu_icount_round(int64_t count)
|
||||
return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
|
||||
}
|
||||
|
||||
static void icount_warp_rt(void)
|
||||
static void icount_warp_rt(void *opaque)
|
||||
{
|
||||
/* The icount_warp_timer is rescheduled soon after vm_clock_warp_start
|
||||
* changes from -1 to another value, so the race here is okay.
|
||||
@@ -346,8 +337,7 @@ static void icount_warp_rt(void)
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock);
|
||||
if (runstate_is_running()) {
|
||||
int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT,
|
||||
cpu_get_clock_locked());
|
||||
int64_t clock = cpu_get_clock_locked();
|
||||
int64_t warp_delta;
|
||||
|
||||
warp_delta = clock - vm_clock_warp_start;
|
||||
@@ -370,11 +360,6 @@ static void icount_warp_rt(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void icount_dummy_timer(void *opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
}
|
||||
|
||||
void qtest_clock_warp(int64_t dest)
|
||||
{
|
||||
int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
@@ -410,18 +395,6 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
|
||||
* do not fire, so computing the deadline does not make sense.
|
||||
*/
|
||||
if (!runstate_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (icount_sleep) {
|
||||
/*
|
||||
* If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now.
|
||||
@@ -431,7 +404,7 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
* the CPU starts running, in case the CPU is woken by an event other
|
||||
* than the earliest QEMU_CLOCK_VIRTUAL timer.
|
||||
*/
|
||||
icount_warp_rt();
|
||||
icount_warp_rt(NULL);
|
||||
timer_del(icount_warp_timer);
|
||||
}
|
||||
if (!all_cpu_threads_idle()) {
|
||||
@@ -532,80 +505,10 @@ static const VMStateDescription vmstate_timers = {
|
||||
}
|
||||
};
|
||||
|
||||
static void cpu_throttle_thread(void *opaque)
|
||||
{
|
||||
CPUState *cpu = opaque;
|
||||
double pct;
|
||||
double throttle_ratio;
|
||||
long sleeptime_ns;
|
||||
|
||||
if (!cpu_throttle_get_percentage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pct = (double)cpu_throttle_get_percentage()/100;
|
||||
throttle_ratio = pct / (1 - pct);
|
||||
sleeptime_ns = (long)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS);
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
atomic_set(&cpu->throttle_thread_scheduled, 0);
|
||||
g_usleep(sleeptime_ns / 1000); /* Convert ns to us for usleep call */
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
static void cpu_throttle_timer_tick(void *opaque)
|
||||
{
|
||||
CPUState *cpu;
|
||||
double pct;
|
||||
|
||||
/* Stop the timer if needed */
|
||||
if (!cpu_throttle_get_percentage()) {
|
||||
return;
|
||||
}
|
||||
CPU_FOREACH(cpu) {
|
||||
if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
|
||||
async_run_on_cpu(cpu, cpu_throttle_thread, cpu);
|
||||
}
|
||||
}
|
||||
|
||||
pct = (double)cpu_throttle_get_percentage()/100;
|
||||
timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
|
||||
CPU_THROTTLE_TIMESLICE_NS / (1-pct));
|
||||
}
|
||||
|
||||
void cpu_throttle_set(int new_throttle_pct)
|
||||
{
|
||||
/* Ensure throttle percentage is within valid range */
|
||||
new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);
|
||||
new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);
|
||||
|
||||
atomic_set(&throttle_percentage, new_throttle_pct);
|
||||
|
||||
timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
|
||||
CPU_THROTTLE_TIMESLICE_NS);
|
||||
}
|
||||
|
||||
void cpu_throttle_stop(void)
|
||||
{
|
||||
atomic_set(&throttle_percentage, 0);
|
||||
}
|
||||
|
||||
bool cpu_throttle_active(void)
|
||||
{
|
||||
return (cpu_throttle_get_percentage() != 0);
|
||||
}
|
||||
|
||||
int cpu_throttle_get_percentage(void)
|
||||
{
|
||||
return atomic_read(&throttle_percentage);
|
||||
}
|
||||
|
||||
void cpu_ticks_init(void)
|
||||
{
|
||||
seqlock_init(&timers_state.vm_clock_seqlock, NULL);
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
cpu_throttle_timer_tick, NULL);
|
||||
}
|
||||
|
||||
void configure_icount(QemuOpts *opts, Error **errp)
|
||||
@@ -624,7 +527,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
icount_sleep = qemu_opt_get_bool(opts, "sleep", true);
|
||||
if (icount_sleep) {
|
||||
icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
icount_dummy_timer, NULL);
|
||||
icount_warp_rt, NULL);
|
||||
}
|
||||
|
||||
icount_align_option = qemu_opt_get_bool(opts, "align", false);
|
||||
@@ -713,6 +616,15 @@ 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;
|
||||
@@ -749,6 +661,14 @@ static void cpu_handle_guest_debug(CPUState *cpu)
|
||||
cpu->stopped = true;
|
||||
}
|
||||
|
||||
static void cpu_signal(int sig)
|
||||
{
|
||||
if (current_cpu) {
|
||||
cpu_exit(current_cpu);
|
||||
}
|
||||
exit_request = 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
static void sigbus_reraise(void)
|
||||
{
|
||||
@@ -861,11 +781,29 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_tcg_init_cpu_signals(void)
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction sigact;
|
||||
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_handler = cpu_signal;
|
||||
sigaction(SIG_IPI, &sigact, NULL);
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIG_IPI);
|
||||
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
|
||||
}
|
||||
|
||||
#else /* _WIN32 */
|
||||
static void qemu_kvm_init_cpu_signals(CPUState *cpu)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static void qemu_tcg_init_cpu_signals(void)
|
||||
{
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static QemuMutex qemu_global_mutex;
|
||||
@@ -874,6 +812,9 @@ static unsigned iothread_requesting_mutex;
|
||||
|
||||
static QemuThread io_thread;
|
||||
|
||||
static QemuThread *tcg_cpu_thread;
|
||||
static QemuCond *tcg_halt_cond;
|
||||
|
||||
/* cpu creation */
|
||||
static QemuCond qemu_cpu_cond;
|
||||
/* system init */
|
||||
@@ -904,8 +845,6 @@ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
wi.func = func;
|
||||
wi.data = data;
|
||||
wi.free = false;
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = &wi;
|
||||
} else {
|
||||
@@ -914,10 +853,9 @@ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
cpu->queued_work_last = &wi;
|
||||
wi.next = NULL;
|
||||
wi.done = false;
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
|
||||
qemu_cpu_kick(cpu);
|
||||
while (!atomic_mb_read(&wi.done)) {
|
||||
while (!wi.done) {
|
||||
CPUState *self_cpu = current_cpu;
|
||||
|
||||
qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex);
|
||||
@@ -938,8 +876,6 @@ void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
wi->func = func;
|
||||
wi->data = data;
|
||||
wi->free = true;
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = wi;
|
||||
} else {
|
||||
@@ -948,7 +884,6 @@ void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
cpu->queued_work_last = wi;
|
||||
wi->next = NULL;
|
||||
wi->done = false;
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
@@ -961,23 +896,15 @@ static void flush_queued_work(CPUState *cpu)
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
while (cpu->queued_work_first != NULL) {
|
||||
wi = cpu->queued_work_first;
|
||||
while ((wi = cpu->queued_work_first)) {
|
||||
cpu->queued_work_first = wi->next;
|
||||
if (!cpu->queued_work_first) {
|
||||
cpu->queued_work_last = NULL;
|
||||
}
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
wi->func(wi->data);
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
wi->done = true;
|
||||
if (wi->free) {
|
||||
g_free(wi);
|
||||
} else {
|
||||
atomic_mb_set(&wi->done, true);
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
cpu->queued_work_last = NULL;
|
||||
qemu_cond_broadcast(&qemu_work_cond);
|
||||
}
|
||||
|
||||
@@ -986,19 +913,21 @@ static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
if (cpu->stop) {
|
||||
cpu->stop = false;
|
||||
cpu->stopped = true;
|
||||
qemu_cond_broadcast(&qemu_pause_cond);
|
||||
qemu_cond_signal(&qemu_pause_cond);
|
||||
}
|
||||
flush_queued_work(cpu);
|
||||
cpu->thread_kicked = false;
|
||||
}
|
||||
|
||||
static void qemu_tcg_wait_io_event(CPUState *cpu)
|
||||
static void qemu_tcg_wait_io_event(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
while (all_cpu_threads_idle()) {
|
||||
/* Start accounting real time to the virtual clock if the CPUs
|
||||
are idle. */
|
||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
while (iothread_requesting_mutex) {
|
||||
@@ -1112,6 +1041,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
rcu_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_tcg_init_cpu_signals();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
@@ -1123,7 +1053,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
|
||||
/* wait for initial kick-off after machine start */
|
||||
while (first_cpu->stopped) {
|
||||
qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex);
|
||||
qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex);
|
||||
|
||||
/* process any pending work */
|
||||
CPU_FOREACH(cpu) {
|
||||
@@ -1132,7 +1062,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
}
|
||||
|
||||
/* process any pending work */
|
||||
atomic_mb_set(&exit_request, 1);
|
||||
exit_request = 1;
|
||||
|
||||
while (1) {
|
||||
tcg_exec_all();
|
||||
@@ -1144,7 +1074,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
}
|
||||
qemu_tcg_wait_io_event(QTAILQ_FIRST(&cpus));
|
||||
qemu_tcg_wait_io_event();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -1155,47 +1085,61 @@ static void qemu_cpu_kick_thread(CPUState *cpu)
|
||||
#ifndef _WIN32
|
||||
int err;
|
||||
|
||||
if (cpu->thread_kicked) {
|
||||
return;
|
||||
}
|
||||
cpu->thread_kicked = true;
|
||||
err = pthread_kill(cpu->thread->thread, SIG_IPI);
|
||||
if (err) {
|
||||
fprintf(stderr, "qemu:%s: %s", __func__, strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
#else /* _WIN32 */
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
CONTEXT tcgContext;
|
||||
|
||||
static void qemu_cpu_kick_no_halt(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
/* Ensure whatever caused the exit has reached the CPU threads before
|
||||
* writing exit_request.
|
||||
*/
|
||||
atomic_mb_set(&exit_request, 1);
|
||||
cpu = atomic_mb_read(&tcg_current_cpu);
|
||||
if (cpu) {
|
||||
cpu_exit(cpu);
|
||||
if (SuspendThread(cpu->hThread) == (DWORD)-1) {
|
||||
fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__,
|
||||
GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* On multi-core systems, we are not sure that the thread is actually
|
||||
* suspended until we can get the context.
|
||||
*/
|
||||
tcgContext.ContextFlags = CONTEXT_CONTROL;
|
||||
while (GetThreadContext(cpu->hThread, &tcgContext) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cpu_signal(0);
|
||||
|
||||
if (ResumeThread(cpu->hThread) == (DWORD)-1) {
|
||||
fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__,
|
||||
GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void qemu_cpu_kick(CPUState *cpu)
|
||||
{
|
||||
qemu_cond_broadcast(cpu->halt_cond);
|
||||
if (tcg_enabled()) {
|
||||
qemu_cpu_kick_no_halt();
|
||||
} else {
|
||||
if (!tcg_enabled() && !cpu->thread_kicked) {
|
||||
qemu_cpu_kick_thread(cpu);
|
||||
cpu->thread_kicked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_cpu_kick_self(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
assert(current_cpu);
|
||||
qemu_cpu_kick_thread(current_cpu);
|
||||
|
||||
if (!current_cpu->thread_kicked) {
|
||||
qemu_cpu_kick_thread(current_cpu);
|
||||
current_cpu->thread_kicked = true;
|
||||
}
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool qemu_cpu_is_self(CPUState *cpu)
|
||||
@@ -1227,7 +1171,7 @@ void qemu_mutex_lock_iothread(void)
|
||||
atomic_dec(&iothread_requesting_mutex);
|
||||
} else {
|
||||
if (qemu_mutex_trylock(&qemu_global_mutex)) {
|
||||
qemu_cpu_kick_no_halt();
|
||||
qemu_cpu_kick_thread(first_cpu);
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
}
|
||||
atomic_dec(&iothread_requesting_mutex);
|
||||
@@ -1307,8 +1251,6 @@ void resume_all_vcpus(void)
|
||||
static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
static QemuCond *tcg_halt_cond;
|
||||
static QemuThread *tcg_cpu_thread;
|
||||
|
||||
tcg_cpu_address_space_init(cpu, cpu->as);
|
||||
|
||||
@@ -1387,7 +1329,7 @@ void cpu_stop_current(void)
|
||||
current_cpu->stop = false;
|
||||
current_cpu->stopped = true;
|
||||
cpu_exit(current_cpu);
|
||||
qemu_cond_broadcast(&qemu_pause_cond);
|
||||
qemu_cond_signal(&qemu_pause_cond);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1415,36 +1357,12 @@ int vm_stop_force_state(RunState state)
|
||||
return vm_stop(state);
|
||||
} else {
|
||||
runstate_set(state);
|
||||
|
||||
bdrv_drain_all();
|
||||
/* Make sure to return an error if the flush in a previous vm_stop()
|
||||
* failed. */
|
||||
return bdrv_flush_all();
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t tcg_get_icount_limit(void)
|
||||
{
|
||||
int64_t deadline;
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
/* Maintain prior (possibly buggy) behaviour where if no deadline
|
||||
* was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than
|
||||
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
|
||||
* nanoseconds.
|
||||
*/
|
||||
if ((deadline < 0) || (deadline > INT32_MAX)) {
|
||||
deadline = INT32_MAX;
|
||||
}
|
||||
|
||||
return qemu_icount_round(deadline);
|
||||
} else {
|
||||
return replay_get_instructions();
|
||||
}
|
||||
}
|
||||
|
||||
static int tcg_cpu_exec(CPUState *cpu)
|
||||
{
|
||||
int ret;
|
||||
@@ -1457,12 +1375,24 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
#endif
|
||||
if (use_icount) {
|
||||
int64_t count;
|
||||
int64_t deadline;
|
||||
int decr;
|
||||
timers_state.qemu_icount -= (cpu->icount_decr.u16.low
|
||||
+ cpu->icount_extra);
|
||||
cpu->icount_decr.u16.low = 0;
|
||||
cpu->icount_extra = 0;
|
||||
count = tcg_get_icount_limit();
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
/* Maintain prior (possibly buggy) behaviour where if no deadline
|
||||
* was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than
|
||||
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
|
||||
* nanoseconds.
|
||||
*/
|
||||
if ((deadline < 0) || (deadline > INT32_MAX)) {
|
||||
deadline = INT32_MAX;
|
||||
}
|
||||
|
||||
count = qemu_icount_round(deadline);
|
||||
timers_state.qemu_icount += count;
|
||||
decr = (count > 0xffff) ? 0xffff : count;
|
||||
count -= decr;
|
||||
@@ -1480,7 +1410,6 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
+ cpu->icount_extra);
|
||||
cpu->icount_decr.u32 = 0;
|
||||
cpu->icount_extra = 0;
|
||||
replay_account_executed_instructions();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1511,9 +1440,7 @@ static void tcg_exec_all(void)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pairs with smp_wmb in qemu_cpu_kick. */
|
||||
atomic_mb_set(&exit_request, 0);
|
||||
exit_request = 0;
|
||||
}
|
||||
|
||||
void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
|
||||
|
||||
@@ -69,47 +69,6 @@ void tlb_flush(CPUState *cpu, int flush_global)
|
||||
tlb_flush_count++;
|
||||
}
|
||||
|
||||
static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, va_list argp)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf("tlb_flush_by_mmuidx:");
|
||||
#endif
|
||||
/* must reset current TB so that interrupts cannot modify the
|
||||
links while we are modifying them */
|
||||
cpu->current_tb = NULL;
|
||||
|
||||
for (;;) {
|
||||
int mmu_idx = va_arg(argp, int);
|
||||
|
||||
if (mmu_idx < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf(" %d", mmu_idx);
|
||||
#endif
|
||||
|
||||
memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0]));
|
||||
memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
|
||||
}
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache));
|
||||
}
|
||||
|
||||
void tlb_flush_by_mmuidx(CPUState *cpu, ...)
|
||||
{
|
||||
va_list argp;
|
||||
va_start(argp, cpu);
|
||||
v_tlb_flush_by_mmuidx(cpu, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
|
||||
{
|
||||
if (addr == (tlb_entry->addr_read &
|
||||
@@ -162,62 +121,6 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
tb_flush_jmp_cache(cpu, addr);
|
||||
}
|
||||
|
||||
void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int i, k;
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, addr);
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf("tlb_flush_page_by_mmu_idx: " TARGET_FMT_lx, addr);
|
||||
#endif
|
||||
/* Check if we need to flush due to large pages. */
|
||||
if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) {
|
||||
#if defined(DEBUG_TLB)
|
||||
printf(" forced full flush ("
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
env->tlb_flush_addr, env->tlb_flush_mask);
|
||||
#endif
|
||||
v_tlb_flush_by_mmuidx(cpu, argp);
|
||||
va_end(argp);
|
||||
return;
|
||||
}
|
||||
/* must reset current TB so that interrupts cannot modify the
|
||||
links while we are modifying them */
|
||||
cpu->current_tb = NULL;
|
||||
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
|
||||
for (;;) {
|
||||
int mmu_idx = va_arg(argp, int);
|
||||
|
||||
if (mmu_idx < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf(" %d", mmu_idx);
|
||||
#endif
|
||||
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
|
||||
|
||||
/* check whether there are vltb entries that need to be flushed */
|
||||
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
||||
tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr);
|
||||
}
|
||||
}
|
||||
va_end(argp);
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
tb_flush_jmp_cache(cpu, 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)
|
||||
@@ -262,24 +165,27 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
|
||||
return ram_addr;
|
||||
}
|
||||
|
||||
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
||||
void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length)
|
||||
{
|
||||
CPUState *cpu;
|
||||
CPUArchState *env;
|
||||
|
||||
int mmu_idx;
|
||||
CPU_FOREACH(cpu) {
|
||||
int mmu_idx;
|
||||
|
||||
env = cpu->env_ptr;
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
unsigned int i;
|
||||
env = cpu->env_ptr;
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < CPU_TLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i],
|
||||
start1, length);
|
||||
}
|
||||
for (i = 0; i < CPU_TLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i],
|
||||
start1, length);
|
||||
}
|
||||
|
||||
for (i = 0; i < CPU_VTLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i],
|
||||
start1, length);
|
||||
for (i = 0; i < CPU_VTLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i],
|
||||
start1, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,9 +199,8 @@ static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
|
||||
|
||||
/* update the TLB corresponding to virtual page vaddr
|
||||
so that it is no longer dirty */
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
void tlb_set_dirty(CPUArchState *env, target_ulong vaddr)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
@@ -450,7 +355,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
CPUState *cpu = ENV_GET_CPU(env1);
|
||||
|
||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = cpu_mmu_index(env1, true);
|
||||
mmu_idx = cpu_mmu_index(env1);
|
||||
if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code !=
|
||||
(addr & TARGET_PAGE_MASK))) {
|
||||
cpu_ldub_code(env1, addr);
|
||||
|
||||
+5
-12
@@ -1,12 +1,5 @@
|
||||
crypto-obj-y = init.o
|
||||
crypto-obj-y += hash.o
|
||||
crypto-obj-y += aes.o
|
||||
crypto-obj-y += desrfb.o
|
||||
crypto-obj-y += cipher.o
|
||||
crypto-obj-y += tlscreds.o
|
||||
crypto-obj-y += tlscredsanon.o
|
||||
crypto-obj-y += tlscredsx509.o
|
||||
crypto-obj-y += tlssession.o
|
||||
|
||||
# Let the userspace emulators avoid linking gnutls/etc
|
||||
crypto-aes-obj-y = aes.o
|
||||
util-obj-y += init.o
|
||||
util-obj-y += hash.o
|
||||
util-obj-y += aes.o
|
||||
util-obj-y += desrfb.o
|
||||
util-obj-y += cipher.o
|
||||
+9
-20
@@ -25,7 +25,8 @@ typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
|
||||
struct QCryptoCipherBuiltinAES {
|
||||
AES_KEY encrypt_key;
|
||||
AES_KEY decrypt_key;
|
||||
uint8_t iv[AES_BLOCK_SIZE];
|
||||
uint8_t *iv;
|
||||
size_t niv;
|
||||
};
|
||||
typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
|
||||
struct QCryptoCipherBuiltinDESRFB {
|
||||
@@ -39,7 +40,6 @@ struct QCryptoCipherBuiltin {
|
||||
QCryptoCipherBuiltinAES aes;
|
||||
QCryptoCipherBuiltinDESRFB desrfb;
|
||||
} state;
|
||||
size_t blocksize;
|
||||
void (*free)(QCryptoCipher *cipher);
|
||||
int (*setiv)(QCryptoCipher *cipher,
|
||||
const uint8_t *iv, size_t niv,
|
||||
@@ -61,6 +61,7 @@ static void qcrypto_cipher_free_aes(QCryptoCipher *cipher)
|
||||
{
|
||||
QCryptoCipherBuiltin *ctxt = cipher->opaque;
|
||||
|
||||
g_free(ctxt->state.aes.iv);
|
||||
g_free(ctxt);
|
||||
cipher->opaque = NULL;
|
||||
}
|
||||
@@ -144,13 +145,15 @@ static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltin *ctxt = cipher->opaque;
|
||||
if (niv != AES_BLOCK_SIZE) {
|
||||
error_setg(errp, "IV must be %d bytes not %zu",
|
||||
AES_BLOCK_SIZE, niv);
|
||||
if (niv != 16) {
|
||||
error_setg(errp, "IV must be 16 bytes not %zu", niv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(ctxt->state.aes.iv, iv, AES_BLOCK_SIZE);
|
||||
g_free(ctxt->state.aes.iv);
|
||||
ctxt->state.aes.iv = g_new0(uint8_t, niv);
|
||||
memcpy(ctxt->state.aes.iv, iv, niv);
|
||||
ctxt->state.aes.niv = niv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -182,7 +185,6 @@ static int qcrypto_cipher_init_aes(QCryptoCipher *cipher,
|
||||
goto error;
|
||||
}
|
||||
|
||||
ctxt->blocksize = AES_BLOCK_SIZE;
|
||||
ctxt->free = qcrypto_cipher_free_aes;
|
||||
ctxt->setiv = qcrypto_cipher_setiv_aes;
|
||||
ctxt->encrypt = qcrypto_cipher_encrypt_aes;
|
||||
@@ -284,7 +286,6 @@ static int qcrypto_cipher_init_des_rfb(QCryptoCipher *cipher,
|
||||
memcpy(ctxt->state.desrfb.key, key, nkey);
|
||||
ctxt->state.desrfb.nkey = nkey;
|
||||
|
||||
ctxt->blocksize = 8;
|
||||
ctxt->free = qcrypto_cipher_free_des_rfb;
|
||||
ctxt->setiv = qcrypto_cipher_setiv_des_rfb;
|
||||
ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb;
|
||||
@@ -373,12 +374,6 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
|
||||
{
|
||||
QCryptoCipherBuiltin *ctxt = cipher->opaque;
|
||||
|
||||
if (len % ctxt->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctxt->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ctxt->encrypt(cipher, in, out, len, errp);
|
||||
}
|
||||
|
||||
@@ -391,12 +386,6 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
|
||||
{
|
||||
QCryptoCipherBuiltin *ctxt = cipher->opaque;
|
||||
|
||||
if (len % ctxt->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctxt->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ctxt->decrypt(cipher, in, out, len, errp);
|
||||
}
|
||||
|
||||
|
||||
+16
-45
@@ -34,11 +34,6 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt;
|
||||
struct QCryptoCipherGcrypt {
|
||||
gcry_cipher_hd_t handle;
|
||||
size_t blocksize;
|
||||
};
|
||||
|
||||
QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode,
|
||||
@@ -46,7 +41,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipher *cipher;
|
||||
QCryptoCipherGcrypt *ctx;
|
||||
gcry_cipher_hd_t handle;
|
||||
gcry_error_t err;
|
||||
int gcryalg, gcrymode;
|
||||
|
||||
@@ -92,9 +87,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
cipher->alg = alg;
|
||||
cipher->mode = mode;
|
||||
|
||||
ctx = g_new0(QCryptoCipherGcrypt, 1);
|
||||
|
||||
err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0);
|
||||
err = gcry_cipher_open(&handle, gcryalg, gcrymode, 0);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gcry_strerror(err));
|
||||
@@ -107,12 +100,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
* bizarre RFB variant of DES :-)
|
||||
*/
|
||||
uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
|
||||
err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
|
||||
err = gcry_cipher_setkey(handle, rfbkey, nkey);
|
||||
g_free(rfbkey);
|
||||
ctx->blocksize = 8;
|
||||
} else {
|
||||
err = gcry_cipher_setkey(ctx->handle, key, nkey);
|
||||
ctx->blocksize = 16;
|
||||
err = gcry_cipher_setkey(handle, key, nkey);
|
||||
}
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set key: %s",
|
||||
@@ -120,12 +111,11 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
goto error;
|
||||
}
|
||||
|
||||
cipher->opaque = ctx;
|
||||
cipher->opaque = handle;
|
||||
return cipher;
|
||||
|
||||
error:
|
||||
gcry_cipher_close(ctx->handle);
|
||||
g_free(ctx);
|
||||
gcry_cipher_close(handle);
|
||||
g_free(cipher);
|
||||
return NULL;
|
||||
}
|
||||
@@ -133,13 +123,12 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
|
||||
void qcrypto_cipher_free(QCryptoCipher *cipher)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx;
|
||||
gcry_cipher_hd_t handle;
|
||||
if (!cipher) {
|
||||
return;
|
||||
}
|
||||
ctx = cipher->opaque;
|
||||
gcry_cipher_close(ctx->handle);
|
||||
g_free(ctx);
|
||||
handle = cipher->opaque;
|
||||
gcry_cipher_close(handle);
|
||||
g_free(cipher);
|
||||
}
|
||||
|
||||
@@ -150,16 +139,10 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = cipher->opaque;
|
||||
gcry_cipher_hd_t handle = cipher->opaque;
|
||||
gcry_error_t err;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = gcry_cipher_encrypt(ctx->handle,
|
||||
err = gcry_cipher_encrypt(handle,
|
||||
out, len,
|
||||
in, len);
|
||||
if (err != 0) {
|
||||
@@ -178,16 +161,10 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = cipher->opaque;
|
||||
gcry_cipher_hd_t handle = cipher->opaque;
|
||||
gcry_error_t err;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = gcry_cipher_decrypt(ctx->handle,
|
||||
err = gcry_cipher_decrypt(handle,
|
||||
out, len,
|
||||
in, len);
|
||||
if (err != 0) {
|
||||
@@ -203,17 +180,11 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
|
||||
const uint8_t *iv, size_t niv,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = cipher->opaque;
|
||||
gcry_cipher_hd_t handle = cipher->opaque;
|
||||
gcry_error_t err;
|
||||
|
||||
if (niv != ctx->blocksize) {
|
||||
error_setg(errp, "Expected IV size %zu not %zu",
|
||||
ctx->blocksize, niv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gcry_cipher_reset(ctx->handle);
|
||||
err = gcry_cipher_setiv(ctx->handle, iv, niv);
|
||||
gcry_cipher_reset(handle);
|
||||
err = gcry_cipher_setiv(handle, iv, niv);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set IV: %s",
|
||||
gcry_strerror(err));
|
||||
|
||||
+8
-20
@@ -69,7 +69,7 @@ struct QCryptoCipherNettle {
|
||||
nettle_cipher_func *alg_encrypt;
|
||||
nettle_cipher_func *alg_decrypt;
|
||||
uint8_t *iv;
|
||||
size_t blocksize;
|
||||
size_t niv;
|
||||
};
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
|
||||
@@ -125,7 +125,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
ctx->alg_encrypt = des_encrypt_wrapper;
|
||||
ctx->alg_decrypt = des_decrypt_wrapper;
|
||||
|
||||
ctx->blocksize = DES_BLOCK_SIZE;
|
||||
ctx->niv = DES_BLOCK_SIZE;
|
||||
break;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
@@ -140,14 +140,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
ctx->alg_encrypt = aes_encrypt_wrapper;
|
||||
ctx->alg_decrypt = aes_decrypt_wrapper;
|
||||
|
||||
ctx->blocksize = AES_BLOCK_SIZE;
|
||||
ctx->niv = AES_BLOCK_SIZE;
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Unsupported cipher algorithm %d", alg);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ctx->iv = g_new0(uint8_t, ctx->blocksize);
|
||||
ctx->iv = g_new0(uint8_t, ctx->niv);
|
||||
cipher->opaque = ctx;
|
||||
|
||||
return cipher;
|
||||
@@ -184,12 +184,6 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
|
||||
{
|
||||
QCryptoCipherNettle *ctx = cipher->opaque;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (cipher->mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in);
|
||||
@@ -197,7 +191,7 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
|
||||
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt,
|
||||
ctx->blocksize, ctx->iv,
|
||||
ctx->niv, ctx->iv,
|
||||
len, out, in);
|
||||
break;
|
||||
default:
|
||||
@@ -217,12 +211,6 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
|
||||
{
|
||||
QCryptoCipherNettle *ctx = cipher->opaque;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (cipher->mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
ctx->alg_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt,
|
||||
@@ -231,7 +219,7 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
|
||||
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
cbc_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt,
|
||||
ctx->alg_decrypt, ctx->blocksize, ctx->iv,
|
||||
ctx->alg_decrypt, ctx->niv, ctx->iv,
|
||||
len, out, in);
|
||||
break;
|
||||
default:
|
||||
@@ -247,9 +235,9 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherNettle *ctx = cipher->opaque;
|
||||
if (niv != ctx->blocksize) {
|
||||
if (niv != ctx->niv) {
|
||||
error_setg(errp, "Expected IV size %zu not %zu",
|
||||
ctx->blocksize, niv);
|
||||
ctx->niv, niv);
|
||||
return -1;
|
||||
}
|
||||
memcpy(ctx->iv, iv, niv);
|
||||
|
||||
+4
-4
@@ -47,7 +47,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_GCRYPT) || defined(CONFIG_NETTLE)
|
||||
#if defined(CONFIG_GNUTLS_GCRYPT) || defined(CONFIG_GNUTLS_NETTLE)
|
||||
static uint8_t *
|
||||
qcrypto_cipher_munge_des_rfb_key(const uint8_t *key,
|
||||
size_t nkey)
|
||||
@@ -63,11 +63,11 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_GCRYPT || CONFIG_NETTLE */
|
||||
#endif /* CONFIG_GNUTLS_GCRYPT || CONFIG_GNUTLS_NETTLE */
|
||||
|
||||
#ifdef CONFIG_GCRYPT
|
||||
#ifdef CONFIG_GNUTLS_GCRYPT
|
||||
#include "crypto/cipher-gcrypt.c"
|
||||
#elif defined CONFIG_NETTLE
|
||||
#elif defined CONFIG_GNUTLS_NETTLE
|
||||
#include "crypto/cipher-nettle.c"
|
||||
#else
|
||||
#include "crypto/cipher-builtin.c"
|
||||
|
||||
+14
-12
@@ -24,9 +24,8 @@
|
||||
#ifdef CONFIG_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GCRYPT
|
||||
#ifdef CONFIG_GNUTLS_GCRYPT
|
||||
#include <gcrypt.h>
|
||||
#endif
|
||||
|
||||
@@ -38,7 +37,6 @@
|
||||
* - When GNUTLS >= 2.12, we must not initialize gcrypt threading
|
||||
* because GNUTLS will do that itself
|
||||
* - When GNUTLS < 2.12 we must always initialize gcrypt threading
|
||||
* - When GNUTLS is disabled we must always initialize gcrypt threading
|
||||
*
|
||||
* But....
|
||||
*
|
||||
@@ -49,15 +47,12 @@
|
||||
*
|
||||
* - gcrypt < 1.6.0
|
||||
* AND
|
||||
* - gnutls < 2.12
|
||||
* OR
|
||||
* - gnutls is disabled
|
||||
* - gnutls < 2.12
|
||||
*
|
||||
*/
|
||||
|
||||
#if (defined(CONFIG_GCRYPT) && \
|
||||
(!defined(CONFIG_GNUTLS) || \
|
||||
!defined(GNUTLS_VERSION_NUMBER) || \
|
||||
#if (defined(CONFIG_GNUTLS_GCRYPT) && \
|
||||
(!defined(GNUTLS_VERSION_NUMBER) || \
|
||||
(GNUTLS_VERSION_NUMBER < 0x020c00)) && \
|
||||
(!defined(GCRYPT_VERSION_NUMBER) || \
|
||||
(GCRYPT_VERSION_NUMBER < 0x010600)))
|
||||
@@ -118,7 +113,6 @@ static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = {
|
||||
|
||||
int qcrypto_init(Error **errp)
|
||||
{
|
||||
#ifdef CONFIG_GNUTLS
|
||||
int ret;
|
||||
ret = gnutls_global_init();
|
||||
if (ret < 0) {
|
||||
@@ -131,9 +125,8 @@ int qcrypto_init(Error **errp)
|
||||
gnutls_global_set_log_level(10);
|
||||
gnutls_global_set_log_function(qcrypto_gnutls_log);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GCRYPT
|
||||
#ifdef CONFIG_GNUTLS_GCRYPT
|
||||
if (!gcry_check_version(GCRYPT_VERSION)) {
|
||||
error_setg(errp, "Unable to initialize gcrypt");
|
||||
return -1;
|
||||
@@ -146,3 +139,12 @@ int qcrypto_init(Error **errp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* ! CONFIG_GNUTLS */
|
||||
|
||||
int qcrypto_init(Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* ! CONFIG_GNUTLS */
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* QEMU crypto TLS credential support
|
||||
*
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "crypto/tlscredspriv.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define DH_BITS 2048
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
int
|
||||
qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
|
||||
const char *filename,
|
||||
gnutls_dh_params_t *dh_params,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : "<generated>");
|
||||
|
||||
if (filename == NULL) {
|
||||
ret = gnutls_dh_params_init(dh_params);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Unable to initialize DH parameters: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
ret = gnutls_dh_params_generate2(*dh_params, DH_BITS);
|
||||
if (ret < 0) {
|
||||
gnutls_dh_params_deinit(*dh_params);
|
||||
*dh_params = NULL;
|
||||
error_setg(errp, "Unable to generate DH parameters: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
GError *gerr = NULL;
|
||||
gchar *contents;
|
||||
gsize len;
|
||||
gnutls_datum_t data;
|
||||
if (!g_file_get_contents(filename,
|
||||
&contents,
|
||||
&len,
|
||||
&gerr)) {
|
||||
|
||||
error_setg(errp, "%s", gerr->message);
|
||||
g_error_free(gerr);
|
||||
return -1;
|
||||
}
|
||||
data.data = (unsigned char *)contents;
|
||||
data.size = len;
|
||||
ret = gnutls_dh_params_init(dh_params);
|
||||
if (ret < 0) {
|
||||
g_free(contents);
|
||||
error_setg(errp, "Unable to initialize DH parameters: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
ret = gnutls_dh_params_import_pkcs3(*dh_params,
|
||||
&data,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
g_free(contents);
|
||||
if (ret < 0) {
|
||||
gnutls_dh_params_deinit(*dh_params);
|
||||
*dh_params = NULL;
|
||||
error_setg(errp, "Unable to load DH parameters from %s: %s",
|
||||
filename, gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
|
||||
const char *filename,
|
||||
bool required,
|
||||
char **cred,
|
||||
Error **errp)
|
||||
{
|
||||
struct stat sb;
|
||||
int ret = -1;
|
||||
|
||||
if (!creds->dir) {
|
||||
if (required) {
|
||||
error_setg(errp, "Missing 'dir' property value");
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*cred = g_strdup_printf("%s/%s", creds->dir, filename);
|
||||
|
||||
if (stat(*cred, &sb) < 0) {
|
||||
if (errno == ENOENT && !required) {
|
||||
ret = 0;
|
||||
} else {
|
||||
error_setg_errno(errp, errno,
|
||||
"Unable to access credentials %s",
|
||||
*cred);
|
||||
}
|
||||
g_free(*cred);
|
||||
*cred = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
trace_qcrypto_tls_creds_get_path(creds, filename,
|
||||
*cred ? *cred : "<none>");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_prop_set_verify(Object *obj,
|
||||
bool value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
creds->verifyPeer = value;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_prop_get_verify(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
return creds->verifyPeer;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_prop_set_dir(Object *obj,
|
||||
const char *value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
creds->dir = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_tls_creds_prop_get_dir(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
return g_strdup(creds->dir);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_prop_set_endpoint(Object *obj,
|
||||
int value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
creds->endpoint = value;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_prop_get_endpoint(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
return creds->endpoint;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_init(Object *obj)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
creds->verifyPeer = true;
|
||||
|
||||
object_property_add_bool(obj, "verify-peer",
|
||||
qcrypto_tls_creds_prop_get_verify,
|
||||
qcrypto_tls_creds_prop_set_verify,
|
||||
NULL);
|
||||
object_property_add_str(obj, "dir",
|
||||
qcrypto_tls_creds_prop_get_dir,
|
||||
qcrypto_tls_creds_prop_set_dir,
|
||||
NULL);
|
||||
object_property_add_enum(obj, "endpoint",
|
||||
"QCryptoTLSCredsEndpoint",
|
||||
QCryptoTLSCredsEndpoint_lookup,
|
||||
qcrypto_tls_creds_prop_get_endpoint,
|
||||
qcrypto_tls_creds_prop_set_endpoint,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_finalize(Object *obj)
|
||||
{
|
||||
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
||||
|
||||
g_free(creds->dir);
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qcrypto_tls_creds_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_QCRYPTO_TLS_CREDS,
|
||||
.instance_size = sizeof(QCryptoTLSCreds),
|
||||
.instance_init = qcrypto_tls_creds_init,
|
||||
.instance_finalize = qcrypto_tls_creds_finalize,
|
||||
.class_size = sizeof(QCryptoTLSCredsClass),
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_register_types(void)
|
||||
{
|
||||
type_register_static(&qcrypto_tls_creds_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qcrypto_tls_creds_register_types);
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* QEMU crypto TLS anonymous credential support
|
||||
*
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "crypto/tlscredsanon.h"
|
||||
#include "crypto/tlscredspriv.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
|
||||
Error **errp)
|
||||
{
|
||||
char *dhparams = NULL;
|
||||
int ret;
|
||||
int rv = -1;
|
||||
|
||||
trace_qcrypto_tls_creds_anon_load(creds,
|
||||
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
|
||||
|
||||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_DH_PARAMS,
|
||||
false, &dhparams, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = gnutls_anon_allocate_server_credentials(&creds->data.server);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot allocate credentials: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
|
||||
&creds->parent_obj.dh_params,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
gnutls_anon_set_server_dh_params(creds->data.server,
|
||||
creds->parent_obj.dh_params);
|
||||
} else {
|
||||
ret = gnutls_anon_allocate_client_credentials(&creds->data.client);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot allocate credentials: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
cleanup:
|
||||
g_free(dhparams);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
|
||||
{
|
||||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
|
||||
if (creds->data.client) {
|
||||
gnutls_anon_free_client_credentials(creds->data.client);
|
||||
creds->data.client = NULL;
|
||||
}
|
||||
} else {
|
||||
if (creds->data.server) {
|
||||
gnutls_anon_free_server_credentials(creds->data.server);
|
||||
creds->data.server = NULL;
|
||||
}
|
||||
}
|
||||
if (creds->parent_obj.dh_params) {
|
||||
gnutls_dh_params_deinit(creds->parent_obj.dh_params);
|
||||
creds->parent_obj.dh_params = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED,
|
||||
Error **errp)
|
||||
{
|
||||
error_setg(errp, "TLS credentials support requires GNUTLS");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED)
|
||||
{
|
||||
/* nada */
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_prop_set_loaded(Object *obj,
|
||||
bool value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
|
||||
|
||||
if (value) {
|
||||
qcrypto_tls_creds_anon_load(creds, errp);
|
||||
} else {
|
||||
qcrypto_tls_creds_anon_unload(creds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_anon_prop_get_loaded(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
|
||||
|
||||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
return creds->data.server != NULL;
|
||||
} else {
|
||||
return creds->data.client != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_anon_prop_get_loaded(Object *obj G_GNUC_UNUSED,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(uc), true, "loaded", errp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_init(Object *obj)
|
||||
{
|
||||
object_property_add_bool(obj, "loaded",
|
||||
qcrypto_tls_creds_anon_prop_get_loaded,
|
||||
qcrypto_tls_creds_anon_prop_set_loaded,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_finalize(Object *obj)
|
||||
{
|
||||
QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
|
||||
|
||||
qcrypto_tls_creds_anon_unload(creds);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = qcrypto_tls_creds_anon_complete;
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qcrypto_tls_creds_anon_info = {
|
||||
.parent = TYPE_QCRYPTO_TLS_CREDS,
|
||||
.name = TYPE_QCRYPTO_TLS_CREDS_ANON,
|
||||
.instance_size = sizeof(QCryptoTLSCredsAnon),
|
||||
.instance_init = qcrypto_tls_creds_anon_init,
|
||||
.instance_finalize = qcrypto_tls_creds_anon_finalize,
|
||||
.class_size = sizeof(QCryptoTLSCredsAnonClass),
|
||||
.class_init = qcrypto_tls_creds_anon_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_anon_register_types(void)
|
||||
{
|
||||
type_register_static(&qcrypto_tls_creds_anon_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qcrypto_tls_creds_anon_register_types);
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* QEMU crypto TLS credential support private helpers
|
||||
*
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QCRYPTO_TLSCRED_PRIV_H__
|
||||
#define QCRYPTO_TLSCRED_PRIV_H__
|
||||
|
||||
#include "crypto/tlscreds.h"
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
|
||||
int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
|
||||
const char *filename,
|
||||
bool required,
|
||||
char **cred,
|
||||
Error **errp);
|
||||
|
||||
int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
|
||||
const char *filename,
|
||||
gnutls_dh_params_t *dh_params,
|
||||
Error **errp);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* QCRYPTO_TLSCRED_PRIV_H__ */
|
||||
|
||||
@@ -1,815 +0,0 @@
|
||||
/*
|
||||
* QEMU crypto TLS x509 credential support
|
||||
*
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "crypto/tlscredsx509.h"
|
||||
#include "crypto/tlscredspriv.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
|
||||
#include <gnutls/x509.h>
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
|
||||
const char *certFile,
|
||||
bool isServer,
|
||||
bool isCA,
|
||||
Error **errp)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
if (now == ((time_t)-1)) {
|
||||
error_setg_errno(errp, errno, "cannot get current time");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gnutls_x509_crt_get_expiration_time(cert) < now) {
|
||||
error_setg(errp,
|
||||
(isCA ?
|
||||
"The CA certificate %s has expired" :
|
||||
(isServer ?
|
||||
"The server certificate %s has expired" :
|
||||
"The client certificate %s has expired")),
|
||||
certFile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gnutls_x509_crt_get_activation_time(cert) > now) {
|
||||
error_setg(errp,
|
||||
(isCA ?
|
||||
"The CA certificate %s is not yet active" :
|
||||
(isServer ?
|
||||
"The server certificate %s is not yet active" :
|
||||
"The client certificate %s is not yet active")),
|
||||
certFile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if LIBGNUTLS_VERSION_NUMBER >= 2
|
||||
/*
|
||||
* The gnutls_x509_crt_get_basic_constraints function isn't
|
||||
* available in GNUTLS 1.0.x branches. This isn't critical
|
||||
* though, since gnutls_certificate_verify_peers2 will do
|
||||
* pretty much the same check at runtime, so we can just
|
||||
* disable this code
|
||||
*/
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
|
||||
gnutls_x509_crt_t cert,
|
||||
const char *certFile,
|
||||
bool isServer,
|
||||
bool isCA,
|
||||
Error **errp)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL);
|
||||
trace_qcrypto_tls_creds_x509_check_basic_constraints(
|
||||
creds, certFile, status);
|
||||
|
||||
if (status > 0) { /* It is a CA cert */
|
||||
if (!isCA) {
|
||||
error_setg(errp, isServer ?
|
||||
"The certificate %s basic constraints show a CA, "
|
||||
"but we need one for a server" :
|
||||
"The certificate %s basic constraints show a CA, "
|
||||
"but we need one for a client",
|
||||
certFile);
|
||||
return -1;
|
||||
}
|
||||
} else if (status == 0) { /* It is not a CA cert */
|
||||
if (isCA) {
|
||||
error_setg(errp,
|
||||
"The certificate %s basic constraints do not "
|
||||
"show a CA",
|
||||
certFile);
|
||||
return -1;
|
||||
}
|
||||
} else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
|
||||
/* Missing basicConstraints */
|
||||
if (isCA) {
|
||||
error_setg(errp,
|
||||
"The certificate %s is missing basic constraints "
|
||||
"for a CA",
|
||||
certFile);
|
||||
return -1;
|
||||
}
|
||||
} else { /* General error */
|
||||
error_setg(errp,
|
||||
"Unable to query certificate %s basic constraints: %s",
|
||||
certFile, gnutls_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds,
|
||||
gnutls_x509_crt_t cert,
|
||||
const char *certFile,
|
||||
bool isCA,
|
||||
Error **errp)
|
||||
{
|
||||
int status;
|
||||
unsigned int usage = 0;
|
||||
unsigned int critical = 0;
|
||||
|
||||
status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
|
||||
trace_qcrypto_tls_creds_x509_check_key_usage(
|
||||
creds, certFile, status, usage, critical);
|
||||
|
||||
if (status < 0) {
|
||||
if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
|
||||
usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN :
|
||||
GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT;
|
||||
} else {
|
||||
error_setg(errp,
|
||||
"Unable to query certificate %s key usage: %s",
|
||||
certFile, gnutls_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCA) {
|
||||
if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
|
||||
if (critical) {
|
||||
error_setg(errp,
|
||||
"Certificate %s usage does not permit "
|
||||
"certificate signing", certFile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
|
||||
if (critical) {
|
||||
error_setg(errp,
|
||||
"Certificate %s usage does not permit digital "
|
||||
"signature", certFile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) {
|
||||
if (critical) {
|
||||
error_setg(errp,
|
||||
"Certificate %s usage does not permit key "
|
||||
"encipherment", certFile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds,
|
||||
gnutls_x509_crt_t cert,
|
||||
const char *certFile,
|
||||
bool isServer,
|
||||
Error **errp)
|
||||
{
|
||||
int status;
|
||||
size_t i;
|
||||
unsigned int purposeCritical;
|
||||
unsigned int critical;
|
||||
char *buffer = NULL;
|
||||
size_t size;
|
||||
bool allowClient = false, allowServer = false;
|
||||
|
||||
critical = 0;
|
||||
for (i = 0; ; i++) {
|
||||
size = 0;
|
||||
status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer,
|
||||
&size, NULL);
|
||||
|
||||
if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
|
||||
|
||||
/* If there is no data at all, then we must allow
|
||||
client/server to pass */
|
||||
if (i == 0) {
|
||||
allowServer = allowClient = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) {
|
||||
error_setg(errp,
|
||||
"Unable to query certificate %s key purpose: %s",
|
||||
certFile, gnutls_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer = g_new0(char, size);
|
||||
|
||||
status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer,
|
||||
&size, &purposeCritical);
|
||||
|
||||
if (status < 0) {
|
||||
trace_qcrypto_tls_creds_x509_check_key_purpose(
|
||||
creds, certFile, status, "<none>", purposeCritical);
|
||||
g_free(buffer);
|
||||
error_setg(errp,
|
||||
"Unable to query certificate %s key purpose: %s",
|
||||
certFile, gnutls_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
trace_qcrypto_tls_creds_x509_check_key_purpose(
|
||||
creds, certFile, status, buffer, purposeCritical);
|
||||
if (purposeCritical) {
|
||||
critical = true;
|
||||
}
|
||||
|
||||
if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) {
|
||||
allowServer = true;
|
||||
} else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) {
|
||||
allowClient = true;
|
||||
} else if (g_str_equal(buffer, GNUTLS_KP_ANY)) {
|
||||
allowServer = allowClient = true;
|
||||
}
|
||||
|
||||
g_free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
if (!allowServer) {
|
||||
if (critical) {
|
||||
error_setg(errp,
|
||||
"Certificate %s purpose does not allow "
|
||||
"use with a TLS server", certFile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!allowClient) {
|
||||
if (critical) {
|
||||
error_setg(errp,
|
||||
"Certificate %s purpose does not allow use "
|
||||
"with a TLS client", certFile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
|
||||
gnutls_x509_crt_t cert,
|
||||
const char *certFile,
|
||||
bool isServer,
|
||||
bool isCA,
|
||||
Error **errp)
|
||||
{
|
||||
if (qcrypto_tls_creds_check_cert_times(cert, certFile,
|
||||
isServer, isCA,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if LIBGNUTLS_VERSION_NUMBER >= 2
|
||||
if (qcrypto_tls_creds_check_cert_basic_constraints(creds,
|
||||
cert, certFile,
|
||||
isServer, isCA,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (qcrypto_tls_creds_check_cert_key_usage(creds,
|
||||
cert, certFile,
|
||||
isCA, errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!isCA &&
|
||||
qcrypto_tls_creds_check_cert_key_purpose(creds,
|
||||
cert, certFile,
|
||||
isServer, errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert,
|
||||
const char *certFile,
|
||||
gnutls_x509_crt_t *cacerts,
|
||||
size_t ncacerts,
|
||||
const char *cacertFile,
|
||||
bool isServer,
|
||||
Error **errp)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
if (gnutls_x509_crt_list_verify(&cert, 1,
|
||||
cacerts, ncacerts,
|
||||
NULL, 0,
|
||||
0, &status) < 0) {
|
||||
error_setg(errp, isServer ?
|
||||
"Unable to verify server certificate %s against "
|
||||
"CA certificate %s" :
|
||||
"Unable to verify client certificate %s against "
|
||||
"CA certificate %s",
|
||||
certFile, cacertFile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
const char *reason = "Invalid certificate";
|
||||
|
||||
if (status & GNUTLS_CERT_INVALID) {
|
||||
reason = "The certificate is not trusted";
|
||||
}
|
||||
|
||||
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
|
||||
reason = "The certificate hasn't got a known issuer";
|
||||
}
|
||||
|
||||
if (status & GNUTLS_CERT_REVOKED) {
|
||||
reason = "The certificate has been revoked";
|
||||
}
|
||||
|
||||
#ifndef GNUTLS_1_0_COMPAT
|
||||
if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
|
||||
reason = "The certificate uses an insecure algorithm";
|
||||
}
|
||||
#endif
|
||||
|
||||
error_setg(errp,
|
||||
"Our own certificate %s failed validation against %s: %s",
|
||||
certFile, cacertFile, reason);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gnutls_x509_crt_t
|
||||
qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds,
|
||||
const char *certFile,
|
||||
bool isServer,
|
||||
Error **errp)
|
||||
{
|
||||
gnutls_datum_t data;
|
||||
gnutls_x509_crt_t cert = NULL;
|
||||
char *buf = NULL;
|
||||
gsize buflen;
|
||||
GError *gerr;
|
||||
int ret = -1;
|
||||
|
||||
trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile);
|
||||
|
||||
if (gnutls_x509_crt_init(&cert) < 0) {
|
||||
error_setg(errp, "Unable to initialize certificate");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) {
|
||||
error_setg(errp, "Cannot load CA cert list %s: %s",
|
||||
certFile, gerr->message);
|
||||
g_error_free(gerr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
data.data = (unsigned char *)buf;
|
||||
data.size = strlen(buf);
|
||||
|
||||
if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) {
|
||||
error_setg(errp, isServer ?
|
||||
"Unable to import server certificate %s" :
|
||||
"Unable to import client certificate %s",
|
||||
certFile);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
if (ret != 0) {
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
cert = NULL;
|
||||
}
|
||||
g_free(buf);
|
||||
return cert;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds,
|
||||
const char *certFile,
|
||||
gnutls_x509_crt_t *certs,
|
||||
unsigned int certMax,
|
||||
size_t *ncerts,
|
||||
Error **errp)
|
||||
{
|
||||
gnutls_datum_t data;
|
||||
char *buf = NULL;
|
||||
gsize buflen;
|
||||
int ret = -1;
|
||||
GError *gerr = NULL;
|
||||
|
||||
*ncerts = 0;
|
||||
trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile);
|
||||
|
||||
if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) {
|
||||
error_setg(errp, "Cannot load CA cert list %s: %s",
|
||||
certFile, gerr->message);
|
||||
g_error_free(gerr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
data.data = (unsigned char *)buf;
|
||||
data.size = strlen(buf);
|
||||
|
||||
if (gnutls_x509_crt_list_import(certs, &certMax, &data,
|
||||
GNUTLS_X509_FMT_PEM, 0) < 0) {
|
||||
error_setg(errp,
|
||||
"Unable to import CA certificate list %s",
|
||||
certFile);
|
||||
goto cleanup;
|
||||
}
|
||||
*ncerts = certMax;
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
g_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_CERTS 16
|
||||
static int
|
||||
qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
|
||||
bool isServer,
|
||||
const char *cacertFile,
|
||||
const char *certFile,
|
||||
Error **errp)
|
||||
{
|
||||
gnutls_x509_crt_t cert = NULL;
|
||||
gnutls_x509_crt_t cacerts[MAX_CERTS];
|
||||
size_t ncacerts = 0;
|
||||
size_t i;
|
||||
int ret = -1;
|
||||
|
||||
memset(cacerts, 0, sizeof(cacerts));
|
||||
if (certFile &&
|
||||
access(certFile, R_OK) == 0) {
|
||||
cert = qcrypto_tls_creds_load_cert(creds,
|
||||
certFile, isServer,
|
||||
errp);
|
||||
if (!cert) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (access(cacertFile, R_OK) == 0) {
|
||||
if (qcrypto_tls_creds_load_ca_cert_list(creds,
|
||||
cacertFile, cacerts,
|
||||
MAX_CERTS, &ncacerts,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (cert &&
|
||||
qcrypto_tls_creds_check_cert(creds,
|
||||
cert, certFile, isServer,
|
||||
false, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < ncacerts; i++) {
|
||||
if (qcrypto_tls_creds_check_cert(creds,
|
||||
cacerts[i], cacertFile,
|
||||
isServer, true, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (cert && ncacerts &&
|
||||
qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts,
|
||||
ncacerts, cacertFile,
|
||||
isServer, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
if (cert) {
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
}
|
||||
for (i = 0; i < ncacerts; i++) {
|
||||
gnutls_x509_crt_deinit(cacerts[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
||||
Error **errp)
|
||||
{
|
||||
char *cacert = NULL, *cacrl = NULL, *cert = NULL,
|
||||
*key = NULL, *dhparams = NULL;
|
||||
int ret;
|
||||
int rv = -1;
|
||||
|
||||
trace_qcrypto_tls_creds_x509_load(creds,
|
||||
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
|
||||
|
||||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_CA_CERT,
|
||||
true, &cacert, errp) < 0 ||
|
||||
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_CA_CRL,
|
||||
false, &cacrl, errp) < 0 ||
|
||||
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
|
||||
true, &cert, errp) < 0 ||
|
||||
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
|
||||
true, &key, errp) < 0 ||
|
||||
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_DH_PARAMS,
|
||||
false, &dhparams, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_CA_CERT,
|
||||
true, &cacert, errp) < 0 ||
|
||||
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
|
||||
false, &cert, errp) < 0 ||
|
||||
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
||||
QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
|
||||
false, &key, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (creds->sanityCheck &&
|
||||
qcrypto_tls_creds_x509_sanity_check(creds,
|
||||
creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
|
||||
cacert, cert, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = gnutls_certificate_allocate_credentials(&creds->data);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot allocate credentials: '%s'",
|
||||
gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = gnutls_certificate_set_x509_trust_file(creds->data,
|
||||
cacert,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot load CA certificate '%s': %s",
|
||||
cacert, gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (cert != NULL && key != NULL) {
|
||||
ret = gnutls_certificate_set_x509_key_file(creds->data,
|
||||
cert, key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
|
||||
cert, key, gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (cacrl != NULL) {
|
||||
ret = gnutls_certificate_set_x509_crl_file(creds->data,
|
||||
cacrl,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot load CRL '%s': %s",
|
||||
cacrl, gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
|
||||
&creds->parent_obj.dh_params,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
gnutls_certificate_set_dh_params(creds->data,
|
||||
creds->parent_obj.dh_params);
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
cleanup:
|
||||
g_free(cacert);
|
||||
g_free(cacrl);
|
||||
g_free(cert);
|
||||
g_free(key);
|
||||
g_free(dhparams);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
|
||||
{
|
||||
if (creds->data) {
|
||||
gnutls_certificate_free_credentials(creds->data);
|
||||
creds->data = NULL;
|
||||
}
|
||||
if (creds->parent_obj.dh_params) {
|
||||
gnutls_dh_params_deinit(creds->parent_obj.dh_params);
|
||||
creds->parent_obj.dh_params = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED,
|
||||
Error **errp)
|
||||
{
|
||||
error_setg(errp, "TLS credentials support requires GNUTLS");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED)
|
||||
{
|
||||
/* nada */
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_prop_set_loaded(Object *obj,
|
||||
bool value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
if (value) {
|
||||
qcrypto_tls_creds_x509_load(creds, errp);
|
||||
} else {
|
||||
qcrypto_tls_creds_x509_unload(creds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_x509_prop_get_loaded(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
return creds->data != NULL;
|
||||
}
|
||||
|
||||
|
||||
#else /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! CONFIG_GNUTLS */
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
|
||||
bool value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
creds->sanityCheck = value;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
return creds->sanityCheck;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(uc), true, "loaded", errp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_init(Object *obj)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
creds->sanityCheck = true;
|
||||
|
||||
object_property_add_bool(obj, "loaded",
|
||||
qcrypto_tls_creds_x509_prop_get_loaded,
|
||||
qcrypto_tls_creds_x509_prop_set_loaded,
|
||||
NULL);
|
||||
object_property_add_bool(obj, "sanity-check",
|
||||
qcrypto_tls_creds_x509_prop_get_sanity,
|
||||
qcrypto_tls_creds_x509_prop_set_sanity,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_finalize(Object *obj)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
qcrypto_tls_creds_x509_unload(creds);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = qcrypto_tls_creds_x509_complete;
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qcrypto_tls_creds_x509_info = {
|
||||
.parent = TYPE_QCRYPTO_TLS_CREDS,
|
||||
.name = TYPE_QCRYPTO_TLS_CREDS_X509,
|
||||
.instance_size = sizeof(QCryptoTLSCredsX509),
|
||||
.instance_init = qcrypto_tls_creds_x509_init,
|
||||
.instance_finalize = qcrypto_tls_creds_x509_finalize,
|
||||
.class_size = sizeof(QCryptoTLSCredsX509Class),
|
||||
.class_init = qcrypto_tls_creds_x509_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_register_types(void)
|
||||
{
|
||||
type_register_static(&qcrypto_tls_creds_x509_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qcrypto_tls_creds_x509_register_types);
|
||||
Loaded 100 of 1449 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user