Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ffb4001c2 | ||
|
|
86a8d63bc1 | ||
|
|
102dd9167c | ||
|
|
d0ed2d2e8e | ||
|
|
d194ba1cdb | ||
|
|
8a0a9cf35b | ||
|
|
d928541b51 | ||
|
|
7a6d80e93c | ||
|
|
447a3b3473 | ||
|
|
e295c31e25 | ||
|
|
adf6c527b0 | ||
|
|
57ee5f77c0 | ||
|
|
986626efec | ||
|
|
85a4ca797d | ||
|
|
e47c212cb5 | ||
|
|
8afe984ef7 | ||
|
|
5bb37d151b | ||
|
|
fe5c13ebf1 | ||
|
|
6061f16a8a | ||
|
|
fbcf305e5a | ||
|
|
37769d2727 | ||
|
|
23201c64a7 | ||
|
|
c936f649d4 | ||
|
|
f63d074313 | ||
|
|
9b81fbdbb0 | ||
|
|
7e2191ae98 | ||
|
|
a25808dc5b | ||
|
|
3e8088148b | ||
|
|
6d450bfbc8 | ||
|
|
abf80f8804 | ||
|
|
3d3ec7b809 | ||
|
|
45d6cdff48 | ||
|
|
ed6857bf98 | ||
|
|
64dd41bc2d | ||
|
|
c554919f74 | ||
|
|
77a0262181 | ||
|
|
f03969b952 | ||
|
|
2061800b85 | ||
|
|
0b23c5d40e |
No files matched your search
-13
@@ -39,16 +39,9 @@ qemu-img-cmds.texi
|
||||
qemu-img-cmds.h
|
||||
qemu-io
|
||||
qemu-ga
|
||||
qemu-bridge-helper
|
||||
qemu-monitor.texi
|
||||
QMP/qmp-commands.txt
|
||||
test-coroutine
|
||||
test-qmp-input-visitor
|
||||
test-qmp-output-visitor
|
||||
test-string-input-visitor
|
||||
test-string-output-visitor
|
||||
fsdev/virtfs-proxy-helper.1
|
||||
fsdev/virtfs-proxy-helper.pod
|
||||
.gdbinit
|
||||
*.a
|
||||
*.aux
|
||||
@@ -76,14 +69,8 @@ patches
|
||||
pc-bios/bios-pq/status
|
||||
pc-bios/vgabios-pq/status
|
||||
pc-bios/optionrom/linuxboot.bin
|
||||
pc-bios/optionrom/linuxboot.raw
|
||||
pc-bios/optionrom/linuxboot.img
|
||||
pc-bios/optionrom/multiboot.bin
|
||||
pc-bios/optionrom/multiboot.raw
|
||||
pc-bios/optionrom/multiboot.img
|
||||
pc-bios/optionrom/kvmvapic.bin
|
||||
pc-bios/optionrom/kvmvapic.raw
|
||||
pc-bios/optionrom/kvmvapic.img
|
||||
.stgit-*
|
||||
cscope.*
|
||||
tags
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# This mailmap just translates the weird addresses from the original import into git
|
||||
# into proper addresses so that they are counted properly in git shortlog output.
|
||||
#
|
||||
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <aliguori@us.ibm.com> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# There is also a:
|
||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
QEMU Coding Style
|
||||
Qemu Coding Style
|
||||
=================
|
||||
|
||||
Please use the script checkpatch.pl in the scripts directory to check
|
||||
@@ -44,8 +44,7 @@ Rationale:
|
||||
3. Naming
|
||||
|
||||
Variables are lower_case_with_underscores; easy to type and read. Structured
|
||||
type names are in CamelCase; harder to type but standing out. Enum type
|
||||
names and function type names should also be in CamelCase. Scalar type
|
||||
type names are in CamelCase; harder to type but standing out. Scalar type
|
||||
names are lower_case_with_underscores_ending_with_a_t, like the POSIX
|
||||
uint64_t and family. Note that this last convention contradicts POSIX
|
||||
and is therefore likely to be changed.
|
||||
|
||||
@@ -78,7 +78,7 @@ version 0.10.2:
|
||||
|
||||
- fix savevm/loadvm (Anthony Liguori)
|
||||
- live migration: fix dirty tracking windows (Glauber Costa)
|
||||
- live migration: improve error propagation (Glauber Costa)
|
||||
- live migration: improve error propogation (Glauber Costa)
|
||||
- qcow2: fix image creation for > ~2TB images (Chris Wright)
|
||||
- hotplug: fix error handling for if= parameter (Eduardo Habkost)
|
||||
- qcow2: fix data corruption (Nolan Leake)
|
||||
@@ -386,7 +386,7 @@ version 0.5.3:
|
||||
- support of CD-ROM change
|
||||
- multiple network interface support
|
||||
- initial x86-64 host support (Gwenole Beauchesne)
|
||||
- lret to outer privilege fix (OS/2 install fix)
|
||||
- lret to outer priviledge fix (OS/2 install fix)
|
||||
- task switch fixes (SkyOS boot)
|
||||
- VM save/restore commands
|
||||
- new timer API
|
||||
@@ -447,7 +447,7 @@ version 0.5.0:
|
||||
- multi-target build
|
||||
- fixed: no error code in hardware interrupts
|
||||
- fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
|
||||
- correct single stepping through string operations
|
||||
- correct single stepping thru string operations
|
||||
- preliminary SPARC target support (Thomas M. Ogrisegg)
|
||||
- tun-fd option (Rusty Russell)
|
||||
- automatic IDE geometry detection
|
||||
|
||||
@@ -77,13 +77,11 @@ avoided.
|
||||
|
||||
Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign
|
||||
APIs is not allowed in the QEMU codebase. Instead of these routines,
|
||||
use the GLib memory allocation routines g_malloc/g_malloc0/g_new/
|
||||
g_new0/g_realloc/g_free or QEMU's qemu_vmalloc/qemu_memalign/qemu_vfree
|
||||
APIs.
|
||||
use the replacement g_malloc/g_malloc0/g_realloc/g_free or
|
||||
qemu_vmalloc/qemu_memalign/qemu_vfree APIs.
|
||||
|
||||
Please note that g_malloc will exit on allocation failure, so there
|
||||
is no need to test for failure (as you would have to with malloc).
|
||||
Calling g_malloc with a zero size is valid and will return NULL.
|
||||
Please note that NULL check for the g_malloc result is redundant and
|
||||
that g_malloc() call with zero size is not allowed.
|
||||
|
||||
Memory allocated by qemu_vmalloc or qemu_memalign must be freed with
|
||||
qemu_vfree, since breaking this will cause problems on Win32 and user
|
||||
|
||||
@@ -6,7 +6,9 @@ The following points clarify the QEMU license:
|
||||
GNU General Public License. Hence each source file contains its own
|
||||
licensing information.
|
||||
|
||||
Many hardware device emulation sources are released under the BSD license.
|
||||
In particular, the QEMU virtual CPU core library (libqemu.a) is
|
||||
released under the GNU Lesser General Public License. Many hardware
|
||||
device emulation sources are released under the BSD license.
|
||||
|
||||
3) The Tiny Code Generator (TCG) is released under the BSD license
|
||||
(see license headers in files).
|
||||
|
||||
+17
-108
@@ -20,7 +20,7 @@ Descriptions of section entries:
|
||||
Supported: Someone is actually paid to look after this.
|
||||
Maintained: Someone actually looks after it.
|
||||
Odd Fixes: It has a maintainer but they don't have time to do
|
||||
much other than throw the odd patch in. See below.
|
||||
much other than throw the odd patch in. See below..
|
||||
Orphan: No current maintainer [but maybe you could take the
|
||||
role as you write your new code].
|
||||
Obsolete: Old code. Something tagged obsolete generally means
|
||||
@@ -78,7 +78,7 @@ F: target-lm32/
|
||||
|
||||
M68K
|
||||
M: Paul Brook <paul@codesourcery.com>
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: target-m68k/
|
||||
|
||||
MicroBlaze
|
||||
@@ -88,12 +88,11 @@ F: target-microblaze/
|
||||
|
||||
MIPS
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: target-mips/
|
||||
|
||||
PowerPC
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: target-ppc/
|
||||
|
||||
@@ -104,7 +103,7 @@ F: target-s390x/
|
||||
|
||||
SH4
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: target-sh4/
|
||||
|
||||
SPARC
|
||||
@@ -112,11 +111,6 @@ M: Blue Swirl <blauwirbel@gmail.com>
|
||||
S: Maintained
|
||||
F: target-sparc/
|
||||
|
||||
UniCore32
|
||||
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
|
||||
S: Maintained
|
||||
F: target-unicore32/
|
||||
|
||||
X86
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Odd Fixes
|
||||
@@ -166,42 +160,8 @@ S: Supported
|
||||
F: xen-*
|
||||
F: */xen*
|
||||
|
||||
Hosts:
|
||||
------
|
||||
|
||||
LINUX
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Maintained
|
||||
F: linux-*
|
||||
F: linux-headers/
|
||||
|
||||
POSIX
|
||||
L: qemu-devel@nongnu.org
|
||||
S: Maintained
|
||||
F: *posix*
|
||||
|
||||
W32, W64
|
||||
L: qemu-devel@nongnu.org
|
||||
M: Stefan Weil <sw@weilnetz.de>
|
||||
S: Maintained
|
||||
F: *win32*
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
Exynos
|
||||
M: Evgeny Voevodin <e.voevodin@samsung.com>
|
||||
M: Maksim Kozlov <m.kozlov@samsung.com>
|
||||
M: Igor Mitsyanko <i.mitsyanko@samsung.com>
|
||||
M: Dmitry Solodkiy <d.solodkiy@samsung.com>
|
||||
S: Maintained
|
||||
F: hw/exynos*
|
||||
|
||||
Calxeda Highbank
|
||||
M: Mark Langsdorf <mark.langsdorf@calxeda.com>
|
||||
S: Supported
|
||||
F: hw/highbank.c
|
||||
F: hw/xgmac.c
|
||||
|
||||
Gumstix
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
@@ -256,13 +216,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: hw/versatilepb.c
|
||||
|
||||
Xilinx Zynq
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
S: Maintained
|
||||
F: hw/xilinx_zynq.c
|
||||
F: hw/zynq_slcr.c
|
||||
F: hw/cadence_*
|
||||
|
||||
CRIS Machines
|
||||
-------------
|
||||
Axis Dev88
|
||||
@@ -337,31 +290,23 @@ PowerPC Machines
|
||||
----------------
|
||||
405
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc405_boards.c
|
||||
|
||||
New World
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc_newworld.c
|
||||
F: hw/unin_pci.c
|
||||
F: hw/dec_pci.[hc]
|
||||
|
||||
Old World
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc_oldworld.c
|
||||
F: hw/grackle_pci.c
|
||||
|
||||
PReP
|
||||
M: Andreas Färber <andreas.faerber@web.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc_prep.c
|
||||
F: hw/prep_pci.[hc]
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
@@ -399,8 +344,7 @@ X86 Machines
|
||||
PC
|
||||
M: Anthony Liguori <aliguori@us.ibm.com>
|
||||
S: Supported
|
||||
F: hw/pc.[ch]
|
||||
F: hw/pc_piix.c
|
||||
F: hw/pc.[ch] hw/pc_piix.c
|
||||
|
||||
Xtensa Machines
|
||||
---------------
|
||||
@@ -433,16 +377,11 @@ F: hw/pci*
|
||||
F: hw/piix*
|
||||
|
||||
SCSI
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Supported
|
||||
F: hw/virtio-scsi.*
|
||||
F: hw/scsi*
|
||||
T: git git://github.com/bonzini/qemu.git scsi-next
|
||||
|
||||
LSI53C895A
|
||||
M: Paul Brook <paul@codesourcery.com>
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: hw/lsi53c895a.c
|
||||
F: hw/scsi*
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
@@ -460,11 +399,9 @@ S: Supported
|
||||
F: hw/virtio*
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
M: Venkateswararao Jujjuri (JV) <jvrao@linux.vnet.ibm.com>
|
||||
S: Supported
|
||||
F: hw/9pfs/
|
||||
F: fsdev/
|
||||
T: git git://github.com/kvaneesh/QEMU.git
|
||||
F: hw/virtio-9p*
|
||||
|
||||
virtio-blk
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
@@ -536,28 +473,17 @@ M: Mark McLoughlin <markmc@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
|
||||
Network Block Device (NBD)
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: block/nbd.c
|
||||
F: nbd.*
|
||||
F: qemu-nbd.c
|
||||
T: git git://github.com/bonzini/qemu.git nbd-next
|
||||
|
||||
SLIRP
|
||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||
S: Maintained
|
||||
F: slirp/
|
||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||
T: git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Tracing
|
||||
M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
F: trace/
|
||||
F: scripts/tracetool.py
|
||||
F: scripts/tracetool/
|
||||
F: docs/tracing.txt
|
||||
T: git git://github.com/stefanha/qemu.git tracing
|
||||
T: git://repo.or.cz/qemu/stefanha.git tracing
|
||||
|
||||
Checkpatch
|
||||
M: Blue Swirl <blauwirbel@gmail.com>
|
||||
@@ -571,6 +497,11 @@ M: Blue Swirl <blauwirbel@gmail.com>
|
||||
S: Maintained
|
||||
F: bsd-user/
|
||||
|
||||
Darwin user
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
F: darwin-user/
|
||||
|
||||
Linux user
|
||||
M: Riku Voipio <riku.voipio@iki.fi>
|
||||
S: Maintained
|
||||
@@ -632,26 +563,4 @@ F: tcg/sparc/
|
||||
TCI target
|
||||
M: Stefan Weil <sw@weilnetz.de>
|
||||
S: Maintained
|
||||
F: tcg/tci/
|
||||
|
||||
Stable branches
|
||||
---------------
|
||||
Stable 1.0
|
||||
L: qemu-stable@nongnu.org
|
||||
T: git git://git.qemu.org/qemu-stable-1.0.git
|
||||
S: Orphan
|
||||
|
||||
Stable 0.15
|
||||
L: qemu-stable@nongnu.org
|
||||
T: git git://git.qemu.org/qemu-stable-0.15.git
|
||||
S: Orphan
|
||||
|
||||
Stable 0.14
|
||||
L: qemu-stable@nongnu.org
|
||||
T: git git://git.qemu.org/qemu-stable-0.14.git
|
||||
S: Orphan
|
||||
|
||||
Stable 0.10
|
||||
L: qemu-stable@nongnu.org
|
||||
T: git git://git.qemu.org/qemu-stable-0.10.git
|
||||
S: Orphan
|
||||
F: tcg/tci
|
||||
@@ -3,7 +3,13 @@
|
||||
# Always point to the root of the build tree (needs GNU make).
|
||||
BUILD_DIR=$(CURDIR)
|
||||
|
||||
# All following code might depend on configuration variables
|
||||
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
GENERATED_HEADERS += trace-dtrace.h
|
||||
endif
|
||||
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
# Put the all: rule here so that config-host.mak can contain dependencies.
|
||||
all: build-all
|
||||
@@ -18,13 +24,6 @@ config-host.mak:
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
GENERATED_HEADERS += trace-dtrace.h
|
||||
endif
|
||||
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c trace.c
|
||||
|
||||
# Don't try to regenerate Makefile or configure
|
||||
# We don't generate any of them
|
||||
Makefile: ;
|
||||
@@ -37,13 +36,8 @@ $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
|
||||
|
||||
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 QMP/qmp-commands.txt
|
||||
ifdef CONFIG_VIRTFS
|
||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||
endif
|
||||
else
|
||||
DOCS=
|
||||
endif
|
||||
@@ -82,7 +76,7 @@ defconfig:
|
||||
|
||||
-include config-all-devices.mak
|
||||
|
||||
build-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
|
||||
build-all: $(DOCS) $(TOOLS) $(CHECKS) recurse-all
|
||||
|
||||
config-host.h: config-host.h-timestamp
|
||||
config-host.h-timestamp: config-host.mak
|
||||
@@ -98,12 +92,12 @@ ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
endif
|
||||
|
||||
$(universal-obj-y) $(common-obj-y): $(GENERATED_HEADERS)
|
||||
$(common-obj-y): $(GENERATED_HEADERS)
|
||||
subdir-libcacard: $(oslib-obj-y) $(trace-obj-y) qemu-timer-common.o
|
||||
|
||||
$(filter %-softmmu,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(common-obj-y) subdir-libdis
|
||||
$(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis
|
||||
|
||||
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(universal-obj-y) $(trace-obj-y) subdir-libdis-user subdir-libuser
|
||||
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser
|
||||
|
||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
romsubdir-%:
|
||||
@@ -117,7 +111,7 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
|
||||
|
||||
QEMU_CFLAGS+=$(CURL_CFLAGS)
|
||||
|
||||
QEMU_CFLAGS += -I$(SRC_PATH)/include
|
||||
QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
||||
|
||||
ui/cocoa.o: ui/cocoa.m
|
||||
|
||||
@@ -153,60 +147,73 @@ endif
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
|
||||
|
||||
tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
|
||||
qemu-timer-common.o main-loop.o notify.o iohandler.o cutils.o async.o
|
||||
tools-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
tools-obj-y = qemu-tool.o $(oslib-obj-y) $(trace-obj-y) \
|
||||
qemu-timer-common.o cutils.o
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
|
||||
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||
qemu-bridge-helper.o: $(GENERATED_HEADERS)
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o oslib-posix.o $(trace-obj-y)
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
|
||||
|
||||
check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
|
||||
|
||||
check-qint: check-qint.o qint.o $(tools-obj-y)
|
||||
check-qstring: check-qstring.o qstring.o $(tools-obj-y)
|
||||
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
|
||||
check-qlist: check-qlist.o qlist.o qint.o $(tools-obj-y)
|
||||
check-qfloat: check-qfloat.o qfloat.o $(tools-obj-y)
|
||||
check-qjson: check-qjson.o $(qobject-obj-y) $(tools-obj-y)
|
||||
test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(tools-obj-y)
|
||||
|
||||
$(qapi-obj-y): $(GENERATED_HEADERS)
|
||||
qapi-dir := $(BUILD_DIR)/qapi-generated
|
||||
test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
|
||||
qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
|
||||
qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
|
||||
|
||||
gen-out-type = $(subst .,-,$(suffix $@))
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/tests/Makefile
|
||||
endif
|
||||
$(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\
|
||||
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
|
||||
$(qapi-dir)/test-qapi-visit.c $(qapi-dir)/test-qapi-visit.h :\
|
||||
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
|
||||
$(qapi-dir)/test-qmp-commands.h $(qapi-dir)/test-qmp-marshal.c :\
|
||||
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
|
||||
|
||||
$(qapi-dir)/qga-qapi-types.c $(qapi-dir)/qga-qapi-types.h :\
|
||||
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(qapi-dir)/qga-qapi-visit.c $(qapi-dir)/qga-qapi-visit.h :\
|
||||
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(qapi-dir)/qga-qmp-commands.h $(qapi-dir)/qga-qmp-marshal.c :\
|
||||
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." < $<, " GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "." < $<, " GEN $@")
|
||||
qapi-visit.c qapi-visit.h :\
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." < $<, " GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "." < $<, " GEN $@")
|
||||
qmp-commands.h qmp-marshal.c :\
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, " GEN $@")
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -m -o "." < $<, " GEN $@")
|
||||
|
||||
test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
|
||||
test-visitor: test-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
|
||||
|
||||
test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
|
||||
test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
|
||||
|
||||
QGALIB_OBJ=$(addprefix $(qapi-dir)/, qga-qapi-types.o qga-qapi-visit.o qga-qmp-marshal.o)
|
||||
QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
|
||||
$(QGALIB_OBJ): $(QGALIB_GEN) $(GENERATED_HEADERS)
|
||||
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) $(GENERATED_HEADERS)
|
||||
|
||||
qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(tools-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) $(QGALIB_OBJ)
|
||||
qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qobject-obj-y) $(version-obj-y) $(QGALIB_OBJ)
|
||||
|
||||
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
|
||||
|
||||
@@ -214,19 +221,17 @@ clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
rm -f *.o *.d *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -f *.o *.d *.a *.lo $(TOOLS) $(CHECKS) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -Rf .libs
|
||||
rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
|
||||
rm -f qom/*.o qom/*.d
|
||||
rm -f qemu-img-cmds.h
|
||||
rm -f trace/*.o trace/*.d
|
||||
rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
|
||||
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
|
||||
@# May not be present in GENERATED_HEADERS
|
||||
rm -f trace-dtrace.h trace-dtrace.h-timestamp
|
||||
rm -f $(foreach f,$(GENERATED_HEADERS),$(f) $(f)-timestamp)
|
||||
rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp)
|
||||
rm -f $(GENERATED_SOURCES)
|
||||
rm -rf $(qapi-dir)
|
||||
$(MAKE) -C tests/tcg clean
|
||||
$(MAKE) -C tests clean
|
||||
for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
|
||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||
rm -f $$d/qemu-options.def; \
|
||||
@@ -241,7 +246,6 @@ distclean: clean
|
||||
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
|
||||
rm -f qemu-doc.vr
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
|
||||
for d in $(TARGET_DIRS) $(QEMULIBS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
@@ -257,10 +261,9 @@ vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
qemu-icon.bmp \
|
||||
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
||||
mpc8544ds.dtb \
|
||||
multiboot.bin linuxboot.bin kvmvapic.bin \
|
||||
multiboot.bin linuxboot.bin \
|
||||
s390-zipl.rom \
|
||||
spapr-rtas.bin slof.bin \
|
||||
palcode-clipper
|
||||
@@ -269,47 +272,33 @@ BLOBS=
|
||||
endif
|
||||
|
||||
install-doc: $(DOCS)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) QMP/qmp-commands.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
|
||||
ifdef CONFIG_POSIX
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
endif
|
||||
ifdef CONFIG_VIRTFS
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
|
||||
install-datadir:
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)"
|
||||
install-sysconfig:
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu"
|
||||
$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu"
|
||||
|
||||
install-confdir:
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_confdir)"
|
||||
|
||||
install-sysconfig: install-datadir install-confdir
|
||||
$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)"
|
||||
$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/cpus-x86_64.conf "$(DESTDIR)$(qemu_datadir)"
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig install-datadir
|
||||
install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(bindir)"
|
||||
ifneq ($(TOOLS),)
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
endif
|
||||
ifneq ($(HELPERS-y),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
|
||||
endif
|
||||
ifneq ($(BLOBS),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)"
|
||||
set -e; for x in $(BLOBS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
|
||||
done
|
||||
endif
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps"
|
||||
set -e; for x in $(KEYMAPS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
|
||||
done
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
@@ -317,7 +306,13 @@ endif
|
||||
|
||||
# various test targets
|
||||
test speed: all
|
||||
$(MAKE) -C tests/tcg $@
|
||||
$(MAKE) -C tests $@
|
||||
|
||||
.PHONY: check
|
||||
check: $(patsubst %,run-check-%,$(CHECKS))
|
||||
|
||||
run-check-%: %
|
||||
./$<
|
||||
|
||||
.PHONY: TAGS
|
||||
TAGS:
|
||||
@@ -336,7 +331,7 @@ TEXIFLAG=$(if $(V),,--quiet)
|
||||
$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<," GEN $@")
|
||||
|
||||
%.html: %.texi
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
|
||||
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
|
||||
" GEN $@")
|
||||
|
||||
%.info: %.texi
|
||||
@@ -360,25 +355,19 @@ qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
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 > $@, \
|
||||
pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \
|
||||
" GEN $@")
|
||||
|
||||
qemu-img.1: qemu-img.texi qemu-img-cmds.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \
|
||||
$(POD2MAN) --section=1 --center=" " --release=" " qemu-img.pod > $@, \
|
||||
" GEN $@")
|
||||
|
||||
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< fsdev/virtfs-proxy-helper.pod && \
|
||||
$(POD2MAN) --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \
|
||||
pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \
|
||||
" GEN $@")
|
||||
|
||||
qemu-nbd.8: qemu-nbd.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \
|
||||
$(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
|
||||
pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
|
||||
" GEN $@")
|
||||
|
||||
dvi: qemu-doc.dvi qemu-tech.dvi
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ include $(SRC_PATH)/rules.mak
|
||||
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
|
||||
|
||||
QEMU_CFLAGS+=-I..
|
||||
QEMU_CFLAGS += -I$(SRC_PATH)/include
|
||||
QEMU_CFLAGS += $(GLIB_CFLAGS)
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
|
||||
+37
-83
@@ -1,23 +1,9 @@
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
universal-obj-y =
|
||||
|
||||
#######################################################################
|
||||
# QObject
|
||||
qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
|
||||
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
|
||||
qobject-obj-y += qerror.o error.o qemu-error.o
|
||||
|
||||
universal-obj-y += $(qobject-obj-y)
|
||||
|
||||
#######################################################################
|
||||
# QOM
|
||||
include $(SRC_PATH)/qom/Makefile
|
||||
qom-obj-y = $(addprefix qom/, $(qom-y))
|
||||
qom-obj-twice-y = $(addprefix qom/, $(qom-twice-y))
|
||||
|
||||
universal-obj-y += $(qom-obj-y)
|
||||
|
||||
#######################################################################
|
||||
# oslib-obj-y is code depending on the OS (win32 vs posix)
|
||||
oslib-obj-y = osdep.o
|
||||
@@ -26,17 +12,12 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
|
||||
|
||||
#######################################################################
|
||||
# coroutines
|
||||
coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
||||
coroutine-obj-y += qemu-coroutine-sleep.o
|
||||
coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o
|
||||
ifeq ($(CONFIG_UCONTEXT_COROUTINE),y)
|
||||
coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o
|
||||
else
|
||||
ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y)
|
||||
coroutine-obj-$(CONFIG_POSIX) += coroutine-sigaltstack.o
|
||||
else
|
||||
coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o
|
||||
endif
|
||||
endif
|
||||
coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
|
||||
|
||||
#######################################################################
|
||||
@@ -53,7 +34,6 @@ block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow
|
||||
block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-nested-y += qed-check.o
|
||||
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
|
||||
block-nested-y += stream.o
|
||||
block-nested-$(CONFIG_WIN32) += raw-win32.o
|
||||
block-nested-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-nested-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
@@ -81,20 +61,21 @@ ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
||||
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
||||
# only pull in the actual virtio-9p device if we also enabled virtio.
|
||||
CONFIG_REALLY_VIRTFS=y
|
||||
fsdev-nested-y = qemu-fsdev.o virtio-9p-marshal.o
|
||||
fsdev-nested-y = qemu-fsdev.o
|
||||
else
|
||||
fsdev-nested-y = qemu-fsdev-dummy.o
|
||||
endif
|
||||
fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
# suppress *all* target specific code in case of system emulation, i.e. a
|
||||
# single QEMU executable should support all CPUs and machines.
|
||||
# libqemu_common.a: Target independent part of system emulation. The
|
||||
# long term path is to suppress *all* target specific code in case of
|
||||
# system emulation, i.e. a single QEMU executable should support all
|
||||
# CPUs and machines.
|
||||
|
||||
common-obj-y = $(block-obj-y) blockdev.o
|
||||
common-obj-y += $(net-obj-y)
|
||||
common-obj-y += $(qom-obj-twice-y)
|
||||
common-obj-y += $(qobject-obj-y)
|
||||
common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
|
||||
common-obj-y += readline.o console.o cursor.o
|
||||
common-obj-y += $(oslib-obj-y)
|
||||
@@ -121,19 +102,17 @@ common-obj-y += eeprom93xx.o
|
||||
common-obj-y += scsi-disk.o cdrom.o
|
||||
common-obj-y += scsi-generic.o scsi-bus.o
|
||||
common-obj-y += hid.o
|
||||
common-obj-y += usb/core.o usb/bus.o usb/desc.o usb/dev-hub.o
|
||||
common-obj-y += usb/host-$(HOST_USB).o
|
||||
common-obj-y += usb/dev-hid.o usb/dev-storage.o usb/dev-wacom.o
|
||||
common-obj-y += usb/dev-serial.o usb/dev-network.o usb/dev-audio.o
|
||||
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
|
||||
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
|
||||
common-obj-$(CONFIG_SSI) += ssi.o
|
||||
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
||||
common-obj-$(CONFIG_SD) += sd.o
|
||||
common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
|
||||
common-obj-y += bt-hci-csr.o usb/dev-bluetooth.o
|
||||
common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
|
||||
common-obj-y += bt-hci-csr.o
|
||||
common-obj-y += buffered_file.o migration.o migration-tcp.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += qemu-char.o savevm.o #aio.o
|
||||
common-obj-y += msmouse.o ps2.o
|
||||
common-obj-y += qdev.o qdev-properties.o qdev-monitor.o
|
||||
common-obj-y += qdev.o qdev-properties.o
|
||||
common-obj-y += block-migration.o iohandler.o
|
||||
common-obj-y += pflib.o
|
||||
common-obj-y += bitmap.o bitops.o
|
||||
@@ -199,10 +178,7 @@ user-obj-y =
|
||||
user-obj-y += envlist.o path.o
|
||||
user-obj-y += tcg-runtime.o host-utils.o
|
||||
user-obj-y += cutils.o cache-utils.o
|
||||
user-obj-y += module.o
|
||||
user-obj-y += qemu-user.o
|
||||
user-obj-y += $(trace-obj-y)
|
||||
user-obj-y += $(qom-obj-twice-y)
|
||||
|
||||
######################################################################
|
||||
# libhw
|
||||
@@ -210,13 +186,11 @@ user-obj-y += $(qom-obj-twice-y)
|
||||
hw-obj-y =
|
||||
hw-obj-y += vl.o loader.o
|
||||
hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
|
||||
hw-obj-y += usb/libhw.o
|
||||
hw-obj-y += usb-libhw.o
|
||||
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
hw-obj-y += fw_cfg.o
|
||||
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o
|
||||
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
|
||||
hw-obj-$(CONFIG_PCI) += msix.o msi.o
|
||||
hw-obj-$(CONFIG_PCI) += shpc.o
|
||||
hw-obj-$(CONFIG_PCI) += slotid_cap.o
|
||||
hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
|
||||
hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
|
||||
hw-obj-y += watchdog.o
|
||||
@@ -232,28 +206,25 @@ hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
|
||||
|
||||
hw-obj-$(CONFIG_SERIAL) += serial.o
|
||||
hw-obj-$(CONFIG_PARALLEL) += parallel.o
|
||||
hw-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
|
||||
hw-obj-$(CONFIG_I8254) += i8254.o
|
||||
hw-obj-$(CONFIG_PCSPK) += pcspk.o
|
||||
hw-obj-$(CONFIG_PCKBD) += pckbd.o
|
||||
hw-obj-$(CONFIG_USB_UHCI) += usb/hcd-uhci.o
|
||||
hw-obj-$(CONFIG_USB_OHCI) += usb/hcd-ohci.o
|
||||
hw-obj-$(CONFIG_USB_EHCI) += usb/hcd-ehci.o
|
||||
hw-obj-$(CONFIG_USB_XHCI) += usb/hcd-xhci.o
|
||||
hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
|
||||
hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
|
||||
hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
|
||||
hw-obj-$(CONFIG_FDC) += fdc.o
|
||||
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
|
||||
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
|
||||
hw-obj-$(CONFIG_DMA) += dma.o
|
||||
hw-obj-$(CONFIG_I82374) += i82374.o
|
||||
hw-obj-$(CONFIG_HPET) += hpet.o
|
||||
hw-obj-$(CONFIG_APPLESMC) += applesmc.o
|
||||
hw-obj-$(CONFIG_SMARTCARD) += usb/dev-smartcard-reader.o ccid-card-passthru.o
|
||||
hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
|
||||
hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
|
||||
hw-obj-$(CONFIG_USB_REDIR) += usb/redirect.o
|
||||
hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
|
||||
hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
|
||||
hw-obj-$(CONFIG_I8259) += i8259.o
|
||||
|
||||
# PPC devices
|
||||
hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
|
||||
hw-obj-$(CONFIG_I82378) += i82378.o
|
||||
# Mac shared devices
|
||||
hw-obj-$(CONFIG_MACIO) += macio.o
|
||||
hw-obj-$(CONFIG_CUDA) += cuda.o
|
||||
@@ -272,7 +243,6 @@ hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
|
||||
# MIPS devices
|
||||
hw-obj-$(CONFIG_PIIX4) += piix4.o
|
||||
hw-obj-$(CONFIG_G364FB) += g364fb.o
|
||||
hw-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
|
||||
|
||||
# PCI watchdog devices
|
||||
hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
|
||||
@@ -317,15 +287,12 @@ hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o
|
||||
hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
|
||||
hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
|
||||
hw-obj-$(CONFIG_VMMOUSE) += vmmouse.o
|
||||
hw-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
|
||||
|
||||
hw-obj-$(CONFIG_RC4030) += rc4030.o
|
||||
hw-obj-$(CONFIG_DP8393X) += dp8393x.o
|
||||
hw-obj-$(CONFIG_DS1225Y) += ds1225y.o
|
||||
hw-obj-$(CONFIG_MIPSNET) += mipsnet.o
|
||||
|
||||
hw-obj-y += qtest.o
|
||||
|
||||
# Sound
|
||||
sound-obj-y =
|
||||
sound-obj-$(CONFIG_SB16) += sb16.o
|
||||
@@ -345,9 +312,10 @@ hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
|
||||
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-coth.o cofs.o codir.o cofile.o
|
||||
9pfs-nested-$(CONFIG_VIRTFS) += coxattr.o virtio-9p-synth.o
|
||||
9pfs-nested-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o
|
||||
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-proxy.o
|
||||
|
||||
hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
|
||||
$(addprefix 9pfs/, $(9pfs-nested-y)): QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
||||
|
||||
|
||||
######################################################################
|
||||
# libdis
|
||||
@@ -367,28 +335,22 @@ libdis-$(CONFIG_PPC_DIS) += ppc-dis.o
|
||||
libdis-$(CONFIG_S390_DIS) += s390-dis.o
|
||||
libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
|
||||
libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
|
||||
libdis-$(CONFIG_LM32_DIS) += lm32-dis.o
|
||||
|
||||
######################################################################
|
||||
# trace
|
||||
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
TRACE_H_EXTRA_DEPS=trace-dtrace.h
|
||||
trace.h: trace.h-timestamp trace-dtrace.h
|
||||
else
|
||||
trace.h: trace.h-timestamp
|
||||
endif
|
||||
trace.h: trace.h-timestamp $(TRACE_H_EXTRA_DEPS)
|
||||
trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=h \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
< $< > $@," GEN trace.h")
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h")
|
||||
@cmp -s $@ trace.h || cp $@ trace.h
|
||||
|
||||
trace.c: trace.c-timestamp
|
||||
trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=c \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
< $< > $@," GEN trace.c")
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c")
|
||||
@cmp -s $@ trace.c || cp $@ trace.c
|
||||
|
||||
trace.o: trace.c $(GENERATED_HEADERS)
|
||||
@@ -401,14 +363,11 @@ trace-dtrace.h: trace-dtrace.dtrace
|
||||
# rule file. So we use '.dtrace' instead
|
||||
trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp
|
||||
trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=d \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
< $< > $@," GEN trace-dtrace.dtrace")
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace")
|
||||
@cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace
|
||||
|
||||
trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
|
||||
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
|
||||
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
|
||||
|
||||
ifeq ($(LIBTOOL),)
|
||||
trace-dtrace.lo: trace-dtrace.dtrace
|
||||
@@ -446,26 +405,21 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
qapi-nested-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
|
||||
qapi-nested-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
|
||||
qapi-nested-y += string-input-visitor.o string-output-visitor.o
|
||||
qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o
|
||||
qapi-nested-y += qmp-registry.o qmp-dispatch.o
|
||||
qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y))
|
||||
|
||||
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o
|
||||
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o $(qapi-obj-y)
|
||||
common-obj-y += qmp.o hmp.o
|
||||
|
||||
universal-obj-y += $(qapi-obj-y)
|
||||
|
||||
######################################################################
|
||||
# guest agent
|
||||
|
||||
qga-nested-y = commands.o guest-agent-command-state.o
|
||||
qga-nested-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
|
||||
qga-nested-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
|
||||
qga-nested-y = guest-agent-commands.o guest-agent-command-state.o
|
||||
qga-obj-y = $(addprefix qga/, $(qga-nested-y))
|
||||
qga-obj-y += qemu-ga.o module.o
|
||||
qga-obj-y += qemu-ga.o qemu-sockets.o module.o qemu-option.o
|
||||
qga-obj-$(CONFIG_WIN32) += oslib-win32.o
|
||||
qga-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-sockets.o qemu-option.o
|
||||
qga-obj-$(CONFIG_POSIX) += oslib-posix.o
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
|
||||
+71
-77
@@ -22,24 +22,15 @@ QEMU_CFLAGS += -I.. -I$(TARGET_PATH) -DNEED_CPU_H
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/include
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
# user emulator name
|
||||
QEMU_PROG=qemu-$(TARGET_ARCH2)
|
||||
else
|
||||
# system emulator name
|
||||
ifneq (,$(findstring -mwindows,$(LIBS)))
|
||||
# Terminate program name with a 'w' because the linker builds a windows executable.
|
||||
QEMU_PROGW=qemu-system-$(TARGET_ARCH2)w$(EXESUF)
|
||||
endif # windows executable
|
||||
QEMU_PROG=qemu-system-$(TARGET_ARCH2)$(EXESUF)
|
||||
endif
|
||||
|
||||
PROGS=$(QEMU_PROG)
|
||||
ifdef QEMU_PROGW
|
||||
PROGS+=$(QEMU_PROGW)
|
||||
endif
|
||||
STPFILES=
|
||||
|
||||
ifndef CONFIG_HAIKU
|
||||
@@ -58,14 +49,13 @@ else
|
||||
TARGET_TYPE=system
|
||||
endif
|
||||
|
||||
$(QEMU_PROG).stp: $(SRC_PATH)/trace-events
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=stap \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
--binary=$(bindir)/$(QEMU_PROG) \
|
||||
--target-arch=$(TARGET_ARCH) \
|
||||
--target-type=$(TARGET_TYPE) \
|
||||
< $< > $@," GEN $(QEMU_PROG).stp")
|
||||
$(QEMU_PROG).stp:
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \
|
||||
--$(TRACE_BACKEND) \
|
||||
--binary $(bindir)/$(QEMU_PROG) \
|
||||
--target-arch $(TARGET_ARCH) \
|
||||
--target-type $(TARGET_TYPE) \
|
||||
--stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp")
|
||||
else
|
||||
stap:
|
||||
endif
|
||||
@@ -81,24 +71,19 @@ libobj-y = exec.o translate-all.o cpu-exec.o translate.o
|
||||
libobj-y += tcg/tcg.o tcg/optimize.o
|
||||
libobj-$(CONFIG_TCG_INTERPRETER) += tci.o
|
||||
libobj-y += fpu/softfloat.o
|
||||
ifneq ($(TARGET_BASE_ARCH), sparc)
|
||||
ifneq ($(TARGET_BASE_ARCH), alpha)
|
||||
libobj-y += op_helper.o
|
||||
endif
|
||||
endif
|
||||
libobj-y += helper.o
|
||||
ifneq ($(TARGET_BASE_ARCH), ppc)
|
||||
libobj-y += cpu.o
|
||||
libobj-y += op_helper.o helper.o
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
libobj-y += cpuid.o
|
||||
endif
|
||||
libobj-$(TARGET_SPARC64) += vis_helper.o
|
||||
libobj-$(CONFIG_NEED_MMU) += mmu.o
|
||||
libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
libobj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o
|
||||
libobj-y += cpu_init.o
|
||||
endif
|
||||
libobj-$(TARGET_SPARC) += int32_helper.o
|
||||
libobj-$(TARGET_SPARC64) += int64_helper.o
|
||||
libobj-$(TARGET_ALPHA) += int_helper.o fpu_helper.o sys_helper.o mem_helper.o
|
||||
|
||||
libobj-y += disas.o
|
||||
libobj-$(CONFIG_TCI_DIS) += tci-dis.o
|
||||
@@ -107,12 +92,17 @@ tci-dis.o: QEMU_CFLAGS += -I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/tci
|
||||
|
||||
$(libobj-y): $(GENERATED_HEADERS)
|
||||
|
||||
# HELPER_CFLAGS is used for all the legacy code compiled with static register
|
||||
# libqemu
|
||||
|
||||
translate.o: translate.c cpu.h
|
||||
|
||||
translate-all.o: translate-all.c cpu.h
|
||||
|
||||
tcg/tcg.o: cpu.h
|
||||
|
||||
# HELPER_CFLAGS is used for all the code compiled with static register
|
||||
# variables
|
||||
ifneq ($(TARGET_BASE_ARCH), sparc)
|
||||
op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
|
||||
endif
|
||||
user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
|
||||
op_helper.o ldst_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
|
||||
|
||||
# Note: this is a workaround. The real fix is to avoid compiling
|
||||
# cpu_signal_handler() in user-exec.c.
|
||||
@@ -145,13 +135,39 @@ obj-m68k-y += m68k-sim.o m68k-semi.o
|
||||
|
||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
|
||||
|
||||
obj-y += $(addprefix ../, $(universal-obj-y))
|
||||
obj-y += $(addprefix ../libuser/, $(user-obj-y))
|
||||
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
|
||||
obj-y += $(libobj-y)
|
||||
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
#########################################################
|
||||
# Darwin user emulator target
|
||||
|
||||
ifdef CONFIG_DARWIN_USER
|
||||
|
||||
$(call set-vpath, $(SRC_PATH)/darwin-user)
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
|
||||
|
||||
# Leave some space for the regular program loading zone
|
||||
LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
|
||||
|
||||
LIBS+=-lmx
|
||||
|
||||
obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \
|
||||
gdbstub.o user-exec.o
|
||||
|
||||
obj-i386-y += ioport-user.o
|
||||
|
||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
|
||||
|
||||
obj-y += $(addprefix ../libuser/, $(user-obj-y))
|
||||
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
|
||||
obj-y += $(libobj-y)
|
||||
|
||||
endif #CONFIG_DARWIN_USER
|
||||
|
||||
#########################################################
|
||||
# BSD user emulator target
|
||||
|
||||
@@ -168,7 +184,6 @@ obj-i386-y += ioport-user.o
|
||||
|
||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
|
||||
|
||||
obj-y += $(addprefix ../, $(universal-obj-y))
|
||||
obj-y += $(addprefix ../libuser/, $(user-obj-y))
|
||||
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
|
||||
obj-y += $(libobj-y)
|
||||
@@ -184,28 +199,25 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
|
||||
# need to fix this properly
|
||||
obj-$(CONFIG_NO_PCI) += pci-stub.o
|
||||
obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
|
||||
obj-$(CONFIG_VIRTIO) += virtio-scsi.o
|
||||
obj-y += vhost_net.o
|
||||
obj-$(CONFIG_VHOST_NET) += vhost.o
|
||||
obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
|
||||
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
|
||||
obj-$(CONFIG_NO_KVM) += kvm-stub.o
|
||||
obj-$(CONFIG_VGA) += vga.o
|
||||
obj-y += memory.o savevm.o cputlb.o
|
||||
obj-y += memory.o
|
||||
LIBS+=-lz
|
||||
|
||||
obj-i386-$(CONFIG_KVM) += hyperv.o
|
||||
|
||||
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
|
||||
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
|
||||
QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
|
||||
QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
|
||||
QEMU_CFLAGS += $(GLIB_CFLAGS)
|
||||
|
||||
# xen support
|
||||
obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o
|
||||
obj-$(CONFIG_NO_XEN) += xen-stub.o
|
||||
|
||||
obj-i386-$(CONFIG_XEN) += xen_platform.o xen_apic.o
|
||||
obj-i386-$(CONFIG_XEN) += xen_platform.o
|
||||
|
||||
# Inter-VM PCI shared memory
|
||||
CONFIG_IVSHMEM =
|
||||
@@ -216,23 +228,20 @@ ifeq ($(CONFIG_KVM), y)
|
||||
endif
|
||||
obj-$(CONFIG_IVSHMEM) += ivshmem.o
|
||||
|
||||
# Generic hotplugging
|
||||
obj-y += device-hotplug.o
|
||||
|
||||
# Hardware support
|
||||
obj-i386-y += vga.o
|
||||
obj-i386-y += mc146818rtc.o pc.o
|
||||
obj-i386-y += apic_common.o apic.o kvmvapic.o
|
||||
obj-i386-y += sga.o ioapic_common.o ioapic.o piix_pci.o
|
||||
obj-i386-y += cirrus_vga.o sga.o apic.o ioapic.o piix_pci.o
|
||||
obj-i386-y += vmport.o
|
||||
obj-i386-y += pci-hotplug.o smbios.o wdt_ib700.o
|
||||
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
|
||||
obj-i386-y += debugcon.o multiboot.o
|
||||
obj-i386-y += pc_piix.o
|
||||
obj-i386-y += pc_sysfw.o
|
||||
obj-i386-$(CONFIG_KVM) += kvm/clock.o kvm/apic.o kvm/i8259.o kvm/ioapic.o kvm/i8254.o
|
||||
obj-i386-$(CONFIG_KVM) += kvmclock.o
|
||||
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
|
||||
|
||||
# shared objects
|
||||
obj-ppc-y = ppc.o ppc_booke.o
|
||||
obj-ppc-y += vga.o
|
||||
# PREP target
|
||||
obj-ppc-y += mc146818rtc.o
|
||||
obj-ppc-y += ppc_prep.o
|
||||
@@ -246,7 +255,7 @@ obj-ppc-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
|
||||
obj-ppc-$(CONFIG_PSERIES) += spapr_pci.o device-hotplug.o pci-hotplug.o
|
||||
# PowerPC 4xx boards
|
||||
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
|
||||
obj-ppc-y += ppc440_bamboo.o
|
||||
obj-ppc-y += ppc440.o ppc440_bamboo.o
|
||||
# PowerPC E500 boards
|
||||
obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o
|
||||
# PowerPC 440 Xilinx ML507 reference board.
|
||||
@@ -286,12 +295,14 @@ obj-lm32-y += framebuffer.o
|
||||
|
||||
obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
|
||||
obj-mips-y += mips_addr.o mips_timer.o mips_int.o
|
||||
obj-mips-y += vga.o
|
||||
obj-mips-y += jazz_led.o
|
||||
obj-mips-y += gt64xxx.o mc146818rtc.o
|
||||
obj-mips-y += cirrus_vga.o
|
||||
obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
|
||||
|
||||
obj-microblaze-y = petalogix_s3adsp1800_mmu.o
|
||||
obj-microblaze-y += petalogix_ml605_mmu.o
|
||||
obj-microblaze-y += microblaze_boot.o
|
||||
|
||||
obj-microblaze-y += microblaze_pic_cpu.o
|
||||
obj-microblaze-y += xilinx_intc.o
|
||||
@@ -317,7 +328,9 @@ obj-cris-y += etraxfs_ser.o
|
||||
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
obj-sparc-y = sun4u.o apb_pci.o
|
||||
obj-sparc-y += vga.o
|
||||
obj-sparc-y += mc146818rtc.o
|
||||
obj-sparc-y += cirrus_vga.o
|
||||
else
|
||||
obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
|
||||
obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
|
||||
@@ -330,22 +343,9 @@ endif
|
||||
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
|
||||
obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
|
||||
obj-arm-y += versatile_pci.o
|
||||
obj-arm-y += versatile_i2c.o
|
||||
obj-arm-y += cadence_uart.o
|
||||
obj-arm-y += cadence_ttc.o
|
||||
obj-arm-y += cadence_gem.o
|
||||
obj-arm-y += xilinx_zynq.o zynq_slcr.o
|
||||
obj-arm-y += arm_gic.o
|
||||
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
|
||||
obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
|
||||
obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
|
||||
obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
|
||||
obj-arm-y += arm_l2x0.o
|
||||
obj-arm-y += arm_mptimer.o a15mpcore.o
|
||||
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
|
||||
obj-arm-y += highbank.o
|
||||
obj-arm-y += pl061.o
|
||||
obj-arm-y += xgmac.o
|
||||
obj-arm-y += arm-semi.o
|
||||
obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
|
||||
obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
|
||||
@@ -356,16 +356,18 @@ obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
|
||||
obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
|
||||
omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
|
||||
obj-arm-y += omap_sx1.o palm.o tsc210x.o
|
||||
obj-arm-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o
|
||||
obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
|
||||
obj-arm-y += mst_fpga.o mainstone.o
|
||||
obj-arm-y += z2.o
|
||||
obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
|
||||
obj-arm-y += framebuffer.o
|
||||
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
|
||||
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
|
||||
obj-arm-y += syborg_virtio.o
|
||||
obj-arm-y += vexpress.o
|
||||
obj-arm-y += strongarm.o
|
||||
obj-arm-y += collie.o
|
||||
obj-arm-y += pl041.o lm4549.o
|
||||
obj-arm-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
|
||||
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
|
||||
@@ -377,6 +379,7 @@ obj-m68k-y += m68k-semi.o dummy_m68k.o
|
||||
obj-s390x-y = s390-virtio-bus.o s390-virtio.o
|
||||
|
||||
obj-alpha-y = mc146818rtc.o
|
||||
obj-alpha-y += vga.o cirrus_vga.o
|
||||
obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
|
||||
|
||||
obj-xtensa-y += xtensa_pic.o
|
||||
@@ -384,7 +387,6 @@ obj-xtensa-y += xtensa_sim.o
|
||||
obj-xtensa-y += xtensa_lx60.o
|
||||
obj-xtensa-y += xtensa-semi.o
|
||||
obj-xtensa-y += core-dc232b.o
|
||||
obj-xtensa-y += core-dc233c.o
|
||||
obj-xtensa-y += core-fsf.o
|
||||
|
||||
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
@@ -393,7 +395,6 @@ monitor.o: hmp-commands.h qmp-commands-old.h
|
||||
|
||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
|
||||
|
||||
obj-y += $(addprefix ../, $(universal-obj-y))
|
||||
obj-y += $(addprefix ../, $(common-obj-y))
|
||||
obj-y += $(addprefix ../libdis/, $(libdis-y))
|
||||
obj-y += $(libobj-y)
|
||||
@@ -412,16 +413,9 @@ endif # CONFIG_LINUX_USER
|
||||
|
||||
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
|
||||
|
||||
ifdef QEMU_PROGW
|
||||
# The linker builds a windows executable. Make also a console executable.
|
||||
$(QEMU_PROGW): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
|
||||
$(call LINK,$^)
|
||||
$(QEMU_PROG): $(QEMU_PROGW)
|
||||
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
|
||||
else
|
||||
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
|
||||
$(call LINK,$^)
|
||||
endif
|
||||
|
||||
|
||||
gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh
|
||||
$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
|
||||
@@ -434,7 +428,7 @@ qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
|
||||
rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o kvm/*.o
|
||||
rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o
|
||||
rm -f hmp-commands.h qmp-commands-old.h gdbstub-xml.c
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
rm -f *.stp
|
||||
@@ -448,8 +442,8 @@ ifneq ($(STRIP),)
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
||||
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset"
|
||||
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset"
|
||||
endif
|
||||
|
||||
# Include automatically generated dependency files
|
||||
|
||||
@@ -9,7 +9,6 @@ include $(SRC_PATH)/rules.mak
|
||||
$(call set-vpath, $(SRC_PATH))
|
||||
|
||||
QEMU_CFLAGS+=-I..
|
||||
QEMU_CFLAGS += -I$(SRC_PATH)/include
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# QMP command line tool
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPLv2 or later.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
import sys, os
|
||||
from qmp import QEMUMonitorProtocol
|
||||
|
||||
def print_response(rsp, prefix=[]):
|
||||
if type(rsp) == list:
|
||||
i = 0
|
||||
for item in rsp:
|
||||
if prefix == []:
|
||||
prefix = ['item']
|
||||
print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
|
||||
i += 1
|
||||
elif type(rsp) == dict:
|
||||
for key in rsp.keys():
|
||||
print_response(rsp[key], prefix + [key])
|
||||
else:
|
||||
if len(prefix):
|
||||
print '%s: %s' % ('.'.join(prefix), rsp)
|
||||
else:
|
||||
print '%s' % (rsp)
|
||||
|
||||
def main(args):
|
||||
path = None
|
||||
|
||||
# Use QMP_PATH if it's set
|
||||
if os.environ.has_key('QMP_PATH'):
|
||||
path = os.environ['QMP_PATH']
|
||||
|
||||
while len(args):
|
||||
arg = args[0]
|
||||
|
||||
if arg.startswith('--'):
|
||||
arg = arg[2:]
|
||||
if arg.find('=') == -1:
|
||||
value = True
|
||||
else:
|
||||
arg, value = arg.split('=', 1)
|
||||
|
||||
if arg in ['path']:
|
||||
if type(value) == str:
|
||||
path = value
|
||||
elif arg in ['help']:
|
||||
os.execlp('man', 'man', 'qmp')
|
||||
else:
|
||||
print 'Unknown argument "%s"' % arg
|
||||
|
||||
args = args[1:]
|
||||
else:
|
||||
break
|
||||
|
||||
if not path:
|
||||
print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
|
||||
return 1
|
||||
|
||||
if len(args):
|
||||
command, args = args[0], args[1:]
|
||||
else:
|
||||
print 'No command found'
|
||||
print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
|
||||
return 1
|
||||
|
||||
if command in ['help']:
|
||||
os.execlp('man', 'man', 'qmp')
|
||||
|
||||
srv = QEMUMonitorProtocol(path)
|
||||
srv.connect()
|
||||
|
||||
def do_command(srv, cmd, **kwds):
|
||||
rsp = srv.cmd(cmd, kwds)
|
||||
if rsp.has_key('error'):
|
||||
raise Exception(rsp['error']['desc'])
|
||||
return rsp['return']
|
||||
|
||||
commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
|
||||
|
||||
srv.close()
|
||||
|
||||
if command not in commands:
|
||||
fullcmd = 'qmp-%s' % command
|
||||
try:
|
||||
os.environ['QMP_PATH'] = path
|
||||
os.execvp(fullcmd, [fullcmd] + args)
|
||||
except OSError, (errno, msg):
|
||||
if errno == 2:
|
||||
print 'Command "%s" not found.' % (fullcmd)
|
||||
return 1
|
||||
raise
|
||||
return 0
|
||||
|
||||
srv = QEMUMonitorProtocol(path)
|
||||
srv.connect()
|
||||
|
||||
arguments = {}
|
||||
for arg in args:
|
||||
if not arg.startswith('--'):
|
||||
print 'Unknown argument "%s"' % arg
|
||||
return 1
|
||||
|
||||
arg = arg[2:]
|
||||
if arg.find('=') == -1:
|
||||
value = True
|
||||
else:
|
||||
arg, value = arg.split('=', 1)
|
||||
|
||||
if arg in ['help']:
|
||||
os.execlp('man', 'man', 'qmp-%s' % command)
|
||||
return 1
|
||||
|
||||
arguments[arg] = value
|
||||
|
||||
rsp = do_command(srv, command, **arguments)
|
||||
print_response(rsp)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
+1
-72
@@ -26,24 +26,6 @@ Example:
|
||||
Note: If action is "stop", a STOP event will eventually follow the
|
||||
BLOCK_IO_ERROR event.
|
||||
|
||||
DEVICE_TRAY_MOVED
|
||||
-----------------
|
||||
|
||||
It's emitted whenever the tray of a removable device is moved by the guest
|
||||
or by HMP/QMP commands.
|
||||
|
||||
Data:
|
||||
|
||||
- "device": device name (json-string)
|
||||
- "tray-open": true if the tray has been opened or false if it has been closed
|
||||
(json-bool)
|
||||
|
||||
{ "event": "DEVICE_TRAY_MOVED",
|
||||
"data": { "device": "ide1-cd0",
|
||||
"tray-open": true
|
||||
},
|
||||
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
|
||||
|
||||
RESET
|
||||
-----
|
||||
|
||||
@@ -144,7 +126,7 @@ the authentication ID is not provided.
|
||||
VNC_DISCONNECTED
|
||||
----------------
|
||||
|
||||
Emitted when the connection is closed.
|
||||
Emitted when the conection is closed.
|
||||
|
||||
Data:
|
||||
|
||||
@@ -282,56 +264,3 @@ Example:
|
||||
|
||||
Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
|
||||
followed respectively by the RESET, SHUTDOWN, or STOP events.
|
||||
|
||||
|
||||
BLOCK_JOB_COMPLETED
|
||||
-------------------
|
||||
|
||||
Emitted when a block job has completed.
|
||||
|
||||
Data:
|
||||
|
||||
- "type": Job type ("stream" for image streaming, json-string)
|
||||
- "device": Device name (json-string)
|
||||
- "len": Maximum progress value (json-int)
|
||||
- "offset": Current progress value (json-int)
|
||||
On success this is equal to len.
|
||||
On failure this is less than len.
|
||||
- "speed": Rate limit, bytes per second (json-int)
|
||||
- "error": Error message (json-string, optional)
|
||||
Only present on failure. This field contains a human-readable
|
||||
error message. There are no semantics other than that streaming
|
||||
has failed and clients should not try to interpret the error
|
||||
string.
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "BLOCK_JOB_COMPLETED",
|
||||
"data": { "type": "stream", "device": "virtio-disk0",
|
||||
"len": 10737418240, "offset": 10737418240,
|
||||
"speed": 0 },
|
||||
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
|
||||
|
||||
|
||||
BLOCK_JOB_CANCELLED
|
||||
-------------------
|
||||
|
||||
Emitted when a block job has been cancelled.
|
||||
|
||||
Data:
|
||||
|
||||
- "type": Job type ("stream" for image streaming, json-string)
|
||||
- "device": Device name (json-string)
|
||||
- "len": Maximum progress value (json-int)
|
||||
- "offset": Current progress value (json-int)
|
||||
On success this is equal to len.
|
||||
On failure this is less than len.
|
||||
- "speed": Rate limit, bytes per second (json-int)
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "BLOCK_JOB_CANCELLED",
|
||||
"data": { "type": "stream", "device": "virtio-disk0",
|
||||
"len": 10737418240, "offset": 134217728,
|
||||
"speed": 0 },
|
||||
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
|
||||
+2
-16
@@ -209,27 +209,13 @@ incompatible way are disabled by default and will be advertised by the
|
||||
capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
|
||||
that array and enable the capabilities they support.
|
||||
|
||||
The QMP Server performs a type check on the arguments to a command. It
|
||||
generates an error if a value does not have the expected type for its
|
||||
key, or if it does not understand a key that the Client included. The
|
||||
strictness of the Server catches wrong assumptions of Clients about
|
||||
the Server's schema. Clients can assume that, when such validation
|
||||
errors occur, they will be reported before the command generated any
|
||||
side effect.
|
||||
Additionally, Clients must not assume any particular:
|
||||
|
||||
However, Clients must not assume any particular:
|
||||
|
||||
- Length of json-arrays
|
||||
- Size of json-objects; in particular, future versions of QEMU may add
|
||||
new keys and Clients should be able to ignore them.
|
||||
- Size of json-objects or length of json-arrays
|
||||
- Order of json-object members or json-array elements
|
||||
- Amount of errors generated by a command, that is, new errors can be added
|
||||
to any existing command in newer versions of the Server
|
||||
|
||||
Of course, the Server does guarantee to send valid JSON. But apart from
|
||||
this, a Client should be "conservative in what they send, and liberal in
|
||||
what they accept".
|
||||
|
||||
6. Downstream extension of QMP
|
||||
------------------------------
|
||||
|
||||
|
||||
@@ -128,12 +128,6 @@ class QEMUMonitorProtocol:
|
||||
qmp_cmd['id'] = id
|
||||
return self.cmd_obj(qmp_cmd)
|
||||
|
||||
def command(self, cmd, **kwds):
|
||||
ret = self.cmd(cmd, kwds)
|
||||
if ret.has_key('error'):
|
||||
raise Exception(ret['error']['desc'])
|
||||
return ret['return']
|
||||
|
||||
def get_events(self, wait=False):
|
||||
"""
|
||||
Get a list of available QMP events.
|
||||
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
##
|
||||
# QEMU Object Model test tools
|
||||
#
|
||||
# Copyright IBM, Corp. 2012
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or later. See
|
||||
# the COPYING file in the top-level directory.
|
||||
##
|
||||
|
||||
import fuse, stat
|
||||
from fuse import Fuse
|
||||
import os, posix
|
||||
from errno import *
|
||||
from qmp import QEMUMonitorProtocol
|
||||
|
||||
fuse.fuse_python_api = (0, 2)
|
||||
|
||||
class QOMFS(Fuse):
|
||||
def __init__(self, qmp, *args, **kwds):
|
||||
Fuse.__init__(self, *args, **kwds)
|
||||
self.qmp = qmp
|
||||
self.qmp.connect()
|
||||
self.ino_map = {}
|
||||
self.ino_count = 1
|
||||
|
||||
def get_ino(self, path):
|
||||
if self.ino_map.has_key(path):
|
||||
return self.ino_map[path]
|
||||
self.ino_map[path] = self.ino_count
|
||||
self.ino_count += 1
|
||||
return self.ino_map[path]
|
||||
|
||||
def is_object(self, path):
|
||||
try:
|
||||
items = self.qmp.command('qom-list', path=path)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_property(self, path):
|
||||
try:
|
||||
path, prop = path.rsplit('/', 1)
|
||||
for item in self.qmp.command('qom-list', path=path):
|
||||
if item['name'] == prop:
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_link(self, path):
|
||||
try:
|
||||
path, prop = path.rsplit('/', 1)
|
||||
for item in self.qmp.command('qom-list', path=path):
|
||||
if item['name'] == prop:
|
||||
if item['type'].startswith('link<'):
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def read(self, path, length, offset):
|
||||
if not self.is_property(path):
|
||||
return -ENOENT
|
||||
|
||||
path, prop = path.rsplit('/', 1)
|
||||
try:
|
||||
data = str(self.qmp.command('qom-get', path=path, property=prop))
|
||||
data += '\n' # make values shell friendly
|
||||
except:
|
||||
return -EPERM
|
||||
|
||||
if offset > len(data):
|
||||
return ''
|
||||
|
||||
return str(data[offset:][:length])
|
||||
|
||||
def readlink(self, path):
|
||||
if not self.is_link(path):
|
||||
return False
|
||||
path, prop = path.rsplit('/', 1)
|
||||
prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
|
||||
return prefix + str(self.qmp.command('qom-get', path=path,
|
||||
property=prop))
|
||||
|
||||
def getattr(self, path):
|
||||
if self.is_link(path):
|
||||
value = posix.stat_result((0755 | stat.S_IFLNK,
|
||||
self.get_ino(path),
|
||||
0,
|
||||
2,
|
||||
1000,
|
||||
1000,
|
||||
4096,
|
||||
0,
|
||||
0,
|
||||
0))
|
||||
elif self.is_object(path):
|
||||
value = posix.stat_result((0755 | stat.S_IFDIR,
|
||||
self.get_ino(path),
|
||||
0,
|
||||
2,
|
||||
1000,
|
||||
1000,
|
||||
4096,
|
||||
0,
|
||||
0,
|
||||
0))
|
||||
elif self.is_property(path):
|
||||
value = posix.stat_result((0644 | stat.S_IFREG,
|
||||
self.get_ino(path),
|
||||
0,
|
||||
1,
|
||||
1000,
|
||||
1000,
|
||||
4096,
|
||||
0,
|
||||
0,
|
||||
0))
|
||||
else:
|
||||
value = -ENOENT
|
||||
return value
|
||||
|
||||
def readdir(self, path, offset):
|
||||
yield fuse.Direntry('.')
|
||||
yield fuse.Direntry('..')
|
||||
for item in self.qmp.command('qom-list', path=path):
|
||||
yield fuse.Direntry(str(item['name']))
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys, os
|
||||
|
||||
fs = QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET']))
|
||||
fs.main(sys.argv)
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
##
|
||||
# QEMU Object Model test tools
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or later. See
|
||||
# the COPYING file in the top-level directory.
|
||||
##
|
||||
|
||||
import sys
|
||||
import os
|
||||
from qmp import QEMUMonitorProtocol
|
||||
|
||||
cmd, args = sys.argv[0], sys.argv[1:]
|
||||
socket_path = None
|
||||
path = None
|
||||
prop = None
|
||||
|
||||
def usage():
|
||||
return '''environment variables:
|
||||
QMP_SOCKET=<path | addr:port>
|
||||
usage:
|
||||
%s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
|
||||
''' % cmd
|
||||
|
||||
def usage_error(error_msg = "unspecified error"):
|
||||
sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
|
||||
exit(1)
|
||||
|
||||
if len(args) > 0:
|
||||
if args[0] == "-h":
|
||||
print usage()
|
||||
exit(0);
|
||||
elif args[0] == "-s":
|
||||
try:
|
||||
socket_path = args[1]
|
||||
except:
|
||||
usage_error("missing argument: QMP socket path or address");
|
||||
args = args[2:]
|
||||
|
||||
if not socket_path:
|
||||
if os.environ.has_key('QMP_SOCKET'):
|
||||
socket_path = os.environ['QMP_SOCKET']
|
||||
else:
|
||||
usage_error("no QMP socket path or address given");
|
||||
|
||||
if len(args) > 0:
|
||||
try:
|
||||
path, prop = args[0].rsplit('.', 1)
|
||||
except:
|
||||
usage_error("invalid format for path/property/value")
|
||||
else:
|
||||
usage_error("not enough arguments")
|
||||
|
||||
srv = QEMUMonitorProtocol(socket_path)
|
||||
srv.connect()
|
||||
|
||||
rsp = srv.command('qom-get', path=path, property=prop)
|
||||
if type(rsp) == dict:
|
||||
for i in rsp.keys():
|
||||
print '%s: %s' % (i, rsp[i])
|
||||
else:
|
||||
print rsp
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
##
|
||||
# QEMU Object Model test tools
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or later. See
|
||||
# the COPYING file in the top-level directory.
|
||||
##
|
||||
|
||||
import sys
|
||||
import os
|
||||
from qmp import QEMUMonitorProtocol
|
||||
|
||||
cmd, args = sys.argv[0], sys.argv[1:]
|
||||
socket_path = None
|
||||
path = None
|
||||
prop = None
|
||||
|
||||
def usage():
|
||||
return '''environment variables:
|
||||
QMP_SOCKET=<path | addr:port>
|
||||
usage:
|
||||
%s [-h] [-s <QMP socket path | addr:port>] [<path>]
|
||||
''' % cmd
|
||||
|
||||
def usage_error(error_msg = "unspecified error"):
|
||||
sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
|
||||
exit(1)
|
||||
|
||||
if len(args) > 0:
|
||||
if args[0] == "-h":
|
||||
print usage()
|
||||
exit(0);
|
||||
elif args[0] == "-s":
|
||||
try:
|
||||
socket_path = args[1]
|
||||
except:
|
||||
usage_error("missing argument: QMP socket path or address");
|
||||
args = args[2:]
|
||||
|
||||
if not socket_path:
|
||||
if os.environ.has_key('QMP_SOCKET'):
|
||||
socket_path = os.environ['QMP_SOCKET']
|
||||
else:
|
||||
usage_error("no QMP socket path or address given");
|
||||
|
||||
srv = QEMUMonitorProtocol(socket_path)
|
||||
srv.connect()
|
||||
|
||||
if len(args) == 0:
|
||||
print '/'
|
||||
sys.exit(0)
|
||||
|
||||
for item in srv.command('qom-list', path=args[0]):
|
||||
if item['type'].startswith('child<'):
|
||||
print '%s/' % item['name']
|
||||
elif item['type'].startswith('link<'):
|
||||
print '@%s/' % item['name']
|
||||
else:
|
||||
print '%s' % item['name']
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
##
|
||||
# QEMU Object Model test tools
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or later. See
|
||||
# the COPYING file in the top-level directory.
|
||||
##
|
||||
|
||||
import sys
|
||||
import os
|
||||
from qmp import QEMUMonitorProtocol
|
||||
|
||||
cmd, args = sys.argv[0], sys.argv[1:]
|
||||
socket_path = None
|
||||
path = None
|
||||
prop = None
|
||||
value = None
|
||||
|
||||
def usage():
|
||||
return '''environment variables:
|
||||
QMP_SOCKET=<path | addr:port>
|
||||
usage:
|
||||
%s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
|
||||
''' % cmd
|
||||
|
||||
def usage_error(error_msg = "unspecified error"):
|
||||
sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
|
||||
exit(1)
|
||||
|
||||
if len(args) > 0:
|
||||
if args[0] == "-h":
|
||||
print usage()
|
||||
exit(0);
|
||||
elif args[0] == "-s":
|
||||
try:
|
||||
socket_path = args[1]
|
||||
except:
|
||||
usage_error("missing argument: QMP socket path or address");
|
||||
args = args[2:]
|
||||
|
||||
if not socket_path:
|
||||
if os.environ.has_key('QMP_SOCKET'):
|
||||
socket_path = os.environ['QMP_SOCKET']
|
||||
else:
|
||||
usage_error("no QMP socket path or address given");
|
||||
|
||||
if len(args) > 1:
|
||||
try:
|
||||
path, prop = args[0].rsplit('.', 1)
|
||||
except:
|
||||
usage_error("invalid format for path/property/value")
|
||||
value = args[1]
|
||||
else:
|
||||
usage_error("not enough arguments")
|
||||
|
||||
srv = QEMUMonitorProtocol(socket_path)
|
||||
srv.connect()
|
||||
|
||||
print srv.command('qom-set', path=path, property=prop, value=sys.argv[2])
|
||||
@@ -1,3 +1,3 @@
|
||||
Read the documentation in qemu-doc.html or on http://wiki.qemu.org
|
||||
Read the documentation in qemu-doc.html.
|
||||
|
||||
- QEMU team
|
||||
Fabrice Bellard.
|
||||
@@ -9,8 +9,6 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
@@ -35,6 +33,7 @@ struct AioHandler
|
||||
IOHandler *io_read;
|
||||
IOHandler *io_write;
|
||||
AioFlushHandler *io_flush;
|
||||
AioProcessQueue *io_process_queue;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
@@ -57,6 +56,7 @@ int qemu_aio_set_fd_handler(int fd,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
AioFlushHandler *io_flush,
|
||||
AioProcessQueue *io_process_queue,
|
||||
void *opaque)
|
||||
{
|
||||
AioHandler *node;
|
||||
@@ -89,6 +89,7 @@ int qemu_aio_set_fd_handler(int fd,
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
node->io_flush = io_flush;
|
||||
node->io_process_queue = io_process_queue;
|
||||
node->opaque = opaque;
|
||||
}
|
||||
|
||||
@@ -99,96 +100,131 @@ int qemu_aio_set_fd_handler(int fd,
|
||||
|
||||
void qemu_aio_flush(void)
|
||||
{
|
||||
while (qemu_aio_wait());
|
||||
AioHandler *node;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = 0;
|
||||
|
||||
/*
|
||||
* If there are pending emulated aio start them now so flush
|
||||
* will be able to return 1.
|
||||
*/
|
||||
qemu_aio_wait();
|
||||
|
||||
QLIST_FOREACH(node, &aio_handlers, node) {
|
||||
if (node->io_flush) {
|
||||
ret |= node->io_flush(node->opaque);
|
||||
}
|
||||
}
|
||||
} while (qemu_bh_poll() || ret > 0);
|
||||
}
|
||||
|
||||
bool qemu_aio_wait(void)
|
||||
int qemu_aio_process_queue(void)
|
||||
{
|
||||
AioHandler *node;
|
||||
fd_set rdfds, wrfds;
|
||||
int max_fd = -1;
|
||||
int ret;
|
||||
bool busy;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call then.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (qemu_bh_poll()) {
|
||||
return true;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
walking_handlers = 1;
|
||||
|
||||
FD_ZERO(&rdfds);
|
||||
FD_ZERO(&wrfds);
|
||||
|
||||
/* fill fd sets */
|
||||
busy = false;
|
||||
QLIST_FOREACH(node, &aio_handlers, node) {
|
||||
/* If there aren't pending AIO operations, don't invoke callbacks.
|
||||
* Otherwise, if there are no AIO requests, qemu_aio_wait() would
|
||||
* wait indefinitely.
|
||||
*/
|
||||
if (node->io_flush) {
|
||||
if (node->io_flush(node->opaque) == 0) {
|
||||
continue;
|
||||
if (node->io_process_queue) {
|
||||
if (node->io_process_queue(node->opaque)) {
|
||||
ret = 1;
|
||||
}
|
||||
busy = true;
|
||||
}
|
||||
if (!node->deleted && node->io_read) {
|
||||
FD_SET(node->fd, &rdfds);
|
||||
max_fd = MAX(max_fd, node->fd + 1);
|
||||
}
|
||||
if (!node->deleted && node->io_write) {
|
||||
FD_SET(node->fd, &wrfds);
|
||||
max_fd = MAX(max_fd, node->fd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
walking_handlers = 0;
|
||||
|
||||
/* No AIO operations? Get us out of here */
|
||||
if (!busy) {
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
|
||||
void qemu_aio_wait(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (qemu_bh_poll())
|
||||
return;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call then.
|
||||
* Return afterwards to avoid waiting needlessly in select().
|
||||
*/
|
||||
if (qemu_aio_process_queue())
|
||||
return;
|
||||
|
||||
do {
|
||||
AioHandler *node;
|
||||
fd_set rdfds, wrfds;
|
||||
int max_fd = -1;
|
||||
|
||||
/* if we have any readable fds, dispatch event */
|
||||
if (ret > 0) {
|
||||
walking_handlers = 1;
|
||||
|
||||
/* we have to walk very carefully in case
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
FD_ZERO(&rdfds);
|
||||
FD_ZERO(&wrfds);
|
||||
|
||||
if (!node->deleted &&
|
||||
FD_ISSET(node->fd, &rdfds) &&
|
||||
node->io_read) {
|
||||
node->io_read(node->opaque);
|
||||
/* fill fd sets */
|
||||
QLIST_FOREACH(node, &aio_handlers, node) {
|
||||
/* If there aren't pending AIO operations, don't invoke callbacks.
|
||||
* Otherwise, if there are no AIO requests, qemu_aio_wait() would
|
||||
* wait indefinitely.
|
||||
*/
|
||||
if (node->io_flush && node->io_flush(node->opaque) == 0)
|
||||
continue;
|
||||
|
||||
if (!node->deleted && node->io_read) {
|
||||
FD_SET(node->fd, &rdfds);
|
||||
max_fd = MAX(max_fd, node->fd + 1);
|
||||
}
|
||||
if (!node->deleted &&
|
||||
FD_ISSET(node->fd, &wrfds) &&
|
||||
node->io_write) {
|
||||
node->io_write(node->opaque);
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
if (tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
if (!node->deleted && node->io_write) {
|
||||
FD_SET(node->fd, &wrfds);
|
||||
max_fd = MAX(max_fd, node->fd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
walking_handlers = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
/* No AIO operations? Get us out of here */
|
||||
if (max_fd == -1)
|
||||
break;
|
||||
|
||||
/* wait until next event */
|
||||
ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
|
||||
if (ret == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
/* if we have any readable fds, dispatch event */
|
||||
if (ret > 0) {
|
||||
walking_handlers = 1;
|
||||
|
||||
/* we have to walk very carefully in case
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
if (!node->deleted &&
|
||||
FD_ISSET(node->fd, &rdfds) &&
|
||||
node->io_read) {
|
||||
node->io_read(node->opaque);
|
||||
}
|
||||
if (!node->deleted &&
|
||||
FD_ISSET(node->fd, &wrfds) &&
|
||||
node->io_write) {
|
||||
node->io_write(node->opaque);
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
if (tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
walking_handlers = 0;
|
||||
}
|
||||
} while (ret == 0);
|
||||
}
|
||||
+64
-93
@@ -41,8 +41,6 @@
|
||||
#include "net.h"
|
||||
#include "gdbstub.h"
|
||||
#include "hw/smbios.h"
|
||||
#include "exec-memory.h"
|
||||
#include "hw/pcspk.h"
|
||||
|
||||
#ifdef TARGET_SPARC
|
||||
int graphic_width = 1024;
|
||||
@@ -54,6 +52,7 @@ int graphic_height = 600;
|
||||
int graphic_depth = 15;
|
||||
#endif
|
||||
|
||||
const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf";
|
||||
|
||||
#if defined(TARGET_ALPHA)
|
||||
#define QEMU_ARCH QEMU_ARCH_ALPHA
|
||||
@@ -95,65 +94,14 @@ const uint32_t arch_type = QEMU_ARCH;
|
||||
#define RAM_SAVE_FLAG_EOS 0x10
|
||||
#define RAM_SAVE_FLAG_CONTINUE 0x20
|
||||
|
||||
#ifdef __ALTIVEC__
|
||||
#include <altivec.h>
|
||||
#define VECTYPE vector unsigned char
|
||||
#define SPLAT(p) vec_splat(vec_ld(0, p), 0)
|
||||
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2)
|
||||
/* altivec.h may redefine the bool macro as vector type.
|
||||
* Reset it to POSIX semantics. */
|
||||
#undef bool
|
||||
#define bool _Bool
|
||||
#elif defined __SSE2__
|
||||
#include <emmintrin.h>
|
||||
#define VECTYPE __m128i
|
||||
#define SPLAT(p) _mm_set1_epi8(*(p))
|
||||
#define ALL_EQ(v1, v2) (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) == 0xFFFF)
|
||||
#else
|
||||
#define VECTYPE unsigned long
|
||||
#define SPLAT(p) (*(p) * (~0UL / 255))
|
||||
#define ALL_EQ(v1, v2) ((v1) == (v2))
|
||||
#endif
|
||||
|
||||
|
||||
static struct defconfig_file {
|
||||
const char *filename;
|
||||
/* Indicates it is an user config file (disabled by -no-user-config) */
|
||||
bool userconfig;
|
||||
} default_config_files[] = {
|
||||
{ CONFIG_QEMU_DATADIR "/cpus-" TARGET_ARCH ".conf", false },
|
||||
{ CONFIG_QEMU_CONFDIR "/qemu.conf", true },
|
||||
{ CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf", true },
|
||||
{ NULL }, /* end of list */
|
||||
};
|
||||
|
||||
|
||||
int qemu_read_default_config_files(bool userconfig)
|
||||
static int is_dup_page(uint8_t *page, uint8_t ch)
|
||||
{
|
||||
int ret;
|
||||
struct defconfig_file *f;
|
||||
|
||||
for (f = default_config_files; f->filename; f++) {
|
||||
if (!userconfig && f->userconfig) {
|
||||
continue;
|
||||
}
|
||||
ret = qemu_read_config_file(f->filename);
|
||||
if (ret < 0 && ret != -ENOENT) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_dup_page(uint8_t *page)
|
||||
{
|
||||
VECTYPE *p = (VECTYPE *)page;
|
||||
VECTYPE val = SPLAT(page);
|
||||
uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch;
|
||||
uint32_t *array = (uint32_t *)page;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TARGET_PAGE_SIZE / sizeof(VECTYPE); i++) {
|
||||
if (!ALL_EQ(val, p[i])) {
|
||||
for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) {
|
||||
if (array[i] != val) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -168,25 +116,26 @@ static int ram_save_block(QEMUFile *f)
|
||||
{
|
||||
RAMBlock *block = last_block;
|
||||
ram_addr_t offset = last_offset;
|
||||
ram_addr_t current_addr;
|
||||
int bytes_sent = 0;
|
||||
MemoryRegion *mr;
|
||||
|
||||
if (!block)
|
||||
block = QLIST_FIRST(&ram_list.blocks);
|
||||
|
||||
current_addr = block->offset + offset;
|
||||
|
||||
do {
|
||||
mr = block->mr;
|
||||
if (memory_region_get_dirty(mr, offset, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) {
|
||||
uint8_t *p;
|
||||
int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
|
||||
|
||||
memory_region_reset_dirty(mr, offset, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION);
|
||||
cpu_physical_memory_reset_dirty(current_addr,
|
||||
current_addr + TARGET_PAGE_SIZE,
|
||||
MIGRATION_DIRTY_FLAG);
|
||||
|
||||
p = memory_region_get_ram_ptr(mr) + offset;
|
||||
p = block->host + offset;
|
||||
|
||||
if (is_dup_page(p)) {
|
||||
if (is_dup_page(p, *p)) {
|
||||
qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
|
||||
if (!cont) {
|
||||
qemu_put_byte(f, strlen(block->idstr));
|
||||
@@ -216,7 +165,10 @@ static int ram_save_block(QEMUFile *f)
|
||||
if (!block)
|
||||
block = QLIST_FIRST(&ram_list.blocks);
|
||||
}
|
||||
} while (block != last_block || offset != last_offset);
|
||||
|
||||
current_addr = block->offset + offset;
|
||||
|
||||
} while (current_addr != last_block->offset + last_offset);
|
||||
|
||||
last_block = block;
|
||||
last_offset = offset;
|
||||
@@ -233,9 +185,9 @@ static ram_addr_t ram_save_remaining(void)
|
||||
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
ram_addr_t addr;
|
||||
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
|
||||
if (memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
for (addr = block->offset; addr < block->offset + block->length;
|
||||
addr += TARGET_PAGE_SIZE) {
|
||||
if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -269,8 +221,12 @@ static int block_compar(const void *a, const void *b)
|
||||
{
|
||||
RAMBlock * const *ablock = a;
|
||||
RAMBlock * const *bblock = b;
|
||||
|
||||
return strcmp((*ablock)->idstr, (*bblock)->idstr);
|
||||
if ((*ablock)->offset < (*bblock)->offset) {
|
||||
return -1;
|
||||
} else if ((*ablock)->offset > (*bblock)->offset) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sort_ram_list(void)
|
||||
@@ -294,7 +250,7 @@ static void sort_ram_list(void)
|
||||
g_free(blocks);
|
||||
}
|
||||
|
||||
int ram_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
uint64_t bytes_transferred_last;
|
||||
@@ -303,11 +259,14 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
int ret;
|
||||
|
||||
if (stage < 0) {
|
||||
memory_global_dirty_log_stop();
|
||||
cpu_physical_memory_set_dirty_tracking(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memory_global_sync_dirty_bitmap(get_system_memory());
|
||||
if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
|
||||
qemu_file_set_error(f, -EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (stage == 1) {
|
||||
RAMBlock *block;
|
||||
@@ -318,15 +277,17 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
|
||||
/* Make sure all dirty bits are set */
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
|
||||
if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
memory_region_set_dirty(block->mr, addr, TARGET_PAGE_SIZE);
|
||||
for (addr = block->offset; addr < block->offset + block->length;
|
||||
addr += TARGET_PAGE_SIZE) {
|
||||
if (!cpu_physical_memory_get_dirty(addr,
|
||||
MIGRATION_DIRTY_FLAG)) {
|
||||
cpu_physical_memory_set_dirty(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory_global_dirty_log_start();
|
||||
/* Enable dirty memory tracking */
|
||||
cpu_physical_memory_set_dirty_tracking(1);
|
||||
|
||||
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
||||
|
||||
@@ -371,7 +332,7 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
while ((bytes_sent = ram_save_block(f)) != 0) {
|
||||
bytes_transferred += bytes_sent;
|
||||
}
|
||||
memory_global_dirty_log_stop();
|
||||
cpu_physical_memory_set_dirty_tracking(0);
|
||||
}
|
||||
|
||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||
@@ -395,7 +356,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return memory_region_get_ram_ptr(block->mr) + offset;
|
||||
return block->host + offset;
|
||||
}
|
||||
|
||||
len = qemu_get_byte(f);
|
||||
@@ -404,7 +365,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
if (!strncmp(id, block->idstr, sizeof(id)))
|
||||
return memory_region_get_ram_ptr(block->mr) + offset;
|
||||
return block->host + offset;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Can't find block %s!\n", id);
|
||||
@@ -417,7 +378,7 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
int flags;
|
||||
int error;
|
||||
|
||||
if (version_id < 4 || version_id > 4) {
|
||||
if (version_id < 3 || version_id > 4) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -428,7 +389,11 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
|
||||
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
|
||||
if (version_id == 4) {
|
||||
if (version_id == 3) {
|
||||
if (addr != ram_bytes_total()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* Synchronize RAM block list */
|
||||
char id[256];
|
||||
ram_addr_t length;
|
||||
@@ -466,7 +431,10 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
void *host;
|
||||
uint8_t ch;
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (version_id == 3)
|
||||
host = qemu_get_ram_ptr(addr);
|
||||
else
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -482,7 +450,10 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
} else if (flags & RAM_SAVE_FLAG_PAGE) {
|
||||
void *host;
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (version_id == 3)
|
||||
host = qemu_get_ram_ptr(addr);
|
||||
else
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
|
||||
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
|
||||
}
|
||||
@@ -502,14 +473,14 @@ struct soundhw {
|
||||
int enabled;
|
||||
int isa;
|
||||
union {
|
||||
int (*init_isa) (ISABus *bus);
|
||||
int (*init_isa) (qemu_irq *pic);
|
||||
int (*init_pci) (PCIBus *bus);
|
||||
} init;
|
||||
};
|
||||
|
||||
static struct soundhw soundhw[] = {
|
||||
#ifdef HAS_AUDIO_CHOICE
|
||||
#ifdef CONFIG_PCSPK
|
||||
#if defined(TARGET_I386) || defined(TARGET_MIPS)
|
||||
{
|
||||
"pcspk",
|
||||
"PC speaker",
|
||||
@@ -657,15 +628,15 @@ void select_soundhw(const char *optarg)
|
||||
}
|
||||
}
|
||||
|
||||
void audio_init(ISABus *isa_bus, PCIBus *pci_bus)
|
||||
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
|
||||
{
|
||||
struct soundhw *c;
|
||||
|
||||
for (c = soundhw; c->name; ++c) {
|
||||
if (c->enabled) {
|
||||
if (c->isa) {
|
||||
if (isa_bus) {
|
||||
c->init.init_isa(isa_bus);
|
||||
if (isa_pic) {
|
||||
c->init.init_isa(isa_pic);
|
||||
}
|
||||
} else {
|
||||
if (pci_bus) {
|
||||
@@ -679,7 +650,7 @@ void audio_init(ISABus *isa_bus, PCIBus *pci_bus)
|
||||
void select_soundhw(const char *optarg)
|
||||
{
|
||||
}
|
||||
void audio_init(ISABus *isa_bus, PCIBus *pci_bus)
|
||||
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
#ifndef QEMU_ARCH_INIT_H
|
||||
#define QEMU_ARCH_INIT_H
|
||||
|
||||
extern const char arch_config_name[];
|
||||
|
||||
enum {
|
||||
QEMU_ARCH_ALL = -1,
|
||||
QEMU_ARCH_ALPHA = 1,
|
||||
@@ -25,7 +27,7 @@ void do_acpitable_option(const char *optarg);
|
||||
void do_smbios_option(const char *optarg);
|
||||
void cpudef_init(void);
|
||||
int audio_available(void);
|
||||
void audio_init(ISABus *isa_bus, PCIBus *pci_bus);
|
||||
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus);
|
||||
int tcg_available(void);
|
||||
int kvm_available(void);
|
||||
int xen_available(void);
|
||||
|
||||
@@ -1624,7 +1624,7 @@ arm_decode_shift (long given, fprintf_function func, void *stream,
|
||||
}
|
||||
|
||||
/* Print one coprocessor instruction on INFO->STREAM.
|
||||
Return true if the instruction matched, false if this is not a
|
||||
Return true if the instuction matched, false if this is not a
|
||||
recognised coprocessor instruction. */
|
||||
|
||||
static bfd_boolean
|
||||
@@ -2214,7 +2214,7 @@ print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
|
||||
}
|
||||
|
||||
/* Print one neon instruction on INFO->STREAM.
|
||||
Return true if the instruction matched, false if this is not a
|
||||
Return true if the instuction matched, false if this is not a
|
||||
recognised neon instruction. */
|
||||
|
||||
static bfd_boolean
|
||||
@@ -3927,7 +3927,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
n = last_mapping_sym - 1;
|
||||
|
||||
/* No mapping symbol found at this address. Look backwards
|
||||
for a preceding one. */
|
||||
for a preceeding one. */
|
||||
for (; n >= 0; n--)
|
||||
{
|
||||
if (get_sym_code_type (info, n, &type))
|
||||
|
||||
+54
-55
@@ -37,26 +37,26 @@
|
||||
#include "hw/arm-misc.h"
|
||||
#endif
|
||||
|
||||
#define TARGET_SYS_OPEN 0x01
|
||||
#define TARGET_SYS_CLOSE 0x02
|
||||
#define TARGET_SYS_WRITEC 0x03
|
||||
#define TARGET_SYS_WRITE0 0x04
|
||||
#define TARGET_SYS_WRITE 0x05
|
||||
#define TARGET_SYS_READ 0x06
|
||||
#define TARGET_SYS_READC 0x07
|
||||
#define TARGET_SYS_ISTTY 0x09
|
||||
#define TARGET_SYS_SEEK 0x0a
|
||||
#define TARGET_SYS_FLEN 0x0c
|
||||
#define TARGET_SYS_TMPNAM 0x0d
|
||||
#define TARGET_SYS_REMOVE 0x0e
|
||||
#define TARGET_SYS_RENAME 0x0f
|
||||
#define TARGET_SYS_CLOCK 0x10
|
||||
#define TARGET_SYS_TIME 0x11
|
||||
#define TARGET_SYS_SYSTEM 0x12
|
||||
#define TARGET_SYS_ERRNO 0x13
|
||||
#define TARGET_SYS_GET_CMDLINE 0x15
|
||||
#define TARGET_SYS_HEAPINFO 0x16
|
||||
#define TARGET_SYS_EXIT 0x18
|
||||
#define SYS_OPEN 0x01
|
||||
#define SYS_CLOSE 0x02
|
||||
#define SYS_WRITEC 0x03
|
||||
#define SYS_WRITE0 0x04
|
||||
#define SYS_WRITE 0x05
|
||||
#define SYS_READ 0x06
|
||||
#define SYS_READC 0x07
|
||||
#define SYS_ISTTY 0x09
|
||||
#define SYS_SEEK 0x0a
|
||||
#define SYS_FLEN 0x0c
|
||||
#define SYS_TMPNAM 0x0d
|
||||
#define SYS_REMOVE 0x0e
|
||||
#define SYS_RENAME 0x0f
|
||||
#define SYS_CLOCK 0x10
|
||||
#define SYS_TIME 0x11
|
||||
#define SYS_SYSTEM 0x12
|
||||
#define SYS_ERRNO 0x13
|
||||
#define SYS_GET_CMDLINE 0x15
|
||||
#define SYS_HEAPINFO 0x16
|
||||
#define SYS_EXIT 0x18
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
@@ -108,7 +108,7 @@ static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
|
||||
return code;
|
||||
}
|
||||
#else
|
||||
static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
|
||||
static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ static target_ulong arm_semi_syscall_len;
|
||||
static target_ulong syscall_err;
|
||||
#endif
|
||||
|
||||
static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
|
||||
static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
TaskState *ts = env->opaque;
|
||||
@@ -138,11 +138,11 @@ static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
|
||||
} else {
|
||||
/* Fixup syscalls that use nonstardard return conventions. */
|
||||
switch (env->regs[0]) {
|
||||
case TARGET_SYS_WRITE:
|
||||
case TARGET_SYS_READ:
|
||||
case SYS_WRITE:
|
||||
case SYS_READ:
|
||||
env->regs[0] = arm_semi_syscall_len - ret;
|
||||
break;
|
||||
case TARGET_SYS_SEEK:
|
||||
case SYS_SEEK:
|
||||
env->regs[0] = 0;
|
||||
break;
|
||||
default:
|
||||
@@ -152,7 +152,7 @@ static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong err)
|
||||
static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
|
||||
{
|
||||
/* The size is always stored in big-endian order, extract
|
||||
the value. We assume the size always fit in 32 bits. */
|
||||
@@ -174,7 +174,7 @@ static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong er
|
||||
__arg; \
|
||||
})
|
||||
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
|
||||
uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
uint32_t do_arm_semihosting(CPUState *env)
|
||||
{
|
||||
target_ulong args;
|
||||
char * s;
|
||||
@@ -184,42 +184,41 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
TaskState *ts = env->opaque;
|
||||
#else
|
||||
CPUARMState *ts = env;
|
||||
CPUState *ts = env;
|
||||
#endif
|
||||
|
||||
nr = env->regs[0];
|
||||
args = env->regs[1];
|
||||
switch (nr) {
|
||||
case TARGET_SYS_OPEN:
|
||||
case SYS_OPEN:
|
||||
if (!(s = lock_user_string(ARG(0))))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
if (ARG(1) >= 12) {
|
||||
unlock_user(s, ARG(0), 0);
|
||||
if (ARG(1) >= 12)
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
if (strcmp(s, ":tt") == 0) {
|
||||
int result_fileno = ARG(1) < 4 ? STDIN_FILENO : STDOUT_FILENO;
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return result_fileno;
|
||||
if (ARG(1) < 4)
|
||||
return STDIN_FILENO;
|
||||
else
|
||||
return STDOUT_FILENO;
|
||||
}
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
|
||||
(int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
|
||||
ret = env->regs[0];
|
||||
return env->regs[0];
|
||||
} else {
|
||||
ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
|
||||
}
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
case TARGET_SYS_CLOSE:
|
||||
case SYS_CLOSE:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return set_swi_errno(ts, close(ARG(0)));
|
||||
}
|
||||
case TARGET_SYS_WRITEC:
|
||||
case SYS_WRITEC:
|
||||
{
|
||||
char c;
|
||||
|
||||
@@ -234,7 +233,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
return write(STDERR_FILENO, &c, 1);
|
||||
}
|
||||
}
|
||||
case TARGET_SYS_WRITE0:
|
||||
case SYS_WRITE0:
|
||||
if (!(s = lock_user_string(args)))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
@@ -247,7 +246,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
}
|
||||
unlock_user(s, args, 0);
|
||||
return ret;
|
||||
case TARGET_SYS_WRITE:
|
||||
case SYS_WRITE:
|
||||
len = ARG(2);
|
||||
if (use_gdb_syscalls()) {
|
||||
arm_semi_syscall_len = len;
|
||||
@@ -263,7 +262,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
return -1;
|
||||
return len - ret;
|
||||
}
|
||||
case TARGET_SYS_READ:
|
||||
case SYS_READ:
|
||||
len = ARG(2);
|
||||
if (use_gdb_syscalls()) {
|
||||
arm_semi_syscall_len = len;
|
||||
@@ -281,17 +280,17 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
return -1;
|
||||
return len - ret;
|
||||
}
|
||||
case TARGET_SYS_READC:
|
||||
case SYS_READC:
|
||||
/* XXX: Read from debug cosole. Not implemented. */
|
||||
return 0;
|
||||
case TARGET_SYS_ISTTY:
|
||||
case SYS_ISTTY:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return isatty(ARG(0));
|
||||
}
|
||||
case TARGET_SYS_SEEK:
|
||||
case SYS_SEEK:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
|
||||
return env->regs[0];
|
||||
@@ -301,7 +300,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
case TARGET_SYS_FLEN:
|
||||
case SYS_FLEN:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
|
||||
ARG(0), env->regs[13]-64);
|
||||
@@ -313,10 +312,10 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
return -1;
|
||||
return buf.st_size;
|
||||
}
|
||||
case TARGET_SYS_TMPNAM:
|
||||
case SYS_TMPNAM:
|
||||
/* XXX: Not implemented. */
|
||||
return -1;
|
||||
case TARGET_SYS_REMOVE:
|
||||
case SYS_REMOVE:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
|
||||
ret = env->regs[0];
|
||||
@@ -328,7 +327,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
unlock_user(s, ARG(0), 0);
|
||||
}
|
||||
return ret;
|
||||
case TARGET_SYS_RENAME:
|
||||
case SYS_RENAME:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
|
||||
ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
|
||||
@@ -348,11 +347,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
}
|
||||
case TARGET_SYS_CLOCK:
|
||||
case SYS_CLOCK:
|
||||
return clock() / (CLOCKS_PER_SEC / 100);
|
||||
case TARGET_SYS_TIME:
|
||||
case SYS_TIME:
|
||||
return set_swi_errno(ts, time(NULL));
|
||||
case TARGET_SYS_SYSTEM:
|
||||
case SYS_SYSTEM:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
|
||||
return env->regs[0];
|
||||
@@ -364,13 +363,13 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
}
|
||||
case TARGET_SYS_ERRNO:
|
||||
case SYS_ERRNO:
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return ts->swi_errno;
|
||||
#else
|
||||
return syscall_err;
|
||||
#endif
|
||||
case TARGET_SYS_GET_CMDLINE:
|
||||
case SYS_GET_CMDLINE:
|
||||
{
|
||||
/* Build a command-line from the original argv.
|
||||
*
|
||||
@@ -453,7 +452,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
|
||||
return status;
|
||||
}
|
||||
case TARGET_SYS_HEAPINFO:
|
||||
case SYS_HEAPINFO:
|
||||
{
|
||||
uint32_t *ptr;
|
||||
uint32_t limit;
|
||||
@@ -499,7 +498,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
case TARGET_SYS_EXIT:
|
||||
case SYS_EXIT:
|
||||
gdb_exit(env, 0);
|
||||
exit(0);
|
||||
default:
|
||||
|
||||
@@ -35,10 +35,10 @@ static struct QEMUBH *first_bh;
|
||||
struct QEMUBH {
|
||||
QEMUBHFunc *cb;
|
||||
void *opaque;
|
||||
int scheduled;
|
||||
int idle;
|
||||
int deleted;
|
||||
QEMUBH *next;
|
||||
bool scheduled;
|
||||
bool idle;
|
||||
bool deleted;
|
||||
};
|
||||
|
||||
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
|
||||
@@ -120,7 +120,7 @@ void qemu_bh_delete(QEMUBH *bh)
|
||||
bh->deleted = 1;
|
||||
}
|
||||
|
||||
void qemu_bh_update_timeout(uint32_t *timeout)
|
||||
void qemu_bh_update_timeout(int *timeout)
|
||||
{
|
||||
QEMUBH *bh;
|
||||
|
||||
|
||||
+7
-31
@@ -585,20 +585,17 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
|
||||
switch (as->fmt) {
|
||||
case AUD_FMT_S8:
|
||||
sign = 1;
|
||||
/* fall through */
|
||||
case AUD_FMT_U8:
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
sign = 1;
|
||||
/* fall through */
|
||||
case AUD_FMT_U16:
|
||||
bits = 16;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S32:
|
||||
sign = 1;
|
||||
/* fall through */
|
||||
case AUD_FMT_U32:
|
||||
bits = 32;
|
||||
break;
|
||||
@@ -957,9 +954,7 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
|
||||
total += isamp;
|
||||
}
|
||||
|
||||
if (!(hw->ctl_caps & VOICE_VOLUME_CAP)) {
|
||||
mixeng_volume (sw->buf, ret, &sw->vol);
|
||||
}
|
||||
mixeng_volume (sw->buf, ret, &sw->vol);
|
||||
|
||||
sw->clip (buf, sw->buf, ret);
|
||||
sw->total_hw_samples_acquired += total;
|
||||
@@ -1043,10 +1038,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
|
||||
swlim = audio_MIN (swlim, samples);
|
||||
if (swlim) {
|
||||
sw->conv (sw->buf, buf, swlim);
|
||||
|
||||
if (!(sw->hw->ctl_caps & VOICE_VOLUME_CAP)) {
|
||||
mixeng_volume (sw->buf, swlim, &sw->vol);
|
||||
}
|
||||
mixeng_volume (sw->buf, swlim, &sw->vol);
|
||||
}
|
||||
|
||||
while (swlim) {
|
||||
@@ -1673,7 +1665,7 @@ static void audio_pp_nb_voices (const char *typ, int nb)
|
||||
printf ("Theoretically supports many %s voices\n", typ);
|
||||
break;
|
||||
default:
|
||||
printf ("Theoretically supports up to %d %s voices\n", nb, typ);
|
||||
printf ("Theoretically supports upto %d %s voices\n", nb, typ);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1775,12 +1767,10 @@ static void audio_atexit (void)
|
||||
HWVoiceOut *hwo = NULL;
|
||||
HWVoiceIn *hwi = NULL;
|
||||
|
||||
while ((hwo = audio_pcm_hw_find_any_out (hwo))) {
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
|
||||
SWVoiceCap *sc;
|
||||
|
||||
if (hwo->enabled) {
|
||||
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
|
||||
}
|
||||
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
|
||||
hwo->pcm_ops->fini_out (hwo);
|
||||
|
||||
for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
|
||||
@@ -1793,10 +1783,8 @@ static void audio_atexit (void)
|
||||
}
|
||||
}
|
||||
|
||||
while ((hwi = audio_pcm_hw_find_any_in (hwi))) {
|
||||
if (hwi->enabled) {
|
||||
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
|
||||
}
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
|
||||
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
|
||||
hwi->pcm_ops->fini_in (hwi);
|
||||
}
|
||||
|
||||
@@ -2062,29 +2050,17 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
|
||||
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
|
||||
{
|
||||
if (sw) {
|
||||
HWVoiceOut *hw = sw->hw;
|
||||
|
||||
sw->vol.mute = mute;
|
||||
sw->vol.l = nominal_volume.l * lvol / 255;
|
||||
sw->vol.r = nominal_volume.r * rvol / 255;
|
||||
|
||||
if (hw->pcm_ops->ctl_out) {
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_VOLUME, sw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
|
||||
{
|
||||
if (sw) {
|
||||
HWVoiceIn *hw = sw->hw;
|
||||
|
||||
sw->vol.mute = mute;
|
||||
sw->vol.l = nominal_volume.l * lvol / 255;
|
||||
sw->vol.r = nominal_volume.r * rvol / 255;
|
||||
|
||||
if (hw->pcm_ops->ctl_in) {
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_VOLUME, sw);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,6 @@ typedef struct HWVoiceOut {
|
||||
int samples;
|
||||
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
|
||||
int ctl_caps;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
QLIST_ENTRY (HWVoiceOut) entries;
|
||||
} HWVoiceOut;
|
||||
@@ -102,7 +101,6 @@ typedef struct HWVoiceIn {
|
||||
|
||||
int samples;
|
||||
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
|
||||
int ctl_caps;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
QLIST_ENTRY (HWVoiceIn) entries;
|
||||
} HWVoiceIn;
|
||||
@@ -152,7 +150,6 @@ struct audio_driver {
|
||||
int max_voices_in;
|
||||
int voice_size_out;
|
||||
int voice_size_in;
|
||||
int ctl_caps;
|
||||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
@@ -234,9 +231,6 @@ void audio_run (const char *msg);
|
||||
|
||||
#define VOICE_ENABLE 1
|
||||
#define VOICE_DISABLE 2
|
||||
#define VOICE_VOLUME 3
|
||||
|
||||
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
|
||||
|
||||
static inline int audio_ring_dist (int dst, int src, int len)
|
||||
{
|
||||
|
||||
@@ -263,8 +263,6 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
}
|
||||
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
hw->ctl_caps = drv->ctl_caps;
|
||||
|
||||
QLIST_INIT (&hw->sw_head);
|
||||
#ifdef DAC
|
||||
QLIST_INIT (&hw->cap_head);
|
||||
|
||||
+1
-1
@@ -201,7 +201,7 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
dolog ("Will use 16 instead of 32 bit samples\n");
|
||||
/* fall through */
|
||||
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
deffmt:
|
||||
|
||||
+12
-24
@@ -33,8 +33,7 @@
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
|
||||
/* Signed 8 bit */
|
||||
#define BSIZE 8
|
||||
#define ITYPE int
|
||||
#define IN_T int8_t
|
||||
#define IN_MIN SCHAR_MIN
|
||||
#define IN_MAX SCHAR_MAX
|
||||
#define SIGNED
|
||||
@@ -43,29 +42,25 @@
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef BSIZE
|
||||
#undef ITYPE
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
/* Unsigned 8 bit */
|
||||
#define BSIZE 8
|
||||
#define ITYPE uint
|
||||
#define IN_T uint8_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX UCHAR_MAX
|
||||
#define SHIFT 8
|
||||
#include "mixeng_template.h"
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef BSIZE
|
||||
#undef ITYPE
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
|
||||
/* Signed 16 bit */
|
||||
#define BSIZE 16
|
||||
#define ITYPE int
|
||||
#define IN_T int16_t
|
||||
#define IN_MIN SHRT_MIN
|
||||
#define IN_MAX SHRT_MAX
|
||||
#define SIGNED
|
||||
@@ -83,13 +78,11 @@
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef BSIZE
|
||||
#undef ITYPE
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
/* Unsigned 16 bit */
|
||||
#define BSIZE 16
|
||||
#define ITYPE uint
|
||||
#define IN_T uint16_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX USHRT_MAX
|
||||
#define SHIFT 16
|
||||
@@ -105,13 +98,11 @@
|
||||
#undef ENDIAN_CONVERSION
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef BSIZE
|
||||
#undef ITYPE
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
/* Signed 32 bit */
|
||||
#define BSIZE 32
|
||||
#define ITYPE int
|
||||
#define IN_T int32_t
|
||||
#define IN_MIN INT32_MIN
|
||||
#define IN_MAX INT32_MAX
|
||||
#define SIGNED
|
||||
@@ -129,13 +120,11 @@
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef BSIZE
|
||||
#undef ITYPE
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
/* Unsigned 32 bit */
|
||||
#define BSIZE 32
|
||||
#define ITYPE uint
|
||||
#define IN_T uint32_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX UINT32_MAX
|
||||
#define SHIFT 32
|
||||
@@ -151,8 +140,7 @@
|
||||
#undef ENDIAN_CONVERSION
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef BSIZE
|
||||
#undef ITYPE
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
t_sample *mixeng_conv[2][2][2][3] = {
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
#define HALF (IN_MAX >> 1)
|
||||
#endif
|
||||
|
||||
#define ET glue (ENDIAN_CONVERSION, glue (glue (glue (_, ITYPE), BSIZE), _t))
|
||||
#define IN_T glue (glue (ITYPE, BSIZE), _t)
|
||||
#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
static mixeng_real inline glue (conv_, ET) (IN_T v)
|
||||
@@ -151,4 +150,3 @@ static void glue (glue (clip_, ET), _from_mono)
|
||||
|
||||
#undef ET
|
||||
#undef HALF
|
||||
#undef IN_T
|
||||
+43
-460
@@ -2,7 +2,8 @@
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/error.h>
|
||||
|
||||
#define AUDIO_CAP "pulseaudio"
|
||||
#include "audio_int.h"
|
||||
@@ -14,7 +15,7 @@ typedef struct {
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
pa_stream *stream;
|
||||
pa_simple *s;
|
||||
void *pcm_buf;
|
||||
struct audio_pt pt;
|
||||
} PAVoiceOut;
|
||||
@@ -25,23 +26,17 @@ typedef struct {
|
||||
int dead;
|
||||
int incr;
|
||||
int wpos;
|
||||
pa_stream *stream;
|
||||
pa_simple *s;
|
||||
void *pcm_buf;
|
||||
struct audio_pt pt;
|
||||
const void *read_data;
|
||||
size_t read_index, read_length;
|
||||
} PAVoiceIn;
|
||||
|
||||
typedef struct {
|
||||
static struct {
|
||||
int samples;
|
||||
char *server;
|
||||
char *sink;
|
||||
char *source;
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
} paaudio;
|
||||
|
||||
static paaudio glob_paaudio = {
|
||||
} conf = {
|
||||
.samples = 4096,
|
||||
};
|
||||
|
||||
@@ -56,146 +51,6 @@ static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
|
||||
}
|
||||
|
||||
#ifndef PA_CONTEXT_IS_GOOD
|
||||
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
|
||||
{
|
||||
return
|
||||
x == PA_CONTEXT_CONNECTING ||
|
||||
x == PA_CONTEXT_AUTHORIZING ||
|
||||
x == PA_CONTEXT_SETTING_NAME ||
|
||||
x == PA_CONTEXT_READY;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef PA_STREAM_IS_GOOD
|
||||
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
|
||||
{
|
||||
return
|
||||
x == PA_STREAM_CREATING ||
|
||||
x == PA_STREAM_READY;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
|
||||
do { \
|
||||
if (!(expression)) { \
|
||||
if (rerror) { \
|
||||
*(rerror) = pa_context_errno ((c)->context); \
|
||||
} \
|
||||
goto label; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
|
||||
do { \
|
||||
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
|
||||
!(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
|
||||
if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
|
||||
((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
|
||||
if (rerror) { \
|
||||
*(rerror) = pa_context_errno ((c)->context); \
|
||||
} \
|
||||
} else { \
|
||||
if (rerror) { \
|
||||
*(rerror) = PA_ERR_BADSTATE; \
|
||||
} \
|
||||
} \
|
||||
goto label; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
|
||||
|
||||
while (length > 0) {
|
||||
size_t l;
|
||||
|
||||
while (!p->read_data) {
|
||||
int r;
|
||||
|
||||
r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
|
||||
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
|
||||
|
||||
if (!p->read_data) {
|
||||
pa_threaded_mainloop_wait (g->mainloop);
|
||||
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
|
||||
} else {
|
||||
p->read_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
l = p->read_length < length ? p->read_length : length;
|
||||
memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
|
||||
|
||||
data = (uint8_t *) data + l;
|
||||
length -= l;
|
||||
|
||||
p->read_index += l;
|
||||
p->read_length -= l;
|
||||
|
||||
if (!p->read_length) {
|
||||
int r;
|
||||
|
||||
r = pa_stream_drop (p->stream);
|
||||
p->read_data = NULL;
|
||||
p->read_length = 0;
|
||||
p->read_index = 0;
|
||||
|
||||
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
|
||||
}
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
return 0;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
|
||||
|
||||
while (length > 0) {
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
while (!(l = pa_stream_writable_size (p->stream))) {
|
||||
pa_threaded_mainloop_wait (g->mainloop);
|
||||
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
|
||||
}
|
||||
|
||||
CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
|
||||
|
||||
if (l > length) {
|
||||
l = length;
|
||||
}
|
||||
|
||||
r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
|
||||
|
||||
data = (const uint8_t *) data + l;
|
||||
length -= l;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
return 0;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *qpa_thread_out (void *arg)
|
||||
{
|
||||
PAVoiceOut *pa = arg;
|
||||
@@ -222,7 +77,7 @@ static void *qpa_thread_out (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
|
||||
decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
|
||||
rpos = pa->rpos;
|
||||
|
||||
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
||||
@@ -236,8 +91,8 @@ static void *qpa_thread_out (void *arg)
|
||||
|
||||
hw->clip (pa->pcm_buf, src, chunk);
|
||||
|
||||
if (qpa_simple_write (pa, pa->pcm_buf,
|
||||
chunk << hw->info.shift, &error) < 0) {
|
||||
if (pa_simple_write (pa->s, pa->pcm_buf,
|
||||
chunk << hw->info.shift, &error) < 0) {
|
||||
qpa_logerr (error, "pa_simple_write failed\n");
|
||||
return NULL;
|
||||
}
|
||||
@@ -314,7 +169,7 @@ static void *qpa_thread_in (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
|
||||
incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
|
||||
wpos = pa->wpos;
|
||||
|
||||
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
||||
@@ -326,8 +181,8 @@ static void *qpa_thread_in (void *arg)
|
||||
int chunk = audio_MIN (to_grab, hw->samples - wpos);
|
||||
void *buf = advance (pa->pcm_buf, wpos);
|
||||
|
||||
if (qpa_simple_read (pa, buf,
|
||||
chunk << hw->info.shift, &error) < 0) {
|
||||
if (pa_simple_read (pa->s, buf,
|
||||
chunk << hw->info.shift, &error) < 0) {
|
||||
qpa_logerr (error, "pa_simple_read failed\n");
|
||||
return NULL;
|
||||
}
|
||||
@@ -428,112 +283,6 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
||||
}
|
||||
}
|
||||
|
||||
static void context_state_cb (pa_context *c, void *userdata)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
switch (pa_context_get_state(c)) {
|
||||
case PA_CONTEXT_READY:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
case PA_CONTEXT_FAILED:
|
||||
pa_threaded_mainloop_signal (g->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
case PA_CONTEXT_SETTING_NAME:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_state_cb (pa_stream *s, void * userdata)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
switch (pa_stream_get_state (s)) {
|
||||
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
pa_threaded_mainloop_signal (g->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
case PA_STREAM_CREATING:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
pa_threaded_mainloop_signal (g->mainloop, 0);
|
||||
}
|
||||
|
||||
static pa_stream *qpa_simple_new (
|
||||
const char *server,
|
||||
const char *name,
|
||||
pa_stream_direction_t dir,
|
||||
const char *dev,
|
||||
const char *stream_name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
const pa_buffer_attr *attr,
|
||||
int *rerror)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
int r;
|
||||
pa_stream *stream;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
stream = pa_stream_new (g->context, name, ss, map);
|
||||
if (!stream) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_stream_set_state_callback (stream, stream_state_cb, g);
|
||||
pa_stream_set_read_callback (stream, stream_request_cb, g);
|
||||
pa_stream_set_write_callback (stream, stream_request_cb, g);
|
||||
|
||||
if (dir == PA_STREAM_PLAYBACK) {
|
||||
r = pa_stream_connect_playback (stream, dev, attr,
|
||||
PA_STREAM_INTERPOLATE_TIMING
|
||||
#ifdef PA_STREAM_ADJUST_LATENCY
|
||||
|PA_STREAM_ADJUST_LATENCY
|
||||
#endif
|
||||
|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
|
||||
} else {
|
||||
r = pa_stream_connect_record (stream, dev, attr,
|
||||
PA_STREAM_INTERPOLATE_TIMING
|
||||
#ifdef PA_STREAM_ADJUST_LATENCY
|
||||
|PA_STREAM_ADJUST_LATENCY
|
||||
#endif
|
||||
|PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
|
||||
return stream;
|
||||
|
||||
fail:
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
|
||||
if (stream) {
|
||||
pa_stream_unref (stream);
|
||||
}
|
||||
|
||||
*rerror = pa_context_errno (g->context);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
{
|
||||
int error;
|
||||
@@ -557,24 +306,24 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
|
||||
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
glob_paaudio.server,
|
||||
pa->s = pa_simple_new (
|
||||
conf.server,
|
||||
"qemu",
|
||||
PA_STREAM_PLAYBACK,
|
||||
glob_paaudio.sink,
|
||||
conf.sink,
|
||||
"pcm.playback",
|
||||
&ss,
|
||||
NULL, /* channel map */
|
||||
&ba, /* buffering attributes */
|
||||
&error
|
||||
);
|
||||
if (!pa->stream) {
|
||||
if (!pa->s) {
|
||||
qpa_logerr (error, "pa_simple_new for playback failed\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = glob_paaudio.samples;
|
||||
hw->samples = conf.samples;
|
||||
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
pa->rpos = hw->rpos;
|
||||
if (!pa->pcm_buf) {
|
||||
@@ -593,10 +342,8 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
g_free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
fail2:
|
||||
if (pa->stream) {
|
||||
pa_stream_unref (pa->stream);
|
||||
pa->stream = NULL;
|
||||
}
|
||||
pa_simple_free (pa->s);
|
||||
pa->s = NULL;
|
||||
fail1:
|
||||
return -1;
|
||||
}
|
||||
@@ -614,24 +361,24 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
|
||||
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
glob_paaudio.server,
|
||||
pa->s = pa_simple_new (
|
||||
conf.server,
|
||||
"qemu",
|
||||
PA_STREAM_RECORD,
|
||||
glob_paaudio.source,
|
||||
conf.source,
|
||||
"pcm.capture",
|
||||
&ss,
|
||||
NULL, /* channel map */
|
||||
NULL, /* buffering attributes */
|
||||
&error
|
||||
);
|
||||
if (!pa->stream) {
|
||||
if (!pa->s) {
|
||||
qpa_logerr (error, "pa_simple_new for capture failed\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = glob_paaudio.samples;
|
||||
hw->samples = conf.samples;
|
||||
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
pa->wpos = hw->wpos;
|
||||
if (!pa->pcm_buf) {
|
||||
@@ -650,10 +397,8 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
g_free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
fail2:
|
||||
if (pa->stream) {
|
||||
pa_stream_unref (pa->stream);
|
||||
pa->stream = NULL;
|
||||
}
|
||||
pa_simple_free (pa->s);
|
||||
pa->s = NULL;
|
||||
fail1:
|
||||
return -1;
|
||||
}
|
||||
@@ -668,9 +413,9 @@ static void qpa_fini_out (HWVoiceOut *hw)
|
||||
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
|
||||
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
|
||||
|
||||
if (pa->stream) {
|
||||
pa_stream_unref (pa->stream);
|
||||
pa->stream = NULL;
|
||||
if (pa->s) {
|
||||
pa_simple_free (pa->s);
|
||||
pa->s = NULL;
|
||||
}
|
||||
|
||||
audio_pt_fini (&pa->pt, AUDIO_FUNC);
|
||||
@@ -688,9 +433,9 @@ static void qpa_fini_in (HWVoiceIn *hw)
|
||||
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
|
||||
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
|
||||
|
||||
if (pa->stream) {
|
||||
pa_stream_unref (pa->stream);
|
||||
pa->stream = NULL;
|
||||
if (pa->s) {
|
||||
pa_simple_free (pa->s);
|
||||
pa->s = NULL;
|
||||
}
|
||||
|
||||
audio_pt_fini (&pa->pt, AUDIO_FUNC);
|
||||
@@ -700,213 +445,52 @@ static void qpa_fini_in (HWVoiceIn *hw)
|
||||
|
||||
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
pa_operation *op;
|
||||
pa_cvolume v;
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
|
||||
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
|
||||
#endif
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
SWVoiceOut *sw;
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceOut *);
|
||||
va_end (ap);
|
||||
|
||||
v.channels = 2;
|
||||
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
|
||||
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
op = pa_context_set_sink_input_volume (g->context,
|
||||
pa_stream_get_index (pa->stream),
|
||||
&v, NULL, NULL);
|
||||
if (!op)
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"set_sink_input_volume() failed\n");
|
||||
else
|
||||
pa_operation_unref (op);
|
||||
|
||||
op = pa_context_set_sink_input_mute (g->context,
|
||||
pa_stream_get_index (pa->stream),
|
||||
sw->vol.mute, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"set_sink_input_mute() failed\n");
|
||||
} else {
|
||||
pa_operation_unref (op);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
}
|
||||
}
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
pa_operation *op;
|
||||
pa_cvolume v;
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
#ifdef PA_CHECK_VERSION
|
||||
pa_cvolume_init (&v);
|
||||
#endif
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
SWVoiceIn *sw;
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceIn *);
|
||||
va_end (ap);
|
||||
|
||||
v.channels = 2;
|
||||
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
|
||||
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
/* FIXME: use the upcoming "set_source_output_{volume,mute}" */
|
||||
op = pa_context_set_source_volume_by_index (g->context,
|
||||
pa_stream_get_device_index (pa->stream),
|
||||
&v, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"set_source_volume() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
|
||||
op = pa_context_set_source_mute_by_index (g->context,
|
||||
pa_stream_get_index (pa->stream),
|
||||
sw->vol.mute, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"set_source_mute() failed\n");
|
||||
} else {
|
||||
pa_operation_unref (op);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
}
|
||||
}
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* common */
|
||||
static void *qpa_audio_init (void)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
|
||||
g->mainloop = pa_threaded_mainloop_new ();
|
||||
if (!g->mainloop) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
|
||||
if (!g->context) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback (g->context, context_state_cb, g);
|
||||
|
||||
if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"pa_context_connect() failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
if (pa_threaded_mainloop_start (g->mainloop) < 0) {
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
pa_context_state_t state;
|
||||
|
||||
state = pa_context_get_state (g->context);
|
||||
|
||||
if (state == PA_CONTEXT_READY) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD (state)) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"Wrong context state\n");
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
/* Wait until the context is ready */
|
||||
pa_threaded_mainloop_wait (g->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
|
||||
return &glob_paaudio;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
fail:
|
||||
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
|
||||
return NULL;
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void qpa_audio_fini (void *opaque)
|
||||
{
|
||||
paaudio *g = opaque;
|
||||
|
||||
if (g->mainloop) {
|
||||
pa_threaded_mainloop_stop (g->mainloop);
|
||||
}
|
||||
|
||||
if (g->context) {
|
||||
pa_context_disconnect (g->context);
|
||||
pa_context_unref (g->context);
|
||||
g->context = NULL;
|
||||
}
|
||||
|
||||
if (g->mainloop) {
|
||||
pa_threaded_mainloop_free (g->mainloop);
|
||||
}
|
||||
|
||||
g->mainloop = NULL;
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
struct audio_option qpa_options[] = {
|
||||
{
|
||||
.name = "SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_paaudio.samples,
|
||||
.valp = &conf.samples,
|
||||
.descr = "buffer size in samples"
|
||||
},
|
||||
{
|
||||
.name = "SERVER",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_paaudio.server,
|
||||
.valp = &conf.server,
|
||||
.descr = "server address"
|
||||
},
|
||||
{
|
||||
.name = "SINK",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_paaudio.sink,
|
||||
.valp = &conf.sink,
|
||||
.descr = "sink device name"
|
||||
},
|
||||
{
|
||||
.name = "SOURCE",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_paaudio.source,
|
||||
.valp = &conf.source,
|
||||
.descr = "source device name"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
@@ -937,6 +521,5 @@ struct audio_driver pa_audio_driver = {
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof (PAVoiceOut),
|
||||
.voice_size_in = sizeof (PAVoiceIn),
|
||||
.ctl_caps = VOICE_VOLUME_CAP
|
||||
.voice_size_in = sizeof (PAVoiceIn)
|
||||
};
|
||||
@@ -202,26 +202,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
|
||||
}
|
||||
spice_server_playback_stop (&out->sin);
|
||||
break;
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
SWVoiceOut *sw;
|
||||
va_list ap;
|
||||
uint16_t vol[2];
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceOut *);
|
||||
va_end (ap);
|
||||
|
||||
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
|
||||
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
|
||||
spice_server_playback_set_volume (&out->sin, 2, vol);
|
||||
spice_server_playback_set_mute (&out->sin, sw->vol.mute);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -323,26 +304,7 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||
in->active = 0;
|
||||
spice_server_record_stop (&in->sin);
|
||||
break;
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
|
||||
SWVoiceIn *sw;
|
||||
va_list ap;
|
||||
uint16_t vol[2];
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceIn *);
|
||||
va_end (ap);
|
||||
|
||||
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
|
||||
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
|
||||
spice_server_record_set_volume (&in->sin, 2, vol);
|
||||
spice_server_record_set_mute (&in->sin, sw->vol.mute);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -375,9 +337,6 @@ struct audio_driver spice_audio_driver = {
|
||||
.max_voices_in = 1,
|
||||
.voice_size_out = sizeof (SpiceVoiceOut),
|
||||
.voice_size_in = sizeof (SpiceVoiceIn),
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
.ctl_caps = VOICE_VOLUME_CAP
|
||||
#endif
|
||||
};
|
||||
|
||||
void qemu_spice_audio_init (void)
|
||||
|
||||
@@ -349,15 +349,21 @@ static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
else {
|
||||
hw->poll_mode = 0;
|
||||
}
|
||||
wave->paused = 0;
|
||||
if (wave->paused) {
|
||||
mr = waveOutRestart (wave->hwo);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutRestart");
|
||||
}
|
||||
wave->paused = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (!wave->paused) {
|
||||
mr = waveOutReset (wave->hwo);
|
||||
mr = waveOutPause (wave->hwo);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutReset");
|
||||
winwave_logerr (mr, "waveOutPause");
|
||||
}
|
||||
else {
|
||||
wave->paused = 1;
|
||||
|
||||
@@ -100,19 +100,31 @@ BalloonInfo *qmp_query_balloon(Error **errp)
|
||||
return info;
|
||||
}
|
||||
|
||||
void qmp_balloon(int64_t value, Error **errp)
|
||||
/**
|
||||
* do_balloon(): Request VM to change its memory allocation
|
||||
*/
|
||||
int do_balloon(Monitor *mon, const QDict *params,
|
||||
MonitorCompletion cb, void *opaque)
|
||||
{
|
||||
int64_t target;
|
||||
int ret;
|
||||
|
||||
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
||||
error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
|
||||
return;
|
||||
qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value <= 0) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size");
|
||||
return;
|
||||
target = qdict_get_int(params, "value");
|
||||
if (target <= 0) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qemu_balloon(value) == 0) {
|
||||
error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon");
|
||||
ret = qemu_balloon(target);
|
||||
if (ret == 0) {
|
||||
qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cb(opaque, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -24,4 +24,7 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
|
||||
QEMUBalloonStatus *stat_func, void *opaque);
|
||||
void qemu_remove_balloon_handler(void *opaque);
|
||||
|
||||
int do_balloon(Monitor *mon, const QDict *params,
|
||||
MonitorCompletion cb, void *opaque);
|
||||
|
||||
#endif
|
||||
+47
-28
@@ -9,8 +9,6 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
@@ -18,6 +16,7 @@
|
||||
#include "hw/hw.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "monitor.h"
|
||||
#include "block-migration.h"
|
||||
#include "migration.h"
|
||||
#include "blockdev.h"
|
||||
@@ -203,7 +202,8 @@ static void blk_mig_read_cb(void *opaque, int ret)
|
||||
assert(block_mig_state.submitted >= 0);
|
||||
}
|
||||
|
||||
static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
|
||||
BlkMigDevState *bmds)
|
||||
{
|
||||
int64_t total_sectors = bmds->total_sectors;
|
||||
int64_t cur_sector = bmds->cur_sector;
|
||||
@@ -251,12 +251,22 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
|
||||
blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
|
||||
nr_sectors, blk_mig_read_cb, blk);
|
||||
if (!blk->aiocb) {
|
||||
goto error;
|
||||
}
|
||||
block_mig_state.submitted++;
|
||||
|
||||
bdrv_reset_dirty(bs, cur_sector, nr_sectors);
|
||||
bmds->cur_sector = cur_sector + nr_sectors;
|
||||
|
||||
return (bmds->cur_sector >= total_sectors);
|
||||
|
||||
error:
|
||||
monitor_printf(mon, "Error reading sector %" PRId64 "\n", cur_sector);
|
||||
qemu_file_set_error(f, -EIO);
|
||||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_dirty_tracking(int enable)
|
||||
@@ -270,6 +280,7 @@ static void set_dirty_tracking(int enable)
|
||||
|
||||
static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
BlkMigDevState *bmds;
|
||||
int64_t sectors;
|
||||
|
||||
@@ -292,17 +303,19 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
|
||||
if (bmds->shared_base) {
|
||||
DPRINTF("Start migration for %s with shared base image\n",
|
||||
bs->device_name);
|
||||
monitor_printf(mon, "Start migration for %s with shared base "
|
||||
"image\n",
|
||||
bs->device_name);
|
||||
} else {
|
||||
DPRINTF("Start full migration for %s\n", bs->device_name);
|
||||
monitor_printf(mon, "Start full migration for %s\n",
|
||||
bs->device_name);
|
||||
}
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_blk_migration(QEMUFile *f)
|
||||
static void init_blk_migration(Monitor *mon, QEMUFile *f)
|
||||
{
|
||||
block_mig_state.submitted = 0;
|
||||
block_mig_state.read_done = 0;
|
||||
@@ -313,10 +326,10 @@ static void init_blk_migration(QEMUFile *f)
|
||||
block_mig_state.total_time = 0;
|
||||
block_mig_state.reads = 0;
|
||||
|
||||
bdrv_iterate(init_blk_migration_it, NULL);
|
||||
bdrv_iterate(init_blk_migration_it, mon);
|
||||
}
|
||||
|
||||
static int blk_mig_save_bulked_block(QEMUFile *f)
|
||||
static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
|
||||
{
|
||||
int64_t completed_sector_sum = 0;
|
||||
BlkMigDevState *bmds;
|
||||
@@ -325,7 +338,7 @@ static int blk_mig_save_bulked_block(QEMUFile *f)
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (bmds->bulk_completed == 0) {
|
||||
if (mig_save_device_bulk(f, bmds) == 1) {
|
||||
if (mig_save_device_bulk(mon, f, bmds) == 1) {
|
||||
/* completed bulk section for this device */
|
||||
bmds->bulk_completed = 1;
|
||||
}
|
||||
@@ -347,7 +360,8 @@ static int blk_mig_save_bulked_block(QEMUFile *f)
|
||||
block_mig_state.prev_progress = progress;
|
||||
qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
|
||||
| BLK_MIG_FLAG_PROGRESS);
|
||||
DPRINTF("Completed %d %%\r", progress);
|
||||
monitor_printf(mon, "Completed %d %%\r", progress);
|
||||
monitor_flush(mon);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -362,8 +376,8 @@ static void blk_mig_reset_dirty_cursor(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
int is_async)
|
||||
static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
|
||||
BlkMigDevState *bmds, int is_async)
|
||||
{
|
||||
BlkMigBlock *blk;
|
||||
int64_t total_sectors = bmds->total_sectors;
|
||||
@@ -373,7 +387,7 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
|
||||
for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
|
||||
if (bmds_aio_inflight(bmds, sector)) {
|
||||
bdrv_drain_all();
|
||||
qemu_aio_flush();
|
||||
}
|
||||
if (bdrv_get_dirty(bmds->bs, sector)) {
|
||||
|
||||
@@ -399,6 +413,9 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
|
||||
blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
|
||||
nr_sectors, blk_mig_read_cb, blk);
|
||||
if (!blk->aiocb) {
|
||||
goto error;
|
||||
}
|
||||
block_mig_state.submitted++;
|
||||
bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
|
||||
} else {
|
||||
@@ -422,20 +439,20 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
return (bmds->cur_dirty >= bmds->total_sectors);
|
||||
|
||||
error:
|
||||
DPRINTF("Error reading sector %" PRId64 "\n", sector);
|
||||
monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector);
|
||||
qemu_file_set_error(f, ret);
|
||||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
||||
static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
int ret = 0;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (mig_save_device_dirty(f, bmds, is_async) == 0) {
|
||||
if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
@@ -514,7 +531,7 @@ static int is_stage2_completed(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blk_mig_cleanup(void)
|
||||
static void blk_mig_cleanup(Monitor *mon)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
BlkMigBlock *blk;
|
||||
@@ -534,9 +551,11 @@ static void blk_mig_cleanup(void)
|
||||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
}
|
||||
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -544,7 +563,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
stage, block_mig_state.submitted, block_mig_state.transferred);
|
||||
|
||||
if (stage < 0) {
|
||||
blk_mig_cleanup();
|
||||
blk_mig_cleanup(mon);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -555,7 +574,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
}
|
||||
|
||||
if (stage == 1) {
|
||||
init_blk_migration(f);
|
||||
init_blk_migration(mon, f);
|
||||
|
||||
/* start track dirty blocks */
|
||||
set_dirty_tracking(1);
|
||||
@@ -565,7 +584,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
blk_mig_cleanup(mon);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -578,12 +597,12 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
qemu_file_get_rate_limit(f)) {
|
||||
if (block_mig_state.bulk_completed == 0) {
|
||||
/* first finish the bulk phase */
|
||||
if (blk_mig_save_bulked_block(f) == 0) {
|
||||
if (blk_mig_save_bulked_block(mon, f) == 0) {
|
||||
/* finished saving bulk on all devices */
|
||||
block_mig_state.bulk_completed = 1;
|
||||
}
|
||||
} else {
|
||||
if (blk_mig_save_dirty_block(f, 1) == 0) {
|
||||
if (blk_mig_save_dirty_block(mon, f, 1) == 0) {
|
||||
/* no more dirty blocks */
|
||||
break;
|
||||
}
|
||||
@@ -594,7 +613,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
blk_mig_cleanup(mon);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -604,8 +623,8 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
all async read completed */
|
||||
assert(block_mig_state.submitted == 0);
|
||||
|
||||
while (blk_mig_save_dirty_block(f, 0) != 0);
|
||||
blk_mig_cleanup();
|
||||
while (blk_mig_save_dirty_block(mon, f, 0) != 0);
|
||||
blk_mig_cleanup(mon);
|
||||
|
||||
/* report completion */
|
||||
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
||||
@@ -615,7 +634,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DPRINTF("Block migration completed\n");
|
||||
monitor_printf(mon, "Block migration completed\n");
|
||||
}
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
@@ -15,21 +15,14 @@ typedef struct BlockDriverInfo {
|
||||
int cluster_size;
|
||||
/* offset at which the VM state can be saved (0 if not possible) */
|
||||
int64_t vm_state_offset;
|
||||
bool is_dirty;
|
||||
} BlockDriverInfo;
|
||||
|
||||
typedef struct BlockFragInfo {
|
||||
uint64_t allocated_clusters;
|
||||
uint64_t total_clusters;
|
||||
uint64_t fragmented_clusters;
|
||||
} BlockFragInfo;
|
||||
|
||||
typedef struct QEMUSnapshotInfo {
|
||||
char id_str[128]; /* unique snapshot id */
|
||||
/* the following fields are informative. They are not needed for
|
||||
the consistency of the snapshot */
|
||||
char name[256]; /* user chosen name */
|
||||
uint64_t vm_state_size; /* VM state info size */
|
||||
char name[256]; /* user choosen name */
|
||||
uint32_t vm_state_size; /* VM state info size */
|
||||
uint32_t date_sec; /* UTC date of the snapshot */
|
||||
uint32_t date_nsec;
|
||||
uint64_t vm_clock_nsec; /* VM clock relative to boot */
|
||||
@@ -77,8 +70,6 @@ typedef struct BlockDevOps {
|
||||
#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */
|
||||
#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */
|
||||
#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */
|
||||
#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */
|
||||
#define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */
|
||||
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
|
||||
|
||||
@@ -93,25 +84,20 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
|
||||
} BlockQMPEventAction;
|
||||
} BlockMonEventAction;
|
||||
|
||||
void bdrv_iostatus_enable(BlockDriverState *bs);
|
||||
void bdrv_iostatus_reset(BlockDriverState *bs);
|
||||
void bdrv_iostatus_disable(BlockDriverState *bs);
|
||||
bool bdrv_iostatus_is_enabled(const BlockDriverState *bs);
|
||||
void bdrv_iostatus_set_err(BlockDriverState *bs, int error);
|
||||
void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
|
||||
BlockQMPEventAction action, int is_read);
|
||||
void bdrv_mon_event(const BlockDriverState *bdrv,
|
||||
BlockMonEventAction action, int is_read);
|
||||
void bdrv_info_print(Monitor *mon, const QObject *data);
|
||||
void bdrv_info(Monitor *mon, QObject **ret_data);
|
||||
void bdrv_stats_print(Monitor *mon, const QObject *data);
|
||||
void bdrv_info_stats(Monitor *mon, QObject **ret_data);
|
||||
|
||||
/* disk I/O throttling */
|
||||
void bdrv_io_limits_enable(BlockDriverState *bs);
|
||||
void bdrv_io_limits_disable(BlockDriverState *bs);
|
||||
bool bdrv_io_limits_enabled(BlockDriverState *bs);
|
||||
|
||||
void bdrv_init(void);
|
||||
void bdrv_init_with_whitelist(void);
|
||||
BlockDriver *bdrv_find_protocol(const char *filename);
|
||||
@@ -122,7 +108,6 @@ int bdrv_create(BlockDriver *drv, const char* filename,
|
||||
int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
|
||||
BlockDriverState *bdrv_new(const char *device_name);
|
||||
void bdrv_make_anon(BlockDriverState *bs);
|
||||
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
|
||||
void bdrv_delete(BlockDriverState *bs);
|
||||
int bdrv_parse_cache_flags(const char *mode, int *flags);
|
||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
|
||||
@@ -151,29 +136,15 @@ int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
|
||||
const void *buf, int count);
|
||||
int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
|
||||
int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
/*
|
||||
* Efficiently zero a region of the disk image. Note that this is a regular
|
||||
* I/O request like read or write and should have a reasonable size. This
|
||||
* function is not suitable for zeroing the entire image in a single request
|
||||
* because it may allocate memory for the entire region.
|
||||
*/
|
||||
int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors);
|
||||
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
||||
const char *backing_file);
|
||||
int bdrv_truncate(BlockDriverState *bs, int64_t offset);
|
||||
int64_t bdrv_getlength(BlockDriverState *bs);
|
||||
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
|
||||
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
|
||||
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
|
||||
int bdrv_commit(BlockDriverState *bs);
|
||||
int bdrv_commit_all(void);
|
||||
void bdrv_commit_all(void);
|
||||
int bdrv_change_backing_file(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt);
|
||||
void bdrv_register(BlockDriver *bdrv);
|
||||
@@ -183,12 +154,13 @@ typedef struct BdrvCheckResult {
|
||||
int corruptions;
|
||||
int leaks;
|
||||
int check_errors;
|
||||
BlockFragInfo bfi;
|
||||
} BdrvCheckResult;
|
||||
|
||||
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
|
||||
|
||||
/* async block I/O */
|
||||
typedef struct BlockDriverAIOCB BlockDriverAIOCB;
|
||||
typedef void BlockDriverCompletionFunc(void *opaque, int ret);
|
||||
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
|
||||
int sector_num);
|
||||
BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -229,14 +201,11 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
||||
void bdrv_invalidate_cache(BlockDriverState *bs);
|
||||
void bdrv_invalidate_cache_all(void);
|
||||
|
||||
void bdrv_clear_incoming_migration_all(void);
|
||||
|
||||
/* Ensure contents are flushed to disk. */
|
||||
int bdrv_flush(BlockDriverState *bs);
|
||||
int coroutine_fn bdrv_co_flush(BlockDriverState *bs);
|
||||
void bdrv_flush_all(void);
|
||||
void bdrv_close_all(void);
|
||||
void bdrv_drain_all(void);
|
||||
|
||||
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
|
||||
int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
|
||||
@@ -262,17 +231,9 @@ typedef enum FDriveType {
|
||||
FDRIVE_DRV_NONE = 0x03, /* No drive connected */
|
||||
} FDriveType;
|
||||
|
||||
typedef enum FDriveRate {
|
||||
FDRIVE_RATE_500K = 0x00, /* 500 Kbps */
|
||||
FDRIVE_RATE_300K = 0x01, /* 300 Kbps */
|
||||
FDRIVE_RATE_250K = 0x02, /* 250 Kbps */
|
||||
FDRIVE_RATE_1M = 0x03, /* 1 Mbps */
|
||||
} FDriveRate;
|
||||
|
||||
void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
|
||||
int *max_track, int *last_sect,
|
||||
FDriveType drive_in, FDriveType *drive,
|
||||
FDriveRate *rate);
|
||||
FDriveType drive_in, FDriveType *drive);
|
||||
int bdrv_get_translation_hint(BlockDriverState *bs);
|
||||
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
|
||||
BlockErrorAction on_write_error);
|
||||
@@ -283,7 +244,7 @@ int bdrv_enable_write_cache(BlockDriverState *bs);
|
||||
int bdrv_is_inserted(BlockDriverState *bs);
|
||||
int bdrv_media_changed(BlockDriverState *bs);
|
||||
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
|
||||
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
|
||||
void bdrv_eject(BlockDriverState *bs, int eject_flag);
|
||||
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
|
||||
BlockDriverState *bdrv_find(const char *name);
|
||||
BlockDriverState *bdrv_next(BlockDriverState *bs);
|
||||
@@ -303,8 +264,6 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
|
||||
void bdrv_get_backing_filename(BlockDriverState *bs,
|
||||
char *filename, int filename_size);
|
||||
void bdrv_get_full_backing_filename(BlockDriverState *bs,
|
||||
char *dest, size_t sz);
|
||||
int bdrv_can_snapshot(BlockDriverState *bs);
|
||||
int bdrv_is_snapshot(BlockDriverState *bs);
|
||||
BlockDriverState *bdrv_snapshots(void);
|
||||
@@ -346,9 +305,6 @@ void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors);
|
||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
|
||||
|
||||
void bdrv_enable_copy_on_read(BlockDriverState *bs);
|
||||
void bdrv_disable_copy_on_read(BlockDriverState *bs);
|
||||
|
||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
||||
int bdrv_in_use(BlockDriverState *bs);
|
||||
|
||||
@@ -448,10 +404,10 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
|
||||
|
||||
#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \
|
||||
DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \
|
||||
DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \
|
||||
_conf.logical_block_size, 512), \
|
||||
DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \
|
||||
_conf.physical_block_size, 512), \
|
||||
DEFINE_PROP_UINT16("logical_block_size", _state, \
|
||||
_conf.logical_block_size, 512), \
|
||||
DEFINE_PROP_UINT16("physical_block_size", _state, \
|
||||
_conf.physical_block_size, 512), \
|
||||
DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \
|
||||
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
|
||||
DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \
|
||||
|
||||
+9
-2
@@ -292,10 +292,10 @@ static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config = g_strdup(filename);
|
||||
config = strdup(filename);
|
||||
config[c - filename] = '\0';
|
||||
ret = read_config(s, config);
|
||||
g_free(config);
|
||||
free(config);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -397,6 +397,12 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return bdrv_aio_flush(bs->file, cb, opaque);
|
||||
}
|
||||
|
||||
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
BlkdebugVars *old_vars)
|
||||
{
|
||||
@@ -446,6 +452,7 @@ static BlockDriver bdrv_blkdebug = {
|
||||
|
||||
.bdrv_aio_readv = blkdebug_aio_readv,
|
||||
.bdrv_aio_writev = blkdebug_aio_writev,
|
||||
.bdrv_aio_flush = blkdebug_aio_flush,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
};
|
||||
|
||||
+18
-10
@@ -87,10 +87,10 @@ static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
raw = g_strdup(filename);
|
||||
raw = strdup(filename);
|
||||
raw[c - filename] = '\0';
|
||||
ret = bdrv_file_open(&bs->file, raw, flags);
|
||||
g_free(raw);
|
||||
free(raw);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -310,10 +310,14 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
|
||||
blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
|
||||
|
||||
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb)) {
|
||||
blkverify_aio_cb(acb, -EIO);
|
||||
}
|
||||
if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb)) {
|
||||
blkverify_aio_cb(acb, -EIO);
|
||||
}
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
@@ -325,10 +329,14 @@ static BlockDriverAIOCB *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, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb)) {
|
||||
blkverify_aio_cb(acb, -EIO);
|
||||
}
|
||||
if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb)) {
|
||||
blkverify_aio_cb(acb, -EIO);
|
||||
}
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
|
||||
+46
-52
@@ -64,26 +64,15 @@ static int cow_open(BlockDriverState *bs, int flags)
|
||||
struct cow_header_v2 cow_header;
|
||||
int bitmap_size;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
/* see if it is a cow image */
|
||||
ret = bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header));
|
||||
if (ret < 0) {
|
||||
if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) !=
|
||||
sizeof(cow_header)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version),
|
||||
"COW version %d", cow_header.version);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "cow", version);
|
||||
ret = -ENOTSUP;
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
|
||||
be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -99,11 +88,11 @@ static int cow_open(BlockDriverState *bs, int flags)
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
fail:
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX(hch): right now these functions are extremely inefficient.
|
||||
* XXX(hch): right now these functions are extremly ineffcient.
|
||||
* We should just read the whole bitmap we'll need in one go instead.
|
||||
*/
|
||||
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum)
|
||||
@@ -143,8 +132,8 @@ static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
|
||||
/* Return true if first block has been changed (ie. current version is
|
||||
* in COW file). Set the number of continuous blocks for which that
|
||||
* is true. */
|
||||
static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *num_same)
|
||||
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *num_same)
|
||||
{
|
||||
int changed;
|
||||
|
||||
@@ -182,30 +171,28 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
static int cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, n;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (bdrv_co_is_allocated(bs, sector_num, nb_sectors, &n)) {
|
||||
if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) {
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->cow_sectors_offset + sector_num * 512,
|
||||
buf, n * 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
} else {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
memset(buf, 0, n * 512);
|
||||
}
|
||||
memset(buf, 0, n * 512);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
@@ -233,9 +220,8 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512,
|
||||
buf, nb_sectors * 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != nb_sectors * 512)
|
||||
return -1;
|
||||
|
||||
return cow_update_bitmap(bs, sector_num, nb_sectors);
|
||||
}
|
||||
@@ -257,12 +243,12 @@ static void cow_close(BlockDriverState *bs)
|
||||
|
||||
static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
{
|
||||
int fd, cow_fd;
|
||||
struct cow_header_v2 cow_header;
|
||||
struct stat st;
|
||||
int64_t image_sectors = 0;
|
||||
const char *image_filename = NULL;
|
||||
int ret;
|
||||
BlockDriverState *cow_bs;
|
||||
|
||||
/* Read out options */
|
||||
while (options && options->name) {
|
||||
@@ -274,16 +260,10 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, options);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&cow_bs, filename, BDRV_O_RDWR);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (cow_fd < 0)
|
||||
return -errno;
|
||||
memset(&cow_header, 0, sizeof(cow_header));
|
||||
cow_header.magic = cpu_to_be32(COW_MAGIC);
|
||||
cow_header.version = cpu_to_be32(COW_VERSION);
|
||||
@@ -291,9 +271,16 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
/* Note: if no file, we put a dummy mtime */
|
||||
cow_header.mtime = cpu_to_be32(0);
|
||||
|
||||
if (stat(image_filename, &st) != 0) {
|
||||
fd = open(image_filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
close(cow_fd);
|
||||
goto mtime_fail;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
goto mtime_fail;
|
||||
}
|
||||
close(fd);
|
||||
cow_header.mtime = cpu_to_be32(st.st_mtime);
|
||||
mtime_fail:
|
||||
pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
|
||||
@@ -301,23 +288,29 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
cow_header.sectorsize = cpu_to_be32(512);
|
||||
cow_header.size = cpu_to_be64(image_sectors * 512);
|
||||
ret = bdrv_pwrite(cow_bs, 0, &cow_header, sizeof(cow_header));
|
||||
if (ret < 0) {
|
||||
ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header));
|
||||
if (ret != sizeof(cow_header)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* resize to include at least all the bitmap */
|
||||
ret = bdrv_truncate(cow_bs,
|
||||
sizeof(cow_header) + ((image_sectors + 7) >> 3));
|
||||
if (ret < 0) {
|
||||
ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
bdrv_delete(cow_bs);
|
||||
close(cow_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int cow_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
static QEMUOptionParameter cow_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
@@ -343,7 +336,8 @@ static BlockDriver bdrv_cow = {
|
||||
|
||||
.bdrv_read = cow_co_read,
|
||||
.bdrv_write = cow_co_write,
|
||||
.bdrv_co_is_allocated = cow_co_is_allocated,
|
||||
.bdrv_co_flush_to_disk = cow_co_flush,
|
||||
.bdrv_is_allocated = cow_is_allocated,
|
||||
|
||||
.create_options = cow_create_options,
|
||||
};
|
||||
|
||||
+11
-5
@@ -89,17 +89,19 @@ 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:
|
||||
qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, s);
|
||||
qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush,
|
||||
NULL, s);
|
||||
break;
|
||||
case CURL_POLL_OUT:
|
||||
qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, s);
|
||||
qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush,
|
||||
NULL, s);
|
||||
break;
|
||||
case CURL_POLL_INOUT:
|
||||
qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do,
|
||||
curl_aio_flush, s);
|
||||
curl_aio_flush, NULL, s);
|
||||
break;
|
||||
case CURL_POLL_REMOVE:
|
||||
qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -280,7 +282,7 @@ static CURLState *curl_init_state(BDRVCURLState *s)
|
||||
break;
|
||||
}
|
||||
if (!state) {
|
||||
g_usleep(100);
|
||||
usleep(100);
|
||||
curl_multi_do(s);
|
||||
}
|
||||
} while(!state);
|
||||
@@ -507,6 +509,10 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
|
||||
acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
|
||||
|
||||
if (!acb) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
acb->qiov = qiov;
|
||||
acb->sector_num = sector_num;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
|
||||
+101
-444
@@ -25,12 +25,10 @@
|
||||
#include "config-host.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-error.h"
|
||||
#include "block_int.h"
|
||||
#include "trace.h"
|
||||
#include "hw/scsi-defs.h"
|
||||
|
||||
#include <iscsi/iscsi.h>
|
||||
#include <iscsi/scsi-lowlevel.h>
|
||||
@@ -39,10 +37,8 @@
|
||||
typedef struct IscsiLun {
|
||||
struct iscsi_context *iscsi;
|
||||
int lun;
|
||||
enum scsi_inquiry_peripheral_device_type type;
|
||||
int block_size;
|
||||
uint64_t num_blocks;
|
||||
int events;
|
||||
unsigned long num_blocks;
|
||||
} IscsiLun;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
@@ -65,44 +61,10 @@ struct IscsiTask {
|
||||
int complete;
|
||||
};
|
||||
|
||||
static void
|
||||
iscsi_bh_cb(void *p)
|
||||
{
|
||||
IscsiAIOCB *acb = p;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
|
||||
if (acb->canceled == 0) {
|
||||
acb->common.cb(acb->common.opaque, acb->status);
|
||||
}
|
||||
|
||||
if (acb->task != NULL) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_schedule_bh(IscsiAIOCB *acb)
|
||||
{
|
||||
if (acb->bh) {
|
||||
return;
|
||||
}
|
||||
acb->bh = qemu_bh_new(iscsi_bh_cb, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
void *private_data)
|
||||
{
|
||||
IscsiAIOCB *acb = private_data;
|
||||
|
||||
acb->status = -ECANCELED;
|
||||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -111,19 +73,15 @@ iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
|
||||
IscsiLun *iscsilun = acb->iscsilun;
|
||||
|
||||
if (acb->status != -EINPROGRESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
acb->common.cb(acb->common.opaque, -ECANCELED);
|
||||
acb->canceled = 1;
|
||||
|
||||
/* send a task mgmt call to the target to cancel the task on the target */
|
||||
iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
||||
iscsi_abort_task_cb, acb);
|
||||
iscsi_abort_task_cb, NULL);
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
/* then also cancel the task locally in libiscsi */
|
||||
iscsi_scsi_task_cancel(iscsilun->iscsi, acb->task);
|
||||
}
|
||||
|
||||
static AIOPool iscsi_aio_pool = {
|
||||
@@ -146,27 +104,11 @@ static void
|
||||
iscsi_set_events(IscsiLun *iscsilun)
|
||||
{
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
int ev;
|
||||
|
||||
/* We always register a read handler. */
|
||||
ev = POLLIN;
|
||||
ev |= iscsi_which_events(iscsi);
|
||||
if (ev != iscsilun->events) {
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi),
|
||||
iscsi_process_read,
|
||||
(ev & POLLOUT) ? iscsi_process_write : NULL,
|
||||
iscsi_process_flush,
|
||||
iscsilun);
|
||||
|
||||
}
|
||||
|
||||
/* If we just added an event, the callback might be delayed
|
||||
* unless we call qemu_notify_event().
|
||||
*/
|
||||
if (ev & ~iscsilun->events) {
|
||||
qemu_notify_event();
|
||||
}
|
||||
iscsilun->events = ev;
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
|
||||
(iscsi_which_events(iscsi) & POLLOUT)
|
||||
? iscsi_process_write : NULL,
|
||||
iscsi_process_flush, NULL, iscsilun);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -190,28 +132,61 @@ iscsi_process_write(void *arg)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
iscsi_schedule_bh(QEMUBHFunc *cb, IscsiAIOCB *acb)
|
||||
{
|
||||
acb->bh = qemu_bh_new(cb, acb);
|
||||
if (!acb->bh) {
|
||||
error_report("oom: could not create iscsi bh");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qemu_bh_schedule(acb->bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
|
||||
iscsi_readv_writev_bh_cb(void *p)
|
||||
{
|
||||
IscsiAIOCB *acb = p;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
|
||||
if (acb->canceled == 0) {
|
||||
acb->common.cb(acb->common.opaque, acb->status);
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled);
|
||||
trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled);
|
||||
|
||||
g_free(acb->buf);
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
qemu_aio_release(acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
error_report("Failed to write16 data to iSCSI lun. %s",
|
||||
error_report("Failed to write10 data to iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(acb);
|
||||
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
|
||||
@@ -229,9 +204,12 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
size_t size;
|
||||
uint32_t num_sectors;
|
||||
uint64_t lba;
|
||||
struct iscsi_data data;
|
||||
int fua = 0;
|
||||
|
||||
/* set FUA on writes when cache mode is write through */
|
||||
if (!(bs->open_flags & BDRV_O_CACHE_WB)) {
|
||||
fua = 1;
|
||||
}
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
@@ -240,47 +218,19 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
acb->qiov = qiov;
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
/* XXX we should pass the iovec to write16 to avoid the extra copy */
|
||||
/* XXX we should pass the iovec to write10 to avoid the extra copy */
|
||||
/* this will allow us to get rid of 'buf' completely */
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
acb->buf = g_malloc(size);
|
||||
qemu_iovec_to_buffer(acb->qiov, acb->buf);
|
||||
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
fua, 0, iscsilun->block_size,
|
||||
iscsi_aio_write10_cb, acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi WRITE16 "
|
||||
"command. %s", iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
||||
acb->task->xfer_dir = SCSI_XFER_WRITE;
|
||||
acb->task->cdb_size = 16;
|
||||
acb->task->cdb[0] = 0x8a;
|
||||
if (!(bs->open_flags & BDRV_O_CACHE_WB)) {
|
||||
/* set FUA on writes when cache mode is write through */
|
||||
acb->task->cdb[1] |= 0x04;
|
||||
}
|
||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
*(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
|
||||
*(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
|
||||
num_sectors = size / iscsilun->block_size;
|
||||
*(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
|
||||
acb->task->expxferlen = size;
|
||||
|
||||
data.data = acb->buf;
|
||||
data.size = size;
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
|
||||
iscsi_aio_write16_cb,
|
||||
&data,
|
||||
acb) != 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
error_report("iSCSI: Failed to send write10 command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
g_free(acb->buf);
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
@@ -292,25 +242,30 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
|
||||
iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
trace_iscsi_aio_read16_cb(iscsi, status, acb, acb->canceled);
|
||||
trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled);
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
qemu_aio_release(acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status != 0) {
|
||||
error_report("Failed to read16 data from iSCSI lun. %s",
|
||||
error_report("Failed to read10 data from iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(acb);
|
||||
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
@@ -322,10 +277,8 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
size_t qemu_read_size;
|
||||
size_t qemu_read_size, lun_read_size;
|
||||
int i;
|
||||
uint64_t lba;
|
||||
uint32_t num_sectors;
|
||||
|
||||
qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
|
||||
|
||||
@@ -336,8 +289,6 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
acb->qiov = qiov;
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
acb->read_size = qemu_read_size;
|
||||
acb->buf = NULL;
|
||||
|
||||
@@ -352,44 +303,16 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
acb->read_offset = bdrv_offset % iscsilun->block_size;
|
||||
}
|
||||
|
||||
num_sectors = (qemu_read_size + iscsilun->block_size
|
||||
+ acb->read_offset - 1)
|
||||
/ iscsilun->block_size;
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
lun_read_size = (qemu_read_size + iscsilun->block_size
|
||||
+ acb->read_offset - 1)
|
||||
/ iscsilun->block_size * iscsilun->block_size;
|
||||
acb->task = iscsi_read10_task(iscsi, iscsilun->lun,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
lun_read_size, iscsilun->block_size,
|
||||
iscsi_aio_read10_cb, acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi READ16 "
|
||||
"command. %s", iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
||||
acb->task->xfer_dir = SCSI_XFER_READ;
|
||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
acb->task->expxferlen = qemu_read_size;
|
||||
|
||||
switch (iscsilun->type) {
|
||||
case TYPE_DISK:
|
||||
acb->task->cdb_size = 16;
|
||||
acb->task->cdb[0] = 0x88;
|
||||
*(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
|
||||
*(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
|
||||
*(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
|
||||
break;
|
||||
default:
|
||||
acb->task->cdb_size = 10;
|
||||
acb->task->cdb[0] = 0x28;
|
||||
*(uint32_t *)&acb->task->cdb[2] = htonl(lba);
|
||||
*(uint16_t *)&acb->task->cdb[7] = htons(num_sectors);
|
||||
break;
|
||||
}
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
|
||||
iscsi_aio_read16_cb,
|
||||
NULL,
|
||||
acb) != 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
error_report("iSCSI: Failed to send read10 command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
@@ -413,6 +336,9 @@ iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
qemu_aio_release(acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -423,7 +349,9 @@ iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(acb);
|
||||
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
@@ -438,8 +366,6 @@ iscsi_aio_flush(BlockDriverState *bs,
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun,
|
||||
0, 0, 0, 0,
|
||||
@@ -457,62 +383,6 @@ iscsi_aio_flush(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
error_report("Failed to unmap data on iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
struct unmap_list list[1];
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
list[0].lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size;
|
||||
|
||||
acb->task = iscsi_unmap_task(iscsi, iscsilun->lun,
|
||||
0, 0, &list[0], 1,
|
||||
iscsi_unmap_cb,
|
||||
acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send unmap command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
iscsi_getlength(BlockDriverState *bs)
|
||||
{
|
||||
@@ -525,42 +395,6 @@ iscsi_getlength(BlockDriverState *bs)
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
struct IscsiTask *itask = opaque;
|
||||
struct scsi_readcapacity16 *rc16;
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
if (status != 0) {
|
||||
error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
rc16 = scsi_datain_unmarshall(task);
|
||||
if (rc16 == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
itask->iscsilun->block_size = rc16->block_length;
|
||||
itask->iscsilun->num_blocks = rc16->returned_lba + 1;
|
||||
itask->bs->total_sectors = itask->iscsilun->num_blocks *
|
||||
itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
|
||||
|
||||
itask->status = 0;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
@@ -588,70 +422,15 @@ iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
|
||||
}
|
||||
|
||||
itask->iscsilun->block_size = rc10->block_size;
|
||||
itask->iscsilun->num_blocks = rc10->lba + 1;
|
||||
itask->bs->total_sectors = itask->iscsilun->num_blocks *
|
||||
itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
|
||||
itask->iscsilun->num_blocks = rc10->lba;
|
||||
itask->bs->total_sectors = (uint64_t)rc10->lba *
|
||||
rc10->block_size / BDRV_SECTOR_SIZE ;
|
||||
|
||||
itask->status = 0;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
void *opaque)
|
||||
{
|
||||
struct IscsiTask *itask = opaque;
|
||||
struct scsi_task *task = command_data;
|
||||
struct scsi_inquiry_standard *inq;
|
||||
|
||||
if (status != 0) {
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
inq = scsi_datain_unmarshall(task);
|
||||
if (inq == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall inquiry data.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
itask->iscsilun->type = inq->periperal_device_type;
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
switch (itask->iscsilun->type) {
|
||||
case TYPE_DISK:
|
||||
task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun,
|
||||
iscsi_readcapacity16_cb, opaque);
|
||||
if (task == NULL) {
|
||||
error_report("iSCSI: failed to send readcapacity16 command.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TYPE_ROM:
|
||||
task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun,
|
||||
0, 0,
|
||||
iscsi_readcapacity10_cb, opaque);
|
||||
if (task == NULL) {
|
||||
error_report("iSCSI: failed to send readcapacity16 command.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
itask->status = 0;
|
||||
itask->complete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
@@ -666,120 +445,16 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
return;
|
||||
}
|
||||
|
||||
task = iscsi_inquiry_task(iscsi, itask->iscsilun->lun,
|
||||
0, 0, 36,
|
||||
iscsi_inquiry_cb, opaque);
|
||||
task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
|
||||
iscsi_readcapacity10_cb, opaque);
|
||||
if (task == NULL) {
|
||||
error_report("iSCSI: failed to send inquiry command.");
|
||||
error_report("iSCSI: failed to send readcapacity command.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_chap(struct iscsi_context *iscsi, const char *target)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
const char *user = NULL;
|
||||
const char *password = NULL;
|
||||
|
||||
list = qemu_find_opts("iscsi");
|
||||
if (!list) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
opts = qemu_opts_find(list, target);
|
||||
if (opts == NULL) {
|
||||
opts = QTAILQ_FIRST(&list->head);
|
||||
if (!opts) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
user = qemu_opt_get(opts, "user");
|
||||
if (!user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
password = qemu_opt_get(opts, "password");
|
||||
if (!password) {
|
||||
error_report("CHAP username specified but no password was given");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
||||
error_report("Failed to set initiator username and password");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
const char *digest = NULL;
|
||||
|
||||
list = qemu_find_opts("iscsi");
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
opts = qemu_opts_find(list, target);
|
||||
if (opts == NULL) {
|
||||
opts = QTAILQ_FIRST(&list->head);
|
||||
if (!opts) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
digest = qemu_opt_get(opts, "header-digest");
|
||||
if (!digest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(digest, "CRC32C")) {
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
|
||||
} else if (!strcmp(digest, "NONE")) {
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
|
||||
} else if (!strcmp(digest, "CRC32C-NONE")) {
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
|
||||
} else if (!strcmp(digest, "NONE-CRC32C")) {
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
} else {
|
||||
error_report("Invalid header-digest setting : %s", digest);
|
||||
}
|
||||
}
|
||||
|
||||
static char *parse_initiator_name(const char *target)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
const char *name = NULL;
|
||||
|
||||
list = qemu_find_opts("iscsi");
|
||||
if (!list) {
|
||||
return g_strdup("iqn.2008-11.org.linux-kvm");
|
||||
}
|
||||
|
||||
opts = qemu_opts_find(list, target);
|
||||
if (opts == NULL) {
|
||||
opts = QTAILQ_FIRST(&list->head);
|
||||
if (!opts) {
|
||||
return g_strdup("iqn.2008-11.org.linux-kvm");
|
||||
}
|
||||
}
|
||||
|
||||
name = qemu_opt_get(opts, "initiator-name");
|
||||
if (!name) {
|
||||
return g_strdup("iqn.2008-11.org.linux-kvm");
|
||||
}
|
||||
|
||||
return g_strdup(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* We support iscsi url's on the form
|
||||
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
||||
@@ -790,7 +465,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
struct iscsi_context *iscsi = NULL;
|
||||
struct iscsi_url *iscsi_url = NULL;
|
||||
struct IscsiTask task;
|
||||
char *initiator_name = NULL;
|
||||
int ret;
|
||||
|
||||
if ((BDRV_SECTOR_SIZE % 512) != 0) {
|
||||
@@ -800,6 +474,16 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
|
||||
/* Should really append the KVM name after the ':' here */
|
||||
iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
|
||||
if (iscsi == NULL) {
|
||||
error_report("iSCSI: Failed to create iSCSI context.");
|
||||
ret = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
iscsi_url = iscsi_parse_full_url(iscsi, filename);
|
||||
if (iscsi_url == NULL) {
|
||||
error_report("Failed to parse URL : %s %s", filename,
|
||||
@@ -808,17 +492,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
|
||||
initiator_name = parse_initiator_name(iscsi_url->target);
|
||||
|
||||
iscsi = iscsi_create_context(initiator_name);
|
||||
if (iscsi == NULL) {
|
||||
error_report("iSCSI: Failed to create iSCSI context.");
|
||||
ret = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
|
||||
error_report("iSCSI: Failed to set target name.");
|
||||
ret = -EINVAL;
|
||||
@@ -834,14 +507,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if we got CHAP username/password via the options */
|
||||
if (parse_chap(iscsi, iscsi_url->target) != 0) {
|
||||
error_report("iSCSI: Failed to set CHAP user/password");
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
||||
error_report("iSCSI: Failed to set session type to normal.");
|
||||
ret = -EINVAL;
|
||||
@@ -850,9 +515,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
|
||||
/* check if we got HEADER_DIGEST via the options */
|
||||
parse_header_digest(iscsi, iscsi_url->target);
|
||||
|
||||
task.iscsilun = iscsilun;
|
||||
task.status = 0;
|
||||
task.complete = 0;
|
||||
@@ -886,9 +548,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
if (initiator_name != NULL) {
|
||||
g_free(initiator_name);
|
||||
}
|
||||
if (iscsi_url != NULL) {
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
}
|
||||
@@ -904,7 +563,7 @@ static void iscsi_close(BlockDriverState *bs)
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
|
||||
iscsi_destroy_context(iscsi);
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
@@ -922,8 +581,6 @@ static BlockDriver bdrv_iscsi = {
|
||||
.bdrv_aio_readv = iscsi_aio_readv,
|
||||
.bdrv_aio_writev = iscsi_aio_writev,
|
||||
.bdrv_aio_flush = iscsi_aio_flush,
|
||||
|
||||
.bdrv_aio_discard = iscsi_aio_discard,
|
||||
};
|
||||
|
||||
static void iscsi_block_init(void)
|
||||
|
||||
+86
-304
@@ -46,25 +46,14 @@
|
||||
#define logout(fmt, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define MAX_NBD_REQUESTS 16
|
||||
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
|
||||
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
|
||||
|
||||
typedef struct BDRVNBDState {
|
||||
CoMutex lock;
|
||||
int sock;
|
||||
uint32_t nbdflags;
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
char *export_name; /* An NBD server may export several devices */
|
||||
|
||||
CoMutex send_mutex;
|
||||
CoMutex free_sema;
|
||||
Coroutine *send_coroutine;
|
||||
int in_flight;
|
||||
|
||||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
||||
struct nbd_reply reply;
|
||||
|
||||
/* If it begins with '/', this is a UNIX domain socket. Otherwise,
|
||||
* it's a string of the form <hostname|ip4|\[ip6\]>:port
|
||||
*/
|
||||
@@ -117,141 +106,6 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Poor man semaphore. The free_sema is locked when no other request
|
||||
* can be accepted, and unlocked after receiving one reply. */
|
||||
if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
|
||||
qemu_co_mutex_lock(&s->free_sema);
|
||||
assert(s->in_flight < MAX_NBD_REQUESTS);
|
||||
}
|
||||
s->in_flight++;
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i] == NULL) {
|
||||
s->recv_coroutine[i] = qemu_coroutine_self();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < MAX_NBD_REQUESTS);
|
||||
request->handle = INDEX_TO_HANDLE(s, i);
|
||||
}
|
||||
|
||||
static int nbd_have_request(void *opaque)
|
||||
{
|
||||
BDRVNBDState *s = opaque;
|
||||
|
||||
return s->in_flight > 0;
|
||||
}
|
||||
|
||||
static void nbd_reply_ready(void *opaque)
|
||||
{
|
||||
BDRVNBDState *s = opaque;
|
||||
uint64_t i;
|
||||
int ret;
|
||||
|
||||
if (s->reply.handle == 0) {
|
||||
/* No reply already in flight. Fetch a header. It is possible
|
||||
* that another thread has done the same thing in parallel, so
|
||||
* the socket is not readable anymore.
|
||||
*/
|
||||
ret = nbd_receive_reply(s->sock, &s->reply);
|
||||
if (ret == -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
if (ret < 0) {
|
||||
s->reply.handle = 0;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* There's no need for a mutex on the receive side, because the
|
||||
* handler acts as a synchronization point and ensures that only
|
||||
* one coroutine is called until the reply finishes. */
|
||||
i = HANDLE_TO_INDEX(s, s->reply.handle);
|
||||
if (i >= MAX_NBD_REQUESTS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
fail:
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nbd_restart_write(void *opaque)
|
||||
{
|
||||
BDRVNBDState *s = opaque;
|
||||
qemu_coroutine_enter(s->send_coroutine, NULL);
|
||||
}
|
||||
|
||||
static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
|
||||
struct iovec *iov, int offset)
|
||||
{
|
||||
int rc, ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->send_mutex);
|
||||
s->send_coroutine = qemu_coroutine_self();
|
||||
qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write,
|
||||
nbd_have_request, s);
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
if (rc >= 0 && iov) {
|
||||
ret = qemu_co_sendv(s->sock, iov, request->len, offset);
|
||||
if (ret != request->len) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL,
|
||||
nbd_have_request, s);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request,
|
||||
struct nbd_reply *reply,
|
||||
struct iovec *iov, int offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Wait until we're woken up by the read handler. TODO: perhaps
|
||||
* peek at the next reply and avoid yielding if it's ours? */
|
||||
qemu_coroutine_yield();
|
||||
*reply = s->reply;
|
||||
if (reply->handle != request->handle) {
|
||||
reply->error = EIO;
|
||||
} else {
|
||||
if (iov && reply->error == 0) {
|
||||
ret = qemu_co_recvv(s->sock, iov, request->len, offset);
|
||||
if (ret != request->len) {
|
||||
reply->error = EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the read handler to read another header. */
|
||||
s->reply.handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void nbd_coroutine_end(BDRVNBDState *s, struct nbd_request *request)
|
||||
{
|
||||
int i = HANDLE_TO_INDEX(s, request->handle);
|
||||
s->recv_coroutine[i] = NULL;
|
||||
if (s->in_flight-- == MAX_NBD_REQUESTS) {
|
||||
qemu_co_mutex_unlock(&s->free_sema);
|
||||
}
|
||||
}
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
@@ -267,7 +121,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
/* Failed to establish connection */
|
||||
if (sock < 0) {
|
||||
if (sock == -1) {
|
||||
logout("Failed to establish connection to NBD server\n");
|
||||
return -errno;
|
||||
}
|
||||
@@ -275,17 +129,14 @@ static int nbd_establish_connection(BlockDriverState *bs)
|
||||
/* NBD handshake */
|
||||
ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
|
||||
&blocksize);
|
||||
if (ret < 0) {
|
||||
if (ret == -1) {
|
||||
logout("Failed to negotiate with the NBD server\n");
|
||||
closesocket(sock);
|
||||
return ret;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Now that we're connected, set the socket to be non-blocking and
|
||||
* kick the reply mechanism. */
|
||||
/* Now that we're connected, set the socket to be non-blocking */
|
||||
socket_set_nonblock(sock);
|
||||
qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL,
|
||||
nbd_have_request, s);
|
||||
|
||||
s->sock = sock;
|
||||
s->size = size;
|
||||
@@ -301,11 +152,11 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
struct nbd_request request;
|
||||
|
||||
request.type = NBD_CMD_DISC;
|
||||
request.handle = (uint64_t)(intptr_t)bs;
|
||||
request.from = 0;
|
||||
request.len = 0;
|
||||
nbd_send_request(s->sock, &request);
|
||||
|
||||
qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL);
|
||||
closesocket(s->sock);
|
||||
}
|
||||
|
||||
@@ -314,9 +165,6 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
int result;
|
||||
|
||||
qemu_co_mutex_init(&s->send_mutex);
|
||||
qemu_co_mutex_init(&s->free_sema);
|
||||
|
||||
/* Pop the config into our state object. Exit if invalid. */
|
||||
result = nbd_config(s, filename, flags);
|
||||
if (result != 0) {
|
||||
@@ -328,154 +176,90 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
|
||||
*/
|
||||
result = nbd_establish_connection(bs);
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
request.type = NBD_CMD_READ;
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, qiov->iov, offset);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
|
||||
}
|
||||
|
||||
static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
request.type = NBD_CMD_WRITE;
|
||||
if (!bdrv_enable_write_cache(bs) && (s->nbdflags & NBD_FLAG_SEND_FUA)) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
||||
request.from = sector_num * 512;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, qiov->iov, offset);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
/* qemu-nbd has a limit of slightly less than 1M per request. Try to
|
||||
* remain aligned to 4K. */
|
||||
#define NBD_MAX_SECTORS 2040
|
||||
|
||||
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += NBD_MAX_SECTORS * 512;
|
||||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += NBD_MAX_SECTORS * 512;
|
||||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
static int nbd_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
if (!(s->nbdflags & NBD_FLAG_SEND_FLUSH)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
request.type = NBD_CMD_FLUSH;
|
||||
if (s->nbdflags & NBD_FLAG_SEND_FUA) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
||||
request.from = 0;
|
||||
request.len = 0;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
}
|
||||
|
||||
static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
if (!(s->nbdflags & NBD_FLAG_SEND_TRIM)) {
|
||||
return 0;
|
||||
}
|
||||
request.type = NBD_CMD_TRIM;
|
||||
request.handle = (uint64_t)(intptr_t)bs;
|
||||
request.from = sector_num * 512;;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
if (nbd_send_request(s->sock, &request) == -1)
|
||||
return -errno;
|
||||
|
||||
if (nbd_receive_reply(s->sock, &reply) == -1)
|
||||
return -errno;
|
||||
|
||||
if (reply.error !=0)
|
||||
return -reply.error;
|
||||
|
||||
if (reply.handle != request.handle)
|
||||
return -EIO;
|
||||
|
||||
if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nbd_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
|
||||
request.type = NBD_CMD_WRITE;
|
||||
request.handle = (uint64_t)(intptr_t)bs;
|
||||
request.from = sector_num * 512;;
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
if (nbd_send_request(s->sock, &request) == -1)
|
||||
return -errno;
|
||||
|
||||
if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
|
||||
return -EIO;
|
||||
|
||||
if (nbd_receive_reply(s->sock, &reply) == -1)
|
||||
return -errno;
|
||||
|
||||
if (reply.error !=0)
|
||||
return -reply.error;
|
||||
|
||||
if (reply.handle != request.handle)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int nbd_co_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = nbd_read(bs, sector_num, buf, nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int nbd_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = nbd_write(bs, sector_num, buf, nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nbd_close(BlockDriverState *bs)
|
||||
@@ -495,16 +279,14 @@ static int64_t nbd_getlength(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_nbd = {
|
||||
.format_name = "nbd",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
.bdrv_co_writev = nbd_co_writev,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.protocol_name = "nbd",
|
||||
.format_name = "nbd",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_read = nbd_co_read,
|
||||
.bdrv_write = nbd_co_write,
|
||||
.bdrv_close = nbd_close,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.protocol_name = "nbd",
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
|
||||
+50
-78
@@ -95,13 +95,11 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
static int qcow_open(BlockDriverState *bs, int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, shift, ret;
|
||||
int len, i, shift;
|
||||
QCowHeader header;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
}
|
||||
be32_to_cpus(&header.magic);
|
||||
be32_to_cpus(&header.version);
|
||||
be64_to_cpus(&header.backing_file_offset);
|
||||
@@ -111,31 +109,15 @@ static int qcow_open(BlockDriverState *bs, int flags)
|
||||
be32_to_cpus(&header.crypt_method);
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
ret = -EINVAL;
|
||||
if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
|
||||
goto fail;
|
||||
}
|
||||
if (header.version != QCOW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "qcow", version);
|
||||
ret = -ENOTSUP;
|
||||
if (header.size <= 1 || header.cluster_bits < 9)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (header.size <= 1 || header.cluster_bits < 9) {
|
||||
ret = -EINVAL;
|
||||
if (header.crypt_method > QCOW_CRYPT_AES)
|
||||
goto fail;
|
||||
}
|
||||
if (header.crypt_method > QCOW_CRYPT_AES) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
s->crypt_method_header = header.crypt_method;
|
||||
if (s->crypt_method_header) {
|
||||
if (s->crypt_method_header)
|
||||
bs->encrypted = 1;
|
||||
}
|
||||
s->cluster_bits = header.cluster_bits;
|
||||
s->cluster_size = 1 << s->cluster_bits;
|
||||
s->cluster_sectors = 1 << (s->cluster_bits - 9);
|
||||
@@ -150,33 +132,33 @@ static int qcow_open(BlockDriverState *bs, int flags)
|
||||
|
||||
s->l1_table_offset = header.l1_table_offset;
|
||||
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
||||
|
||||
ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
|
||||
s->l1_size * sizeof(uint64_t))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
/* alloc L2 cache */
|
||||
s->l2_cache = g_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
if (!s->l2_cache)
|
||||
goto fail;
|
||||
s->cluster_cache = g_malloc(s->cluster_size);
|
||||
if (!s->cluster_cache)
|
||||
goto fail;
|
||||
s->cluster_data = g_malloc(s->cluster_size);
|
||||
if (!s->cluster_data)
|
||||
goto fail;
|
||||
s->cluster_cache_offset = -1;
|
||||
|
||||
/* read the backing file name */
|
||||
if (header.backing_file_offset != 0) {
|
||||
len = header.backing_file_size;
|
||||
if (len > 1023) {
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
}
|
||||
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
||||
bs->backing_file, len);
|
||||
if (ret < 0) {
|
||||
if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len)
|
||||
goto fail;
|
||||
}
|
||||
bs->backing_file[len] = '\0';
|
||||
}
|
||||
|
||||
@@ -194,7 +176,7 @@ static int qcow_open(BlockDriverState *bs, int flags)
|
||||
g_free(s->l2_cache);
|
||||
g_free(s->cluster_cache);
|
||||
g_free(s->cluster_data);
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
@@ -386,16 +368,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
@@ -453,7 +433,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
static int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -551,7 +531,7 @@ fail:
|
||||
goto done;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
static int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -644,14 +624,13 @@ static void qcow_close(BlockDriverState *bs)
|
||||
|
||||
static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
{
|
||||
int header_size, backing_filename_len, l1_size, shift, i;
|
||||
int fd, header_size, backing_filename_len, l1_size, i, shift;
|
||||
QCowHeader header;
|
||||
uint8_t *tmp;
|
||||
uint64_t tmp;
|
||||
int64_t total_size = 0;
|
||||
const char *backing_file = NULL;
|
||||
int flags = 0;
|
||||
int ret;
|
||||
BlockDriverState *qcow_bs;
|
||||
|
||||
/* Read out options */
|
||||
while (options && options->name) {
|
||||
@@ -665,21 +644,9 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, options);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&qcow_bs, filename, BDRV_O_RDWR);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(qcow_bs, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(QCOW_VERSION);
|
||||
@@ -715,34 +682,33 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
|
||||
/* write all the data */
|
||||
ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header));
|
||||
ret = qemu_write_full(fd, &header, sizeof(header));
|
||||
if (ret != sizeof(header)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (backing_file) {
|
||||
ret = bdrv_pwrite(qcow_bs, sizeof(header),
|
||||
backing_file, backing_filename_len);
|
||||
ret = qemu_write_full(fd, backing_file, backing_filename_len);
|
||||
if (ret != backing_filename_len) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
}
|
||||
lseek(fd, header_size, SEEK_SET);
|
||||
tmp = 0;
|
||||
for(i = 0;i < l1_size; i++) {
|
||||
ret = qemu_write_full(fd, &tmp, sizeof(tmp));
|
||||
if (ret != sizeof(tmp)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = g_malloc0(BDRV_SECTOR_SIZE);
|
||||
for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/
|
||||
BDRV_SECTOR_SIZE); i++) {
|
||||
ret = bdrv_pwrite(qcow_bs, header_size +
|
||||
BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE);
|
||||
if (ret != BDRV_SECTOR_SIZE) {
|
||||
g_free(tmp);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
g_free(tmp);
|
||||
ret = 0;
|
||||
exit:
|
||||
bdrv_delete(qcow_bs);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -835,6 +801,11 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -872,7 +843,8 @@ static BlockDriver bdrv_qcow = {
|
||||
|
||||
.bdrv_co_readv = qcow_co_readv,
|
||||
.bdrv_co_writev = qcow_co_writev,
|
||||
.bdrv_co_is_allocated = qcow_co_is_allocated,
|
||||
.bdrv_co_flush_to_disk = qcow_co_flush,
|
||||
.bdrv_is_allocated = qcow_is_allocated,
|
||||
|
||||
.bdrv_set_key = qcow_set_key,
|
||||
.bdrv_make_empty = qcow_make_empty,
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "block_int.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qcow2.h"
|
||||
#include "trace.h"
|
||||
|
||||
typedef struct Qcow2CachedTable {
|
||||
void* table;
|
||||
@@ -101,9 +100,6 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_qcow2_cache_entry_flush(qemu_coroutine_self(),
|
||||
c == s->l2_table_cache, i);
|
||||
|
||||
if (c->depends) {
|
||||
ret = qcow2_cache_flush_dependency(bs, c);
|
||||
} else if (c->depends_on_flush) {
|
||||
@@ -136,13 +132,10 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||
|
||||
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int result = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache);
|
||||
|
||||
for (i = 0; i < c->size; i++) {
|
||||
ret = qcow2_cache_entry_flush(bs, c, i);
|
||||
if (ret < 0 && result != -ENOSPC) {
|
||||
@@ -225,9 +218,6 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache,
|
||||
offset, read_from_disk);
|
||||
|
||||
/* Check if the table is already cached */
|
||||
for (i = 0; i < c->size; i++) {
|
||||
if (c->entries[i].offset == offset) {
|
||||
@@ -237,8 +227,6 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
||||
|
||||
/* If not, write a table back and replace it */
|
||||
i = qcow2_cache_find_entry_to_replace(c);
|
||||
trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(),
|
||||
c == s->l2_table_cache, i);
|
||||
if (i < 0) {
|
||||
return i;
|
||||
}
|
||||
@@ -248,8 +236,6 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
||||
return ret;
|
||||
}
|
||||
|
||||
trace_qcow2_cache_get_read(qemu_coroutine_self(),
|
||||
c == s->l2_table_cache, i);
|
||||
c->entries[i].offset = 0;
|
||||
if (read_from_disk) {
|
||||
if (c == s->l2_table_cache) {
|
||||
@@ -272,10 +258,6 @@ found:
|
||||
c->entries[i].cache_hits++;
|
||||
c->entries[i].ref++;
|
||||
*table = c->entries[i].table;
|
||||
|
||||
trace_qcow2_cache_get_done(qemu_coroutine_self(),
|
||||
c == s->l2_table_cache, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+225
-437
@@ -27,7 +27,6 @@
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "trace.h"
|
||||
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
|
||||
{
|
||||
@@ -171,8 +170,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
|
||||
old_l2_offset = s->l1_table[l1_index];
|
||||
|
||||
trace_qcow2_l2_allocate(bs, l1_index);
|
||||
|
||||
/* allocate a new l2 entry */
|
||||
|
||||
l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
|
||||
@@ -187,7 +184,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
|
||||
/* allocate a new entry in the l2 cache */
|
||||
|
||||
trace_qcow2_l2_allocate_get_empty(bs, l1_index);
|
||||
ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -195,7 +191,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
|
||||
l2_table = *table;
|
||||
|
||||
if ((old_l2_offset & L1E_OFFSET_MASK) == 0) {
|
||||
if (old_l2_offset == 0) {
|
||||
/* if there was no old l2 table, clear the new table */
|
||||
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
|
||||
} else {
|
||||
@@ -203,8 +199,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
|
||||
/* if there was an old l2 table, read it from the disk */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ);
|
||||
ret = qcow2_cache_get(bs, s->l2_table_cache,
|
||||
old_l2_offset & L1E_OFFSET_MASK,
|
||||
ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset,
|
||||
(void**) &old_table);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -221,7 +216,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
/* write the l2 table to the file */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
|
||||
|
||||
trace_qcow2_l2_allocate_write_l2(bs, l1_index);
|
||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||
ret = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
if (ret < 0) {
|
||||
@@ -229,7 +223,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
}
|
||||
|
||||
/* update the L1 entry */
|
||||
trace_qcow2_l2_allocate_write_l1(bs, l1_index);
|
||||
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
|
||||
ret = write_l1_entry(bs, l1_index);
|
||||
if (ret < 0) {
|
||||
@@ -237,54 +230,36 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||
}
|
||||
|
||||
*table = l2_table;
|
||||
trace_qcow2_l2_allocate_done(bs, l1_index, 0);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
trace_qcow2_l2_allocate_done(bs, l1_index, ret);
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) table);
|
||||
s->l1_table[l1_index] = old_l2_offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks how many clusters in a given L2 table are contiguous in the image
|
||||
* file. As soon as one of the flags in the bitmask stop_flags changes compared
|
||||
* to the first cluster, the search is stopped and the cluster is not counted
|
||||
* as contiguous. (This allows it, for example, to stop at the first compressed
|
||||
* cluster which may require a different handling)
|
||||
*/
|
||||
static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
|
||||
uint64_t *l2_table, uint64_t start, uint64_t stop_flags)
|
||||
uint64_t *l2_table, uint64_t start, uint64_t mask)
|
||||
{
|
||||
int i;
|
||||
uint64_t mask = stop_flags | L2E_OFFSET_MASK;
|
||||
uint64_t offset = be64_to_cpu(l2_table[0]) & mask;
|
||||
uint64_t offset = be64_to_cpu(l2_table[0]) & ~mask;
|
||||
|
||||
if (!offset)
|
||||
return 0;
|
||||
|
||||
for (i = start; i < start + nb_clusters; i++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
||||
if (offset + (uint64_t) i * cluster_size != l2_entry) {
|
||||
for (i = start; i < start + nb_clusters; i++)
|
||||
if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (i - start);
|
||||
}
|
||||
|
||||
static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table)
|
||||
{
|
||||
int i;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
|
||||
|
||||
if (type != QCOW2_CLUSTER_UNALLOCATED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(nb_clusters-- && l2_table[i] == 0)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
@@ -314,62 +289,89 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
uint64_t start_sect,
|
||||
uint64_t cluster_offset,
|
||||
int n_start, int n_end)
|
||||
|
||||
static int qcow2_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QEMUIOVector qiov;
|
||||
int ret, index_in_cluster, n, n1;
|
||||
uint64_t cluster_offset;
|
||||
struct iovec iov;
|
||||
QEMUIOVector qiov;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
n = nb_sectors;
|
||||
|
||||
ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n,
|
||||
&cluster_offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
if (!cluster_offset) {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = n * 512;
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n);
|
||||
if (n1 > 0) {
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING);
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
memset(buf, 0, 512 * n);
|
||||
}
|
||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
if (qcow2_decompress_cluster(bs, cluster_offset) < 0)
|
||||
return -1;
|
||||
memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||
} else {
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ);
|
||||
ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
if (s->crypt_method) {
|
||||
qcow2_encrypt_sectors(s, sector_num, buf, buf, n, 0,
|
||||
&s->aes_decrypt_key);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
|
||||
uint64_t cluster_offset, int n_start, int n_end)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int n, ret;
|
||||
|
||||
/*
|
||||
* If this is the last cluster and it is only partially used, we must only
|
||||
* copy until the end of the image, or bdrv_check_request will fail for the
|
||||
* bdrv_read/write calls below.
|
||||
*/
|
||||
if (start_sect + n_end > bs->total_sectors) {
|
||||
n_end = bs->total_sectors - start_sect;
|
||||
}
|
||||
|
||||
n = n_end - n_start;
|
||||
if (n <= 0) {
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||
iov.iov_base = qemu_blockalign(bs, iov.iov_len);
|
||||
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
|
||||
|
||||
/* Call .bdrv_co_readv() directly instead of using the public block-layer
|
||||
* interface. This avoids double I/O throttling and request tracking,
|
||||
* which can lead to deadlock when block layer copy-on-read is enabled.
|
||||
*/
|
||||
ret = bs->drv->bdrv_co_readv(bs, start_sect + n_start, n, &qiov);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (s->crypt_method) {
|
||||
qcow2_encrypt_sectors(s, start_sect + n_start,
|
||||
iov.iov_base, iov.iov_base, n, 1,
|
||||
s->cluster_data,
|
||||
s->cluster_data, n, 1,
|
||||
&s->aes_encrypt_key);
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
|
||||
ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
qemu_vfree(iov.iov_base);
|
||||
return ret;
|
||||
ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start,
|
||||
s->cluster_data, n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -384,9 +386,11 @@ out:
|
||||
*
|
||||
* on exit, *num is the number of contiguous sectors we can read.
|
||||
*
|
||||
* Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error
|
||||
* cases.
|
||||
* Return 0, if the offset is found
|
||||
* Return -errno, otherwise.
|
||||
*
|
||||
*/
|
||||
|
||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *cluster_offset)
|
||||
{
|
||||
@@ -422,19 +426,19 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
/* seek the the l2 offset in the l1 table */
|
||||
|
||||
l1_index = offset >> l1_bits;
|
||||
if (l1_index >= s->l1_size) {
|
||||
ret = QCOW2_CLUSTER_UNALLOCATED;
|
||||
if (l1_index >= s->l1_size)
|
||||
goto out;
|
||||
}
|
||||
|
||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||
if (!l2_offset) {
|
||||
ret = QCOW2_CLUSTER_UNALLOCATED;
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
|
||||
/* seek the l2 table of the given l2 offset */
|
||||
|
||||
if (!l2_offset)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* load the l2 table in memory */
|
||||
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
ret = l2_load(bs, l2_offset, &l2_table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -446,46 +450,26 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
nb_clusters = size_to_clusters(s, nb_needed << 9);
|
||||
|
||||
ret = qcow2_get_cluster_type(*cluster_offset);
|
||||
switch (ret) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
/* Compressed clusters can only be processed one by one */
|
||||
c = 1;
|
||||
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], 0,
|
||||
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
|
||||
*cluster_offset = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (!*cluster_offset) {
|
||||
/* how many empty clusters ? */
|
||||
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
|
||||
*cluster_offset = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
} else {
|
||||
/* how many allocated clusters ? */
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], 0,
|
||||
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
|
||||
*cluster_offset &= L2E_OFFSET_MASK;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
&l2_table[l2_index], 0, QCOW_OFLAG_COPIED);
|
||||
}
|
||||
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
|
||||
nb_available = (c * s->cluster_sectors);
|
||||
|
||||
nb_available = (c * s->cluster_sectors);
|
||||
out:
|
||||
if (nb_available > nb_needed)
|
||||
nb_available = nb_needed;
|
||||
|
||||
*num = nb_available - index_in_cluster;
|
||||
|
||||
return ret;
|
||||
*cluster_offset &=~QCOW_OFLAG_COPIED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -501,6 +485,7 @@ out:
|
||||
*/
|
||||
static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t **new_l2_table,
|
||||
uint64_t *new_l2_offset,
|
||||
int *new_l2_index)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -518,13 +503,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
|
||||
/* seek the l2 table of the given l2 offset */
|
||||
|
||||
if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) {
|
||||
if (l2_offset & QCOW_OFLAG_COPIED) {
|
||||
/* load the l2 table in memory */
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
ret = l2_load(bs, l2_offset, &l2_table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -540,7 +525,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
if (l2_offset) {
|
||||
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
|
||||
}
|
||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||
l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
|
||||
}
|
||||
|
||||
/* find the cluster offset for the given disk offset */
|
||||
@@ -548,6 +533,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
|
||||
*new_l2_table = l2_table;
|
||||
*new_l2_offset = l2_offset;
|
||||
*new_l2_index = l2_index;
|
||||
|
||||
return 0;
|
||||
@@ -572,23 +558,24 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret;
|
||||
uint64_t *l2_table;
|
||||
uint64_t l2_offset, *l2_table;
|
||||
int64_t cluster_offset;
|
||||
int nb_csectors;
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compression can't overwrite anything. Fail if the cluster was already
|
||||
* allocated. */
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
if (cluster_offset & L2E_OFFSET_MASK) {
|
||||
if (cluster_offset & QCOW_OFLAG_COPIED) {
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cluster_offset)
|
||||
qcow2_free_any_clusters(bs, cluster_offset, 1);
|
||||
|
||||
cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
|
||||
if (cluster_offset < 0) {
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
@@ -620,12 +607,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, start_sect, *l2_table;
|
||||
uint64_t cluster_offset = m->alloc_offset;
|
||||
uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
|
||||
uint64_t cluster_offset = m->cluster_offset;
|
||||
bool cow = false;
|
||||
|
||||
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
|
||||
|
||||
if (m->nb_clusters == 0)
|
||||
return 0;
|
||||
|
||||
@@ -635,9 +620,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
|
||||
if (m->n_start) {
|
||||
cow = true;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
@@ -645,10 +628,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
if (m->nb_available & (s->cluster_sectors - 1)) {
|
||||
uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1);
|
||||
cow = true;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
|
||||
m->nb_available - end, s->cluster_sectors);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
@@ -665,7 +646,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
}
|
||||
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
|
||||
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
|
||||
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -697,7 +678,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
*/
|
||||
if (j != 0) {
|
||||
for (i = 0; i < j; i++) {
|
||||
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1);
|
||||
qcow2_free_any_clusters(bs,
|
||||
be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,132 +689,13 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of contiguous clusters that can be used for an allocating
|
||||
* write, but require COW to be performed (this includes yet unallocated space,
|
||||
* which must copy from the backing file)
|
||||
*/
|
||||
static int count_cow_clusters(BDRVQcowState *s, int nb_clusters,
|
||||
uint64_t *l2_table, int l2_index)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]);
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
|
||||
switch(cluster_type) {
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
if (l2_entry & QCOW_OFLAG_COPIED) {
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
assert(i <= nb_clusters);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates new clusters for the given guest_offset.
|
||||
*
|
||||
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to
|
||||
* contain the number of clusters that have been allocated and are contiguous
|
||||
* in the image file.
|
||||
*
|
||||
* If *host_offset is non-zero, it specifies the offset in the image file at
|
||||
* which the new clusters must start. *nb_clusters can be 0 on return in this
|
||||
* case if the cluster at host_offset is already in use. If *host_offset is
|
||||
* zero, the clusters can be allocated anywhere in the image file.
|
||||
*
|
||||
* *host_offset is updated to contain the offset into the image file at which
|
||||
* the first allocated cluster starts.
|
||||
*
|
||||
* Return 0 on success and -errno in error cases. -EAGAIN means that the
|
||||
* function has been waiting for another request and the allocation must be
|
||||
* restarted, but the whole request should not be failed.
|
||||
*/
|
||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, unsigned int *nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowL2Meta *old_alloc;
|
||||
|
||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||
*host_offset, *nb_clusters);
|
||||
|
||||
/*
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
|
||||
|
||||
uint64_t start = guest_offset >> s->cluster_bits;
|
||||
uint64_t end = start + *nb_clusters;
|
||||
uint64_t old_start = old_alloc->offset >> s->cluster_bits;
|
||||
uint64_t old_end = old_start + old_alloc->nb_clusters;
|
||||
|
||||
if (end < old_start || start > old_end) {
|
||||
/* No intersection */
|
||||
} else {
|
||||
if (start < old_start) {
|
||||
/* Stop at the start of a running allocation */
|
||||
*nb_clusters = old_start - start;
|
||||
} else {
|
||||
*nb_clusters = 0;
|
||||
}
|
||||
|
||||
if (*nb_clusters == 0) {
|
||||
/* Wait for the dependency to complete. We need to recheck
|
||||
* the free/allocated clusters when we continue. */
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_wait(&old_alloc->dependent_requests);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*nb_clusters) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Allocate new clusters */
|
||||
trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
|
||||
if (*host_offset == 0) {
|
||||
int64_t cluster_offset =
|
||||
qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size);
|
||||
if (cluster_offset < 0) {
|
||||
return cluster_offset;
|
||||
}
|
||||
*host_offset = cluster_offset;
|
||||
return 0;
|
||||
} else {
|
||||
int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
*nb_clusters = ret;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* alloc_cluster_offset
|
||||
*
|
||||
* For a given offset on the virtual disk, find the cluster offset in qcow2
|
||||
* file. If the offset is not found, allocate a new cluster.
|
||||
* For a given offset of the disk image, return cluster offset in qcow2 file.
|
||||
* If the offset is not found, allocate a new cluster.
|
||||
*
|
||||
* If the cluster was already allocated, m->nb_clusters is set to 0 and
|
||||
* If the cluster was already allocated, m->nb_clusters is set to 0,
|
||||
* other fields in m are meaningless.
|
||||
*
|
||||
* If the cluster is newly allocated, m->nb_clusters is set to the number of
|
||||
@@ -849,139 +712,134 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret, sectors;
|
||||
uint64_t *l2_table;
|
||||
unsigned int nb_clusters, keep_clusters;
|
||||
uint64_t cluster_offset;
|
||||
int l2_index, ret;
|
||||
uint64_t l2_offset, *l2_table;
|
||||
int64_t cluster_offset;
|
||||
unsigned int nb_clusters, i = 0;
|
||||
QCowL2Meta *old_alloc;
|
||||
|
||||
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset,
|
||||
n_start, n_end);
|
||||
|
||||
/* Find L2 entry for the first involved cluster */
|
||||
again:
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the number of clusters to look for. We stop at L2 table
|
||||
* boundaries to keep things simple.
|
||||
*/
|
||||
nb_clusters = MIN(size_to_clusters(s, n_end << BDRV_SECTOR_BITS),
|
||||
s->l2_size - l2_index);
|
||||
again:
|
||||
nb_clusters = size_to_clusters(s, n_end << 9);
|
||||
|
||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
|
||||
/*
|
||||
* Check how many clusters are already allocated and don't need COW, and how
|
||||
* many need a new allocation.
|
||||
*/
|
||||
if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL
|
||||
&& (cluster_offset & QCOW_OFLAG_COPIED))
|
||||
{
|
||||
/* We keep all QCOW_OFLAG_COPIED clusters */
|
||||
keep_clusters =
|
||||
count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], 0,
|
||||
QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO);
|
||||
assert(keep_clusters <= nb_clusters);
|
||||
nb_clusters -= keep_clusters;
|
||||
} else {
|
||||
keep_clusters = 0;
|
||||
cluster_offset = 0;
|
||||
/* We keep all QCOW_OFLAG_COPIED clusters */
|
||||
|
||||
if (cluster_offset & QCOW_OFLAG_COPIED) {
|
||||
nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], 0, 0);
|
||||
|
||||
cluster_offset &= ~QCOW_OFLAG_COPIED;
|
||||
m->nb_clusters = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nb_clusters > 0) {
|
||||
/* For the moment, overwrite compressed clusters one by one */
|
||||
uint64_t entry = be64_to_cpu(l2_table[l2_index + keep_clusters]);
|
||||
if (entry & QCOW_OFLAG_COMPRESSED) {
|
||||
nb_clusters = 1;
|
||||
/* for the moment, multiple compressed clusters are not managed */
|
||||
|
||||
if (cluster_offset & QCOW_OFLAG_COMPRESSED)
|
||||
nb_clusters = 1;
|
||||
|
||||
/* how many available clusters ? */
|
||||
|
||||
while (i < nb_clusters) {
|
||||
i += count_contiguous_clusters(nb_clusters - i, s->cluster_size,
|
||||
&l2_table[l2_index], i, 0);
|
||||
if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
i += count_contiguous_free_clusters(nb_clusters - i,
|
||||
&l2_table[l2_index + i]);
|
||||
if (i >= nb_clusters) {
|
||||
break;
|
||||
}
|
||||
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
if ((cluster_offset & QCOW_OFLAG_COPIED) ||
|
||||
(cluster_offset & QCOW_OFLAG_COMPRESSED))
|
||||
break;
|
||||
}
|
||||
assert(i <= nb_clusters);
|
||||
nb_clusters = i;
|
||||
|
||||
/*
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
|
||||
|
||||
uint64_t start = offset >> s->cluster_bits;
|
||||
uint64_t end = start + nb_clusters;
|
||||
uint64_t old_start = old_alloc->offset >> s->cluster_bits;
|
||||
uint64_t old_end = old_start + old_alloc->nb_clusters;
|
||||
|
||||
if (end < old_start || start > old_end) {
|
||||
/* No intersection */
|
||||
} else {
|
||||
nb_clusters = count_cow_clusters(s, nb_clusters, l2_table,
|
||||
l2_index + keep_clusters);
|
||||
if (start < old_start) {
|
||||
/* Stop at the start of a running allocation */
|
||||
nb_clusters = old_start - start;
|
||||
} else {
|
||||
nb_clusters = 0;
|
||||
}
|
||||
|
||||
if (nb_clusters == 0) {
|
||||
/* Wait for the dependency to complete. We need to recheck
|
||||
* the free/allocated clusters when we continue. */
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_wait(&old_alloc->dependent_requests);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cluster_offset &= L2E_OFFSET_MASK;
|
||||
if (!nb_clusters) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* The L2 table isn't used any more after this. As long as the cache works
|
||||
* synchronously, it's important to release it before calling
|
||||
* do_alloc_cluster_offset, which may yield if we need to wait for another
|
||||
* request to complete. If we still had the reference, we could use up the
|
||||
* whole cache with sleeping requests.
|
||||
*/
|
||||
/* save info needed for meta data update */
|
||||
m->offset = offset;
|
||||
m->n_start = n_start;
|
||||
m->nb_clusters = nb_clusters;
|
||||
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
|
||||
|
||||
/* allocate a new cluster */
|
||||
|
||||
cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
|
||||
if (cluster_offset < 0) {
|
||||
ret = cluster_offset;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
out:
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail_put;
|
||||
}
|
||||
|
||||
/* If there is something left to allocate, do that now */
|
||||
*m = (QCowL2Meta) {
|
||||
.cluster_offset = cluster_offset,
|
||||
.nb_clusters = 0,
|
||||
};
|
||||
qemu_co_queue_init(&m->dependent_requests);
|
||||
m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
|
||||
m->cluster_offset = cluster_offset;
|
||||
|
||||
if (nb_clusters > 0) {
|
||||
uint64_t alloc_offset;
|
||||
uint64_t alloc_cluster_offset;
|
||||
uint64_t keep_bytes = keep_clusters * s->cluster_size;
|
||||
|
||||
/* Calculate start and size of allocation */
|
||||
alloc_offset = offset + keep_bytes;
|
||||
|
||||
if (keep_clusters == 0) {
|
||||
alloc_cluster_offset = 0;
|
||||
} else {
|
||||
alloc_cluster_offset = cluster_offset + keep_bytes;
|
||||
}
|
||||
|
||||
/* Allocate, if necessary at a given offset in the image file */
|
||||
ret = do_alloc_cluster_offset(bs, alloc_offset, &alloc_cluster_offset,
|
||||
&nb_clusters);
|
||||
if (ret == -EAGAIN) {
|
||||
goto again;
|
||||
} else if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* save info needed for meta data update */
|
||||
if (nb_clusters > 0) {
|
||||
int requested_sectors = n_end - keep_clusters * s->cluster_sectors;
|
||||
int avail_sectors = (keep_clusters + nb_clusters)
|
||||
<< (s->cluster_bits - BDRV_SECTOR_BITS);
|
||||
|
||||
*m = (QCowL2Meta) {
|
||||
.cluster_offset = keep_clusters == 0 ?
|
||||
alloc_cluster_offset : cluster_offset,
|
||||
.alloc_offset = alloc_cluster_offset,
|
||||
.offset = alloc_offset,
|
||||
.n_start = keep_clusters == 0 ? n_start : 0,
|
||||
.nb_clusters = nb_clusters,
|
||||
.nb_available = MIN(requested_sectors, avail_sectors),
|
||||
};
|
||||
qemu_co_queue_init(&m->dependent_requests);
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
|
||||
}
|
||||
}
|
||||
|
||||
/* Some cleanup work */
|
||||
sectors = (keep_clusters + nb_clusters) << (s->cluster_bits - 9);
|
||||
if (sectors > n_end) {
|
||||
sectors = n_end;
|
||||
}
|
||||
|
||||
assert(sectors > n_start);
|
||||
*num = sectors - n_start;
|
||||
*num = m->nb_available - n_start;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (m->nb_clusters > 0) {
|
||||
QLIST_REMOVE(m, next_in_flight);
|
||||
}
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
fail_put:
|
||||
QLIST_REMOVE(m, next_in_flight);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1046,12 +904,12 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
unsigned int nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
uint64_t l2_offset, *l2_table;
|
||||
int l2_index;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -1063,7 +921,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t old_offset;
|
||||
|
||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
if ((old_offset & L2E_OFFSET_MASK) == 0) {
|
||||
old_offset &= ~QCOW_OFLAG_COPIED;
|
||||
|
||||
if (old_offset == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1116,75 +976,3 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This zeroes as many clusters of nb_clusters as possible at once (i.e.
|
||||
* all clusters in the same L2 table) and returns the number of zeroed
|
||||
* clusters.
|
||||
*/
|
||||
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
unsigned int nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
int l2_index;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Limit nb_clusters to one L2 table */
|
||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t old_offset;
|
||||
|
||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
/* Update L2 entries */
|
||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||
if (old_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||
qcow2_free_any_clusters(bs, old_offset, 1);
|
||||
} else {
|
||||
l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nb_clusters;
|
||||
}
|
||||
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int nb_clusters;
|
||||
int ret;
|
||||
|
||||
/* The zero flag is only supported by version 3 and newer */
|
||||
if (s->qcow_version < 3) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Each L2 table is handled by its own loop iteration */
|
||||
nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);
|
||||
|
||||
while (nb_clusters > 0) {
|
||||
ret = zero_single_l2(bs, offset, nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
nb_clusters -= ret;
|
||||
offset += (ret * s->cluster_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+73
-132
@@ -167,7 +167,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
if (refcount_table_index < s->refcount_table_size) {
|
||||
|
||||
uint64_t refcount_block_offset =
|
||||
s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK;
|
||||
s->refcount_table[refcount_table_index];
|
||||
|
||||
/* If it's already there, we're done */
|
||||
if (refcount_block_offset) {
|
||||
@@ -367,7 +367,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
for(i = 0; i < table_size; i++) {
|
||||
be64_to_cpus(&new_table[i]);
|
||||
cpu_to_be64s(&new_table[i]);
|
||||
}
|
||||
|
||||
/* Hook up the new refcount table in the qcow2 header */
|
||||
@@ -400,7 +400,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return new_block;
|
||||
|
||||
fail_table:
|
||||
g_free(new_table);
|
||||
@@ -582,40 +582,6 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
|
||||
return offset;
|
||||
}
|
||||
|
||||
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t cluster_index;
|
||||
uint64_t old_free_cluster_index;
|
||||
int i, refcount, ret;
|
||||
|
||||
/* Check how many clusters there are free */
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
for(i = 0; i < nb_clusters; i++) {
|
||||
refcount = get_refcount(bs, cluster_index++);
|
||||
|
||||
if (refcount < 0) {
|
||||
return refcount;
|
||||
} else if (refcount != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* And then allocate them */
|
||||
old_free_cluster_index = s->free_cluster_index;
|
||||
s->free_cluster_index = cluster_index + i;
|
||||
|
||||
ret = update_refcount(bs, offset, i << s->cluster_bits, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
s->free_cluster_index = old_free_cluster_index;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* only used to allocate compressed sectors. We try to allocate
|
||||
contiguous sectors. size must be <= cluster_size */
|
||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
@@ -679,35 +645,32 @@ void qcow2_free_clusters(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a cluster using its L2 entry (handles clusters of all types, e.g.
|
||||
* normal cluster, compressed cluster, etc.)
|
||||
* free_any_clusters
|
||||
*
|
||||
* free clusters according to its type: compressed or not
|
||||
*
|
||||
*/
|
||||
|
||||
void qcow2_free_any_clusters(BlockDriverState *bs,
|
||||
uint64_t l2_entry, int nb_clusters)
|
||||
uint64_t cluster_offset, int nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
switch (qcow2_get_cluster_type(l2_entry)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
{
|
||||
int nb_csectors;
|
||||
nb_csectors = ((l2_entry >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
qcow2_free_clusters(bs,
|
||||
(l2_entry & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512);
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits);
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* free the cluster */
|
||||
|
||||
if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
int nb_csectors;
|
||||
nb_csectors = ((cluster_offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
qcow2_free_clusters(bs,
|
||||
(cluster_offset & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512);
|
||||
return;
|
||||
}
|
||||
|
||||
qcow2_free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -737,10 +700,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
l2_table = NULL;
|
||||
l1_table = NULL;
|
||||
l1_size2 = l1_size * sizeof(uint64_t);
|
||||
|
||||
/* WARNING: qcow2_snapshot_goto relies on this function not using the
|
||||
* l1_table_offset when it is the current s->l1_table_offset! Be careful
|
||||
* when changing this! */
|
||||
if (l1_table_offset != s->l1_table_offset) {
|
||||
if (l1_size2 != 0) {
|
||||
l1_table = g_malloc0(align_offset(l1_size2, 512));
|
||||
@@ -767,7 +726,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
l2_offset = l1_table[i];
|
||||
if (l2_offset) {
|
||||
old_l2_offset = l2_offset;
|
||||
l2_offset &= L1E_OFFSET_MASK;
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
|
||||
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
|
||||
(void**) &l2_table);
|
||||
@@ -799,11 +758,10 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
} else {
|
||||
uint64_t cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
||||
if (addend != 0) {
|
||||
refcount = update_cluster_refcount(bs, cluster_index, addend);
|
||||
refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend);
|
||||
} else {
|
||||
refcount = get_refcount(bs, cluster_index);
|
||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
||||
}
|
||||
|
||||
if (refcount < 0) {
|
||||
@@ -861,8 +819,7 @@ fail:
|
||||
qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
|
||||
old_refcount_writethrough);
|
||||
|
||||
/* Update L1 only if it isn't deleted anyway (addend = -1) */
|
||||
if (addend >= 0 && l1_modified) {
|
||||
if (l1_modified) {
|
||||
for(i = 0; i < l1_size; i++)
|
||||
cpu_to_be64s(&l1_table[i]);
|
||||
if (bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table,
|
||||
@@ -941,7 +898,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
int check_copied)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table, l2_entry;
|
||||
uint64_t *l2_table, offset;
|
||||
int i, l2_size, nb_csectors, refcount;
|
||||
|
||||
/* Read L2 table from disk */
|
||||
@@ -953,70 +910,54 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
|
||||
/* Do the actual checks */
|
||||
for(i = 0; i < s->l2_size; i++) {
|
||||
l2_entry = be64_to_cpu(l2_table[i]);
|
||||
|
||||
switch (qcow2_get_cluster_type(l2_entry)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
|
||||
if (l2_entry & QCOW_OFLAG_COPIED) {
|
||||
fprintf(stderr, "ERROR: cluster %" PRId64 ": "
|
||||
"copied flag must never be set for compressed "
|
||||
"clusters\n", l2_entry >> s->cluster_bits);
|
||||
l2_entry &= ~QCOW_OFLAG_COPIED;
|
||||
res->corruptions++;
|
||||
}
|
||||
|
||||
/* Mark cluster as used */
|
||||
nb_csectors = ((l2_entry >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
l2_entry &= s->cluster_offset_mask;
|
||||
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
||||
l2_entry & ~511, nb_csectors * 512);
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if ((l2_entry & L2E_OFFSET_MASK) == 0) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
{
|
||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
|
||||
if (check_copied) {
|
||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
||||
if (refcount < 0) {
|
||||
fprintf(stderr, "Can't get refcount for offset %"
|
||||
PRIx64 ": %s\n", l2_entry, strerror(-refcount));
|
||||
goto fail;
|
||||
offset = be64_to_cpu(l2_table[i]);
|
||||
if (offset != 0) {
|
||||
if (offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
|
||||
if (offset & QCOW_OFLAG_COPIED) {
|
||||
fprintf(stderr, "ERROR: cluster %" PRId64 ": "
|
||||
"copied flag must never be set for compressed "
|
||||
"clusters\n", offset >> s->cluster_bits);
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
res->corruptions++;
|
||||
}
|
||||
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
|
||||
PRIx64 " refcount=%d\n", l2_entry, refcount);
|
||||
|
||||
/* Mark cluster as used */
|
||||
nb_csectors = ((offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
offset &= s->cluster_offset_mask;
|
||||
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
||||
offset & ~511, nb_csectors * 512);
|
||||
} else {
|
||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||
if (check_copied) {
|
||||
uint64_t entry = offset;
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
||||
if (refcount < 0) {
|
||||
fprintf(stderr, "Can't get refcount for offset %"
|
||||
PRIx64 ": %s\n", entry, strerror(-refcount));
|
||||
goto fail;
|
||||
}
|
||||
if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
|
||||
PRIx64 " refcount=%d\n", entry, refcount);
|
||||
res->corruptions++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark cluster as used */
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
inc_refcounts(bs, res, refcount_table,refcount_table_size,
|
||||
offset, s->cluster_size);
|
||||
|
||||
/* Correct offsets are cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
|
||||
"properly aligned; L2 entry corrupted.\n", offset);
|
||||
res->corruptions++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark cluster as used */
|
||||
inc_refcounts(bs, res, refcount_table,refcount_table_size,
|
||||
offset, s->cluster_size);
|
||||
|
||||
/* Correct offsets are cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
|
||||
"properly aligned; L2 entry corrupted.\n", offset);
|
||||
res->corruptions++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,7 +1028,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Mark L2 table as used */
|
||||
l2_offset &= L1E_OFFSET_MASK;
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
||||
l2_offset, s->cluster_size);
|
||||
|
||||
|
||||
+92
-286
@@ -46,11 +46,6 @@ typedef struct QEMU_PACKED QCowSnapshotHeader {
|
||||
/* name follows */
|
||||
} QCowSnapshotHeader;
|
||||
|
||||
typedef struct QEMU_PACKED QCowSnapshotExtraData {
|
||||
uint64_t vm_state_size_large;
|
||||
uint64_t disk_size;
|
||||
} QCowSnapshotExtraData;
|
||||
|
||||
void qcow2_free_snapshots(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -69,12 +64,10 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshotHeader h;
|
||||
QCowSnapshotExtraData extra;
|
||||
QCowSnapshot *sn;
|
||||
int i, id_str_size, name_size;
|
||||
int64_t offset;
|
||||
uint32_t extra_data_size;
|
||||
int ret;
|
||||
|
||||
if (!s->nb_snapshots) {
|
||||
s->snapshots = NULL;
|
||||
@@ -84,15 +77,10 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
|
||||
offset = s->snapshots_offset;
|
||||
s->snapshots = g_malloc0(s->nb_snapshots * sizeof(QCowSnapshot));
|
||||
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
/* Read statically sized part of the snapshot header */
|
||||
offset = align_offset(offset, 8);
|
||||
ret = bdrv_pread(bs->file, offset, &h, sizeof(h));
|
||||
if (ret < 0) {
|
||||
if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset += sizeof(h);
|
||||
sn = s->snapshots + i;
|
||||
sn->l1_table_offset = be64_to_cpu(h.l1_table_offset);
|
||||
@@ -106,49 +94,25 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||
id_str_size = be16_to_cpu(h.id_str_size);
|
||||
name_size = be16_to_cpu(h.name_size);
|
||||
|
||||
/* Read extra data */
|
||||
ret = bdrv_pread(bs->file, offset, &extra,
|
||||
MIN(sizeof(extra), extra_data_size));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += extra_data_size;
|
||||
|
||||
if (extra_data_size >= 8) {
|
||||
sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large);
|
||||
}
|
||||
|
||||
if (extra_data_size >= 16) {
|
||||
sn->disk_size = be64_to_cpu(extra.disk_size);
|
||||
} else {
|
||||
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Read snapshot ID */
|
||||
sn->id_str = g_malloc(id_str_size + 1);
|
||||
ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size);
|
||||
if (ret < 0) {
|
||||
if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size)
|
||||
goto fail;
|
||||
}
|
||||
offset += id_str_size;
|
||||
sn->id_str[id_str_size] = '\0';
|
||||
|
||||
/* Read snapshot name */
|
||||
sn->name = g_malloc(name_size + 1);
|
||||
ret = bdrv_pread(bs->file, offset, sn->name, name_size);
|
||||
if (ret < 0) {
|
||||
if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size)
|
||||
goto fail;
|
||||
}
|
||||
offset += name_size;
|
||||
sn->name[name_size] = '\0';
|
||||
}
|
||||
|
||||
s->snapshots_size = offset - s->snapshots_offset;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fail:
|
||||
qcow2_free_snapshots(bs);
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* add at the end of the file a new list of snapshots */
|
||||
@@ -157,14 +121,10 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
QCowSnapshotHeader h;
|
||||
QCowSnapshotExtraData extra;
|
||||
int i, name_size, id_str_size, snapshots_size;
|
||||
struct {
|
||||
uint32_t nb_snapshots;
|
||||
uint64_t snapshots_offset;
|
||||
} QEMU_PACKED header_data;
|
||||
uint64_t data64;
|
||||
uint32_t data32;
|
||||
int64_t offset, snapshots_offset;
|
||||
int ret;
|
||||
|
||||
/* compute the size of the snapshots */
|
||||
offset = 0;
|
||||
@@ -172,13 +132,11 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
sn = s->snapshots + i;
|
||||
offset = align_offset(offset, 8);
|
||||
offset += sizeof(h);
|
||||
offset += sizeof(extra);
|
||||
offset += strlen(sn->id_str);
|
||||
offset += strlen(sn->name);
|
||||
}
|
||||
snapshots_size = offset;
|
||||
|
||||
/* Allocate space for the new snapshot list */
|
||||
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
|
||||
bdrv_flush(bs->file);
|
||||
offset = snapshots_offset;
|
||||
@@ -186,86 +144,49 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Write all snapshots to the new list */
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
sn = s->snapshots + i;
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
|
||||
h.l1_size = cpu_to_be32(sn->l1_size);
|
||||
/* If it doesn't fit in 32 bit, older implementations should treat it
|
||||
* as a disk-only snapshot rather than truncate the VM state */
|
||||
if (sn->vm_state_size <= 0xffffffff) {
|
||||
h.vm_state_size = cpu_to_be32(sn->vm_state_size);
|
||||
}
|
||||
h.vm_state_size = cpu_to_be32(sn->vm_state_size);
|
||||
h.date_sec = cpu_to_be32(sn->date_sec);
|
||||
h.date_nsec = cpu_to_be32(sn->date_nsec);
|
||||
h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
|
||||
h.extra_data_size = cpu_to_be32(sizeof(extra));
|
||||
|
||||
memset(&extra, 0, sizeof(extra));
|
||||
extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size);
|
||||
extra.disk_size = cpu_to_be64(sn->disk_size);
|
||||
|
||||
id_str_size = strlen(sn->id_str);
|
||||
name_size = strlen(sn->name);
|
||||
h.id_str_size = cpu_to_be16(id_str_size);
|
||||
h.name_size = cpu_to_be16(name_size);
|
||||
offset = align_offset(offset, 8);
|
||||
|
||||
ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h));
|
||||
if (ret < 0) {
|
||||
if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
offset += sizeof(h);
|
||||
|
||||
ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra));
|
||||
if (ret < 0) {
|
||||
if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0)
|
||||
goto fail;
|
||||
}
|
||||
offset += sizeof(extra);
|
||||
|
||||
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, offset, sn->name, name_size);
|
||||
if (ret < 0) {
|
||||
if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0)
|
||||
goto fail;
|
||||
}
|
||||
offset += name_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the header to point to the new snapshot table. This requires the
|
||||
* new table and its refcounts to be stable on disk.
|
||||
*/
|
||||
ret = bdrv_flush(bs);
|
||||
if (ret < 0) {
|
||||
/* update the various header fields */
|
||||
data64 = cpu_to_be64(snapshots_offset);
|
||||
if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset),
|
||||
&data64, sizeof(data64)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) !=
|
||||
offsetof(QCowHeader, nb_snapshots) + sizeof(header_data.nb_snapshots));
|
||||
|
||||
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, offsetof(QCowHeader, nb_snapshots),
|
||||
&header_data, sizeof(header_data));
|
||||
if (ret < 0) {
|
||||
data32 = cpu_to_be32(s->nb_snapshots);
|
||||
if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
|
||||
&data32, sizeof(data32)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* free the old snapshot table */
|
||||
qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size);
|
||||
s->snapshots_offset = snapshots_offset;
|
||||
s->snapshots_size = snapshots_size;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void find_new_snapshot_id(BlockDriverState *bs,
|
||||
@@ -315,93 +236,72 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *new_snapshot_list = NULL;
|
||||
QCowSnapshot *old_snapshot_list = NULL;
|
||||
QCowSnapshot sn1, *sn = &sn1;
|
||||
QCowSnapshot *snapshots1, sn1, *sn = &sn1;
|
||||
int i, ret;
|
||||
uint64_t *l1_table = NULL;
|
||||
int64_t l1_table_offset;
|
||||
|
||||
memset(sn, 0, sizeof(*sn));
|
||||
|
||||
/* Generate an ID if it wasn't passed */
|
||||
if (sn_info->id_str[0] == '\0') {
|
||||
/* compute a new id */
|
||||
find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
|
||||
}
|
||||
|
||||
/* Check that the ID is unique */
|
||||
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) {
|
||||
return -EEXIST;
|
||||
}
|
||||
/* check that the ID is unique */
|
||||
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* Populate sn with passed data */
|
||||
sn->id_str = g_strdup(sn_info->id_str);
|
||||
if (!sn->id_str)
|
||||
goto fail;
|
||||
sn->name = g_strdup(sn_info->name);
|
||||
|
||||
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
if (!sn->name)
|
||||
goto fail;
|
||||
sn->vm_state_size = sn_info->vm_state_size;
|
||||
sn->date_sec = sn_info->date_sec;
|
||||
sn->date_nsec = sn_info->date_nsec;
|
||||
sn->vm_clock_nsec = sn_info->vm_clock_nsec;
|
||||
|
||||
/* Allocate the L1 table of the snapshot and copy the current one there. */
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
/* create the L1 table of the snapshot */
|
||||
l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
|
||||
if (l1_table_offset < 0) {
|
||||
ret = l1_table_offset;
|
||||
goto fail;
|
||||
}
|
||||
bdrv_flush(bs->file);
|
||||
|
||||
sn->l1_table_offset = l1_table_offset;
|
||||
sn->l1_size = s->l1_size;
|
||||
|
||||
l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
||||
if (s->l1_size != 0) {
|
||||
l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
||||
} else {
|
||||
l1_table = NULL;
|
||||
}
|
||||
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
l1_table[i] = cpu_to_be64(s->l1_table[i]);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset,
|
||||
l1_table, s->l1_size * sizeof(uint64_t)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_free(l1_table);
|
||||
l1_table = NULL;
|
||||
|
||||
/*
|
||||
* Increase the refcounts of all clusters and make sure everything is
|
||||
* stable on disk before updating the snapshot table to contain a pointer
|
||||
* to the new L1 table.
|
||||
*/
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_flush(bs);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Append the new snapshot to the snapshot list */
|
||||
new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
|
||||
snapshots1 = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
|
||||
if (s->snapshots) {
|
||||
memcpy(new_snapshot_list, s->snapshots,
|
||||
s->nb_snapshots * sizeof(QCowSnapshot));
|
||||
old_snapshot_list = s->snapshots;
|
||||
memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
|
||||
g_free(s->snapshots);
|
||||
}
|
||||
s->snapshots = new_snapshot_list;
|
||||
s->snapshots = snapshots1;
|
||||
s->snapshots[s->nb_snapshots++] = *sn;
|
||||
|
||||
ret = qcow2_write_snapshots(bs);
|
||||
if (ret < 0) {
|
||||
g_free(s->snapshots);
|
||||
s->snapshots = old_snapshot_list;
|
||||
if (qcow2_write_snapshots(bs) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_free(old_snapshot_list);
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
@@ -409,13 +309,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
g_free(sn->id_str);
|
||||
fail:
|
||||
g_free(sn->name);
|
||||
g_free(l1_table);
|
||||
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the snapshot 'snapshot_name' into the current disk image */
|
||||
@@ -425,99 +322,38 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
QCowSnapshot *sn;
|
||||
int i, snapshot_index;
|
||||
int cur_l1_bytes, sn_l1_bytes;
|
||||
int ret;
|
||||
uint64_t *sn_l1_table = NULL;
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
|
||||
if (snapshot_index < 0) {
|
||||
if (snapshot_index < 0)
|
||||
return -ENOENT;
|
||||
}
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
|
||||
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||
error_report("qcow2: Loading snapshots with different disk "
|
||||
"size is not implemented");
|
||||
ret = -ENOTSUP;
|
||||
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that the current L1 table is big enough to contain the whole
|
||||
* L1 table of the snapshot. If the snapshot L1 table is smaller, the
|
||||
* current one must be padded with zeros.
|
||||
*/
|
||||
ret = qcow2_grow_l1_table(bs, sn->l1_size, true);
|
||||
if (ret < 0) {
|
||||
if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cur_l1_bytes = s->l1_size * sizeof(uint64_t);
|
||||
sn_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||
|
||||
/*
|
||||
* Copy the snapshot L1 table to the current L1 table.
|
||||
*
|
||||
* Before overwriting the old current L1 table on disk, make sure to
|
||||
* increase all refcounts for the clusters referenced by the new one.
|
||||
* Decrease the refcount referenced by the old one only when the L1
|
||||
* table is overwritten.
|
||||
*/
|
||||
sn_l1_table = g_malloc0(cur_l1_bytes);
|
||||
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
if (cur_l1_bytes > sn_l1_bytes) {
|
||||
memset(s->l1_table + sn->l1_size, 0, cur_l1_bytes - sn_l1_bytes);
|
||||
}
|
||||
|
||||
ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset,
|
||||
sn->l1_size, 1);
|
||||
if (ret < 0) {
|
||||
/* copy the snapshot l1 table to the current l1 table */
|
||||
if (bdrv_pread(bs->file, sn->l1_table_offset,
|
||||
s->l1_table, sn_l1_bytes) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table,
|
||||
cur_l1_bytes);
|
||||
if (ret < 0) {
|
||||
if (bdrv_pwrite_sync(bs->file, s->l1_table_offset,
|
||||
s->l1_table, cur_l1_bytes) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrease refcount of clusters of current L1 table.
|
||||
*
|
||||
* At this point, the in-memory s->l1_table points to the old L1 table,
|
||||
* whereas on disk we already have the new one.
|
||||
*
|
||||
* qcow2_update_snapshot_refcount special cases the current L1 table to use
|
||||
* the in-memory data instead of really using the offset to load a new one,
|
||||
* which is why this works.
|
||||
*/
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset,
|
||||
s->l1_size, -1);
|
||||
|
||||
/*
|
||||
* Now update the in-memory L1 table to be in sync with the on-disk one. We
|
||||
* need to do this even if updating refcounts failed.
|
||||
*/
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
s->l1_table[i] = be64_to_cpu(sn_l1_table[i]);
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_free(sn_l1_table);
|
||||
sn_l1_table = NULL;
|
||||
|
||||
/*
|
||||
* Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed
|
||||
* when we decreased the refcount of the old snapshot.
|
||||
*/
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
@@ -526,59 +362,39 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
g_free(sn_l1_table);
|
||||
return ret;
|
||||
fail:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot sn;
|
||||
QCowSnapshot *sn;
|
||||
int snapshot_index, ret;
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
|
||||
if (snapshot_index < 0) {
|
||||
if (snapshot_index < 0)
|
||||
return -ENOENT;
|
||||
}
|
||||
sn = s->snapshots[snapshot_index];
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
|
||||
/* Remove it from the snapshot list */
|
||||
memmove(s->snapshots + snapshot_index,
|
||||
s->snapshots + snapshot_index + 1,
|
||||
(s->nb_snapshots - snapshot_index - 1) * sizeof(sn));
|
||||
ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* must update the copied flag on the current cluster offsets */
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t));
|
||||
|
||||
g_free(sn->id_str);
|
||||
g_free(sn->name);
|
||||
memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
|
||||
s->nb_snapshots--;
|
||||
ret = qcow2_write_snapshots(bs);
|
||||
if (ret < 0) {
|
||||
/* XXX: restore snapshot if error ? */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The snapshot is now unused, clean up. If we fail after this point, we
|
||||
* won't recover but just leak clusters.
|
||||
*/
|
||||
g_free(sn.id_str);
|
||||
g_free(sn.name);
|
||||
|
||||
/*
|
||||
* Now decrease the refcounts of clusters referenced by the snapshot and
|
||||
* free the L1 table.
|
||||
*/
|
||||
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
|
||||
sn.l1_size, -1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t));
|
||||
|
||||
/* must update the copied flag on the current cluster offsets */
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
@@ -619,42 +435,32 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||
{
|
||||
int i, snapshot_index;
|
||||
int i, snapshot_index, l1_size2;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
uint64_t *new_l1_table;
|
||||
int new_l1_bytes;
|
||||
int ret;
|
||||
|
||||
assert(bs->read_only);
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
|
||||
if (snapshot_index < 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
|
||||
/* Allocate and read in the snapshot's L1 table */
|
||||
new_l1_bytes = s->l1_size * sizeof(uint64_t);
|
||||
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
|
||||
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
|
||||
if (ret < 0) {
|
||||
g_free(new_l1_table);
|
||||
return ret;
|
||||
s->l1_size = sn->l1_size;
|
||||
l1_size2 = s->l1_size * sizeof(uint64_t);
|
||||
if (s->l1_table != NULL) {
|
||||
g_free(s->l1_table);
|
||||
}
|
||||
|
||||
/* Switch the L1 table */
|
||||
g_free(s->l1_table);
|
||||
|
||||
s->l1_size = sn->l1_size;
|
||||
s->l1_table_offset = sn->l1_table_offset;
|
||||
s->l1_table = new_l1_table;
|
||||
s->l1_table = g_malloc0(align_offset(l1_size2, 512));
|
||||
|
||||
if (bdrv_pread(bs->file, sn->l1_table_offset,
|
||||
s->l1_table, l1_size2) != l1_size2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+103
-401
@@ -29,7 +29,6 @@
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu-error.h"
|
||||
#include "qerror.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*
|
||||
Differences with QCOW:
|
||||
@@ -54,7 +53,6 @@ typedef struct {
|
||||
} QCowExtension;
|
||||
#define QCOW2_EXT_MAGIC_END 0
|
||||
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
|
||||
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
|
||||
|
||||
static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
@@ -62,7 +60,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
|
||||
if (buf_size >= sizeof(QCowHeader) &&
|
||||
be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) >= 2)
|
||||
be32_to_cpu(cow_header->version) >= QCOW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
@@ -77,12 +75,10 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
* return 0 upon success, non-0 otherwise
|
||||
*/
|
||||
static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
uint64_t end_offset, void **p_feature_table)
|
||||
uint64_t end_offset)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowExtension ext;
|
||||
uint64_t offset;
|
||||
int ret;
|
||||
|
||||
#ifdef DEBUG_EXT
|
||||
printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset);
|
||||
@@ -91,11 +87,12 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
while (offset < end_offset) {
|
||||
|
||||
#ifdef DEBUG_EXT
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
/* Sanity check */
|
||||
if (offset > s->cluster_size)
|
||||
printf("qcow2_read_extension: suspicious offset %lu\n", offset);
|
||||
|
||||
printf("attempting to read extended header in offset %lu\n", offset);
|
||||
printf("attemting to read extended header in offset %lu\n", offset);
|
||||
#endif
|
||||
|
||||
if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) {
|
||||
@@ -110,11 +107,6 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
#ifdef DEBUG_EXT
|
||||
printf("ext.magic = 0x%x\n", ext.magic);
|
||||
#endif
|
||||
if (ext.len > end_offset - offset) {
|
||||
error_report("Header extension too large");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (ext.magic) {
|
||||
case QCOW2_EXT_MAGIC_END:
|
||||
return 0;
|
||||
@@ -133,86 +125,19 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
#ifdef DEBUG_EXT
|
||||
printf("Qcow2: Got format extension %s\n", bs->backing_format);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case QCOW2_EXT_MAGIC_FEATURE_TABLE:
|
||||
if (p_feature_table != NULL) {
|
||||
void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature));
|
||||
ret = bdrv_pread(bs->file, offset , feature_table, ext.len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*p_feature_table = feature_table;
|
||||
}
|
||||
offset = ((offset + ext.len + 7) & ~7);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown magic - save it in case we need to rewrite the header */
|
||||
{
|
||||
Qcow2UnknownHeaderExtension *uext;
|
||||
|
||||
uext = g_malloc0(sizeof(*uext) + ext.len);
|
||||
uext->magic = ext.magic;
|
||||
uext->len = ext.len;
|
||||
QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next);
|
||||
|
||||
ret = bdrv_pread(bs->file, offset , uext->data, uext->len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* unknown magic -- just skip it */
|
||||
offset = ((offset + ext.len + 7) & ~7);
|
||||
break;
|
||||
}
|
||||
|
||||
offset += ((ext.len + 7) & ~7);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup_unknown_header_ext(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
Qcow2UnknownHeaderExtension *uext, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) {
|
||||
QLIST_REMOVE(uext, next);
|
||||
g_free(uext);
|
||||
}
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char msg[64];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "qcow2", msg);
|
||||
}
|
||||
|
||||
static void report_unsupported_feature(BlockDriverState *bs,
|
||||
Qcow2Feature *table, uint64_t mask)
|
||||
{
|
||||
while (table && table->name[0] != '\0') {
|
||||
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
|
||||
if (mask & (1 << table->bit)) {
|
||||
report_unsupported(bs, "%.46s",table->name);
|
||||
mask &= ~(1 << table->bit);
|
||||
}
|
||||
}
|
||||
table++;
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask);
|
||||
}
|
||||
}
|
||||
|
||||
static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
{
|
||||
@@ -244,68 +169,14 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (header.version < 2 || header.version > 3) {
|
||||
report_unsupported(bs, "QCOW version %d", header.version);
|
||||
if (header.version != QCOW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "qcow2", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->qcow_version = header.version;
|
||||
|
||||
/* Initialise version 3 header fields */
|
||||
if (header.version == 2) {
|
||||
header.incompatible_features = 0;
|
||||
header.compatible_features = 0;
|
||||
header.autoclear_features = 0;
|
||||
header.refcount_order = 4;
|
||||
header.header_length = 72;
|
||||
} else {
|
||||
be64_to_cpus(&header.incompatible_features);
|
||||
be64_to_cpus(&header.compatible_features);
|
||||
be64_to_cpus(&header.autoclear_features);
|
||||
be32_to_cpus(&header.refcount_order);
|
||||
be32_to_cpus(&header.header_length);
|
||||
}
|
||||
|
||||
if (header.header_length > sizeof(header)) {
|
||||
s->unknown_header_fields_size = header.header_length - sizeof(header);
|
||||
s->unknown_header_fields = g_malloc(s->unknown_header_fields_size);
|
||||
ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields,
|
||||
s->unknown_header_fields_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (header.backing_file_offset) {
|
||||
ext_end = header.backing_file_offset;
|
||||
} else {
|
||||
ext_end = 1 << header.cluster_bits;
|
||||
}
|
||||
|
||||
/* Handle feature bits */
|
||||
s->incompatible_features = header.incompatible_features;
|
||||
s->compatible_features = header.compatible_features;
|
||||
s->autoclear_features = header.autoclear_features;
|
||||
|
||||
if (s->incompatible_features != 0) {
|
||||
void *feature_table = NULL;
|
||||
qcow2_read_extensions(bs, header.header_length, ext_end,
|
||||
&feature_table);
|
||||
report_unsupported_feature(bs, feature_table,
|
||||
s->incompatible_features);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check support for various header values */
|
||||
if (header.refcount_order != 4) {
|
||||
report_unsupported(bs, "%d bit reference counts",
|
||||
1 << header.refcount_order);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (header.cluster_bits < MIN_CLUSTER_BITS ||
|
||||
header.cluster_bits > MAX_CLUSTER_BITS) {
|
||||
ret = -EINVAL;
|
||||
@@ -379,7 +250,12 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
QLIST_INIT(&s->cluster_allocs);
|
||||
|
||||
/* read qcow2 extensions */
|
||||
if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) {
|
||||
if (header.backing_file_offset) {
|
||||
ext_end = header.backing_file_offset;
|
||||
} else {
|
||||
ext_end = s->cluster_size;
|
||||
}
|
||||
if (qcow2_read_extensions(bs, sizeof(header), ext_end)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -397,21 +273,11 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
}
|
||||
bs->backing_file[len] = '\0';
|
||||
}
|
||||
|
||||
ret = qcow2_read_snapshots(bs);
|
||||
if (ret < 0) {
|
||||
if (qcow2_read_snapshots(bs) < 0) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Clear unknown autoclear feature bits */
|
||||
if (!bs->read_only && s->autoclear_features != 0) {
|
||||
s->autoclear_features = 0;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialise locks */
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
@@ -424,8 +290,6 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
g_free(s->unknown_header_fields);
|
||||
cleanup_unknown_header_ext(bs);
|
||||
qcow2_free_snapshots(bs);
|
||||
qcow2_refcount_close(bs);
|
||||
g_free(s->l1_table);
|
||||
@@ -479,19 +343,16 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
static int qcow2_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t cluster_offset;
|
||||
int ret;
|
||||
|
||||
*pnum = nb_sectors;
|
||||
/* FIXME We can get errors here, but the bdrv_co_is_allocated interface
|
||||
* can't pass them on today */
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
/* FIXME We can get errors here, but the bdrv_is_allocated interface can't
|
||||
* pass them on today */
|
||||
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (ret < 0) {
|
||||
*pnum = 0;
|
||||
}
|
||||
@@ -516,7 +377,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
return n1;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int remaining_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -553,8 +414,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
qemu_iovec_copy(&hd_qiov, qiov, bytes_done,
|
||||
cur_nr_sectors * 512);
|
||||
|
||||
switch (ret) {
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (!cluster_offset) {
|
||||
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
@@ -574,17 +434,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
/* Note: in this case, no need to wait */
|
||||
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* add AIO support for compressed blocks ? */
|
||||
ret = qcow2_decompress_cluster(bs, cluster_offset);
|
||||
if (ret < 0) {
|
||||
@@ -594,9 +444,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
qemu_iovec_from_buffer(&hd_qiov,
|
||||
s->cluster_cache + index_in_cluster * 512,
|
||||
512 * cur_nr_sectors);
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
} else {
|
||||
if ((cluster_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
@@ -637,12 +485,6 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
qemu_iovec_from_buffer(&hd_qiov, cluster_data,
|
||||
512 * cur_nr_sectors);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
remaining_sectors -= cur_nr_sectors;
|
||||
@@ -670,12 +512,12 @@ static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m)
|
||||
/* Restart all dependent requests */
|
||||
if (!qemu_co_queue_empty(&m->dependent_requests)) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_restart_all(&m->dependent_requests);
|
||||
while(qemu_co_queue_next(&m->dependent_requests));
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
static int qcow2_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int remaining_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
@@ -693,9 +535,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
.nb_clusters = 0,
|
||||
};
|
||||
|
||||
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num,
|
||||
remaining_sectors);
|
||||
|
||||
qemu_co_queue_init(&l2meta.dependent_requests);
|
||||
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
@@ -706,7 +545,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
|
||||
while (remaining_sectors != 0) {
|
||||
|
||||
trace_qcow2_writev_start_part(qemu_coroutine_self());
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n_end = index_in_cluster + remaining_sectors;
|
||||
if (s->crypt_method &&
|
||||
@@ -747,8 +585,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
trace_qcow2_writev_data(qemu_coroutine_self(),
|
||||
(cluster_offset >> 9) + index_in_cluster);
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
cur_nr_sectors, &hd_qiov);
|
||||
@@ -767,7 +603,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
remaining_sectors -= cur_nr_sectors;
|
||||
sector_num += cur_nr_sectors;
|
||||
bytes_done += cur_nr_sectors * 512;
|
||||
trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_nr_sectors);
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
@@ -778,7 +613,6 @@ fail:
|
||||
|
||||
qemu_iovec_destroy(&hd_qiov);
|
||||
qemu_vfree(cluster_data);
|
||||
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -794,13 +628,9 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||
qcow2_cache_destroy(bs, s->refcount_block_cache);
|
||||
|
||||
g_free(s->unknown_header_fields);
|
||||
cleanup_unknown_header_ext(bs);
|
||||
|
||||
g_free(s->cluster_cache);
|
||||
qemu_vfree(s->cluster_data);
|
||||
qcow2_refcount_close(bs);
|
||||
qcow2_free_snapshots(bs);
|
||||
}
|
||||
|
||||
static void qcow2_invalidate_cache(BlockDriverState *bs)
|
||||
@@ -834,192 +664,103 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
|
||||
size_t len, size_t buflen)
|
||||
{
|
||||
QCowExtension *ext_backing_fmt = (QCowExtension*) buf;
|
||||
size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7);
|
||||
|
||||
if (buflen < ext_len) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
*ext_backing_fmt = (QCowExtension) {
|
||||
.magic = cpu_to_be32(magic),
|
||||
.len = cpu_to_be32(len),
|
||||
};
|
||||
memcpy(buf + sizeof(QCowExtension), s, len);
|
||||
|
||||
return ext_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the qcow2 header, including the variable length parts of it, i.e.
|
||||
* the backing file name and all extensions. qcow2 was not designed to allow
|
||||
* such changes, so if we run out of space (we can only use the first cluster)
|
||||
* this function may fail.
|
||||
* Updates the variable length parts of the qcow2 header, i.e. the backing file
|
||||
* name and all extensions. qcow2 was not designed to allow such changes, so if
|
||||
* we run out of space (we can only use the first cluster) this function may
|
||||
* fail.
|
||||
*
|
||||
* Returns 0 on success, -errno in error cases.
|
||||
*/
|
||||
int qcow2_update_header(BlockDriverState *bs)
|
||||
static int qcow2_update_ext_header(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt)
|
||||
{
|
||||
size_t backing_file_len = 0;
|
||||
size_t backing_fmt_len = 0;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowHeader *header;
|
||||
char *buf;
|
||||
size_t buflen = s->cluster_size;
|
||||
QCowExtension ext_backing_fmt = {0, 0};
|
||||
int ret;
|
||||
uint64_t total_size;
|
||||
uint32_t refcount_table_clusters;
|
||||
size_t header_length;
|
||||
Qcow2UnknownHeaderExtension *uext;
|
||||
|
||||
buf = qemu_blockalign(bs, buflen);
|
||||
|
||||
/* Header structure */
|
||||
header = (QCowHeader*) buf;
|
||||
|
||||
if (buflen < sizeof(*header)) {
|
||||
ret = -ENOSPC;
|
||||
goto fail;
|
||||
/* Backing file format doesn't make sense without a backing file */
|
||||
if (backing_fmt && !backing_file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
header_length = sizeof(*header) + s->unknown_header_fields_size;
|
||||
total_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);
|
||||
|
||||
*header = (QCowHeader) {
|
||||
/* Version 2 fields */
|
||||
.magic = cpu_to_be32(QCOW_MAGIC),
|
||||
.version = cpu_to_be32(s->qcow_version),
|
||||
.backing_file_offset = 0,
|
||||
.backing_file_size = 0,
|
||||
.cluster_bits = cpu_to_be32(s->cluster_bits),
|
||||
.size = cpu_to_be64(total_size),
|
||||
.crypt_method = cpu_to_be32(s->crypt_method_header),
|
||||
.l1_size = cpu_to_be32(s->l1_size),
|
||||
.l1_table_offset = cpu_to_be64(s->l1_table_offset),
|
||||
.refcount_table_offset = cpu_to_be64(s->refcount_table_offset),
|
||||
.refcount_table_clusters = cpu_to_be32(refcount_table_clusters),
|
||||
.nb_snapshots = cpu_to_be32(s->nb_snapshots),
|
||||
.snapshots_offset = cpu_to_be64(s->snapshots_offset),
|
||||
|
||||
/* Version 3 fields */
|
||||
.incompatible_features = cpu_to_be64(s->incompatible_features),
|
||||
.compatible_features = cpu_to_be64(s->compatible_features),
|
||||
.autoclear_features = cpu_to_be64(s->autoclear_features),
|
||||
.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT),
|
||||
.header_length = cpu_to_be32(header_length),
|
||||
};
|
||||
|
||||
/* For older versions, write a shorter header */
|
||||
switch (s->qcow_version) {
|
||||
case 2:
|
||||
ret = offsetof(QCowHeader, incompatible_features);
|
||||
break;
|
||||
case 3:
|
||||
ret = sizeof(*header);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
/* Prepare the backing file format extension if needed */
|
||||
if (backing_fmt) {
|
||||
ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt));
|
||||
ext_backing_fmt.magic = cpu_to_be32(QCOW2_EXT_MAGIC_BACKING_FORMAT);
|
||||
backing_fmt_len = ((sizeof(ext_backing_fmt)
|
||||
+ strlen(backing_fmt) + 7) & ~7);
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
memset(buf, 0, buflen);
|
||||
/* Check if we can fit the new header into the first cluster */
|
||||
if (backing_file) {
|
||||
backing_file_len = strlen(backing_file);
|
||||
}
|
||||
|
||||
/* Preserve any unknown field in the header */
|
||||
if (s->unknown_header_fields_size) {
|
||||
if (buflen < s->unknown_header_fields_size) {
|
||||
ret = -ENOSPC;
|
||||
goto fail;
|
||||
size_t header_size = sizeof(QCowHeader) + backing_file_len
|
||||
+ backing_fmt_len;
|
||||
|
||||
if (header_size > s->cluster_size) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Rewrite backing file name and qcow2 extensions */
|
||||
size_t ext_size = header_size - sizeof(QCowHeader);
|
||||
uint8_t buf[ext_size];
|
||||
size_t offset = 0;
|
||||
size_t backing_file_offset = 0;
|
||||
|
||||
if (backing_file) {
|
||||
if (backing_fmt) {
|
||||
int padding = backing_fmt_len -
|
||||
(sizeof(ext_backing_fmt) + strlen(backing_fmt));
|
||||
|
||||
memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt));
|
||||
offset += sizeof(ext_backing_fmt);
|
||||
|
||||
memcpy(buf + offset, backing_fmt, strlen(backing_fmt));
|
||||
offset += strlen(backing_fmt);
|
||||
|
||||
memset(buf + offset, 0, padding);
|
||||
offset += padding;
|
||||
}
|
||||
|
||||
memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size);
|
||||
buf += s->unknown_header_fields_size;
|
||||
buflen -= s->unknown_header_fields_size;
|
||||
memcpy(buf + offset, backing_file, backing_file_len);
|
||||
backing_file_offset = sizeof(QCowHeader) + offset;
|
||||
}
|
||||
|
||||
/* Backing file format header extension */
|
||||
if (*bs->backing_format) {
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT,
|
||||
bs->backing_format, strlen(bs->backing_format),
|
||||
buflen);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
}
|
||||
|
||||
/* Feature table */
|
||||
Qcow2Feature features[] = {
|
||||
/* no feature defined yet */
|
||||
};
|
||||
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
|
||||
features, sizeof(features), buflen);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
|
||||
/* Keep unknown header extensions */
|
||||
QLIST_FOREACH(uext, &s->unknown_header_ext, next) {
|
||||
ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
}
|
||||
|
||||
/* End of header extensions */
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen);
|
||||
ret = bdrv_pwrite_sync(bs->file, sizeof(QCowHeader), buf, ext_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
/* Update header fields */
|
||||
uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset);
|
||||
uint32_t be_backing_file_size = cpu_to_be32(backing_file_len);
|
||||
|
||||
/* Backing file name */
|
||||
if (*bs->backing_file) {
|
||||
size_t backing_file_len = strlen(bs->backing_file);
|
||||
|
||||
if (buflen < backing_file_len) {
|
||||
ret = -ENOSPC;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strncpy(buf, bs->backing_file, buflen);
|
||||
|
||||
header->backing_file_offset = cpu_to_be64(buf - ((char*) header));
|
||||
header->backing_file_size = cpu_to_be32(backing_file_len);
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_offset),
|
||||
&be_backing_file_offset, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Write the new header */
|
||||
ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size);
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_size),
|
||||
&be_backing_file_size, sizeof(uint32_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_vfree(header);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_change_backing_file(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt)
|
||||
{
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
|
||||
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
|
||||
|
||||
return qcow2_update_header(bs);
|
||||
return qcow2_update_ext_header(bs, backing_file, backing_fmt);
|
||||
}
|
||||
|
||||
static int preallocate(BlockDriverState *bs)
|
||||
@@ -1078,9 +819,9 @@ static int preallocate(BlockDriverState *bs)
|
||||
static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
const char *backing_file, const char *backing_format,
|
||||
int flags, size_t cluster_size, int prealloc,
|
||||
QEMUOptionParameter *options, int version)
|
||||
QEMUOptionParameter *options)
|
||||
{
|
||||
/* Calculate cluster_bits */
|
||||
/* Calulate cluster_bits */
|
||||
int cluster_bits;
|
||||
cluster_bits = ffs(cluster_size) - 1;
|
||||
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
|
||||
@@ -1122,15 +863,13 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
/* Write the header */
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(version);
|
||||
header.version = cpu_to_be32(QCOW_VERSION);
|
||||
header.cluster_bits = cpu_to_be32(cluster_bits);
|
||||
header.size = cpu_to_be64(0);
|
||||
header.l1_table_offset = cpu_to_be64(0);
|
||||
header.l1_size = cpu_to_be32(0);
|
||||
header.refcount_table_offset = cpu_to_be64(cluster_size);
|
||||
header.refcount_table_clusters = cpu_to_be32(1);
|
||||
header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT);
|
||||
header.header_length = cpu_to_be32(sizeof(header));
|
||||
|
||||
if (flags & BLOCK_FLAG_ENCRYPT) {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
@@ -1192,10 +931,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
|
||||
/* And if we're supposed to preallocate metadata, do that now */
|
||||
if (prealloc) {
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = preallocate(bs);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -1215,7 +951,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
|
||||
int flags = 0;
|
||||
size_t cluster_size = DEFAULT_CLUSTER_SIZE;
|
||||
int prealloc = 0;
|
||||
int version = 2;
|
||||
|
||||
/* Read out options */
|
||||
while (options && options->name) {
|
||||
@@ -1241,16 +976,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
|
||||
options->value.s);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
|
||||
if (!options->value.s || !strcmp(options->value.s, "0.10")) {
|
||||
version = 2;
|
||||
} else if (!strcmp(options->value.s, "1.1")) {
|
||||
version = 3;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid compatibility level: '%s'\n",
|
||||
options->value.s);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
options++;
|
||||
}
|
||||
@@ -1262,7 +987,7 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
|
||||
return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
|
||||
cluster_size, prealloc, options, version);
|
||||
cluster_size, prealloc, options);
|
||||
}
|
||||
|
||||
static int qcow2_make_empty(BlockDriverState *bs)
|
||||
@@ -1285,26 +1010,6 @@ static int qcow2_make_empty(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
/* Emulate misaligned zero writes */
|
||||
if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Whatever is left can use real zero clusters */
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_zero_clusters(bs, sector_num << BDRV_SECTOR_BITS,
|
||||
nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
@@ -1324,19 +1029,16 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
||||
int ret, new_l1_size;
|
||||
|
||||
if (offset & 511) {
|
||||
error_report("The new size must be a multiple of 512");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* cannot proceed if image has snapshots */
|
||||
if (s->nb_snapshots) {
|
||||
error_report("Can't resize an image which has snapshots");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* shrinking is currently not supported */
|
||||
if (offset < bs->total_sectors * 512) {
|
||||
error_report("qcow2 doesn't support shrinking images yet");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -1435,7 +1137,7 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
static int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
@@ -1457,6 +1159,11 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow2_co_flush_to_disk(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
|
||||
{
|
||||
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||
@@ -1533,11 +1240,6 @@ static QEMUOptionParameter qcow2_create_options[] = {
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_COMPAT_LEVEL,
|
||||
.type = OPT_STRING,
|
||||
.help = "Compatibility level (0.10 or 1.1)"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = OPT_STRING,
|
||||
@@ -1574,15 +1276,15 @@ static BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_open = qcow2_open,
|
||||
.bdrv_close = qcow2_close,
|
||||
.bdrv_create = qcow2_create,
|
||||
.bdrv_co_is_allocated = qcow2_co_is_allocated,
|
||||
.bdrv_is_allocated = qcow2_is_allocated,
|
||||
.bdrv_set_key = qcow2_set_key,
|
||||
.bdrv_make_empty = qcow2_make_empty,
|
||||
|
||||
.bdrv_co_readv = qcow2_co_readv,
|
||||
.bdrv_co_writev = qcow2_co_writev,
|
||||
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
|
||||
.bdrv_co_flush_to_disk = qcow2_co_flush_to_disk,
|
||||
|
||||
.bdrv_co_write_zeroes = qcow2_co_write_zeroes,
|
||||
.bdrv_co_discard = qcow2_co_discard,
|
||||
.bdrv_truncate = qcow2_truncate,
|
||||
.bdrv_write_compressed = qcow2_write_compressed,
|
||||
|
||||
+2
-71
@@ -33,6 +33,7 @@
|
||||
//#define DEBUG_EXT
|
||||
|
||||
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
|
||||
#define QCOW_VERSION 2
|
||||
|
||||
#define QCOW_CRYPT_NONE 0
|
||||
#define QCOW_CRYPT_AES 1
|
||||
@@ -43,8 +44,6 @@
|
||||
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
|
||||
/* The cluster reads as all zeros */
|
||||
#define QCOW_OFLAG_ZERO (1LL << 0)
|
||||
|
||||
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
||||
|
||||
@@ -72,14 +71,6 @@ typedef struct QCowHeader {
|
||||
uint32_t refcount_table_clusters;
|
||||
uint32_t nb_snapshots;
|
||||
uint64_t snapshots_offset;
|
||||
|
||||
/* The following fields are only valid for version >= 3 */
|
||||
uint64_t incompatible_features;
|
||||
uint64_t compatible_features;
|
||||
uint64_t autoclear_features;
|
||||
|
||||
uint32_t refcount_order;
|
||||
uint32_t header_length;
|
||||
} QCowHeader;
|
||||
|
||||
typedef struct QCowSnapshot {
|
||||
@@ -87,8 +78,7 @@ typedef struct QCowSnapshot {
|
||||
uint32_t l1_size;
|
||||
char *id_str;
|
||||
char *name;
|
||||
uint64_t disk_size;
|
||||
uint64_t vm_state_size;
|
||||
uint32_t vm_state_size;
|
||||
uint32_t date_sec;
|
||||
uint32_t date_nsec;
|
||||
uint64_t vm_clock_nsec;
|
||||
@@ -97,25 +87,6 @@ typedef struct QCowSnapshot {
|
||||
struct Qcow2Cache;
|
||||
typedef struct Qcow2Cache Qcow2Cache;
|
||||
|
||||
typedef struct Qcow2UnknownHeaderExtension {
|
||||
uint32_t magic;
|
||||
uint32_t len;
|
||||
QLIST_ENTRY(Qcow2UnknownHeaderExtension) next;
|
||||
uint8_t data[];
|
||||
} Qcow2UnknownHeaderExtension;
|
||||
|
||||
enum {
|
||||
QCOW2_FEAT_TYPE_INCOMPATIBLE = 0,
|
||||
QCOW2_FEAT_TYPE_COMPATIBLE = 1,
|
||||
QCOW2_FEAT_TYPE_AUTOCLEAR = 2,
|
||||
};
|
||||
|
||||
typedef struct Qcow2Feature {
|
||||
uint8_t type;
|
||||
uint8_t bit;
|
||||
char name[46];
|
||||
} QEMU_PACKED Qcow2Feature;
|
||||
|
||||
typedef struct BDRVQcowState {
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
@@ -156,15 +127,6 @@ typedef struct BDRVQcowState {
|
||||
QCowSnapshot *snapshots;
|
||||
|
||||
int flags;
|
||||
int qcow_version;
|
||||
|
||||
uint64_t incompatible_features;
|
||||
uint64_t compatible_features;
|
||||
uint64_t autoclear_features;
|
||||
|
||||
size_t unknown_header_fields_size;
|
||||
void* unknown_header_fields;
|
||||
QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext;
|
||||
} BDRVQcowState;
|
||||
|
||||
/* XXX: use std qcow open function ? */
|
||||
@@ -185,7 +147,6 @@ typedef struct QCowL2Meta
|
||||
{
|
||||
uint64_t offset;
|
||||
uint64_t cluster_offset;
|
||||
uint64_t alloc_offset;
|
||||
int n_start;
|
||||
int nb_available;
|
||||
int nb_clusters;
|
||||
@@ -194,19 +155,6 @@ typedef struct QCowL2Meta
|
||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||
} QCowL2Meta;
|
||||
|
||||
enum {
|
||||
QCOW2_CLUSTER_UNALLOCATED,
|
||||
QCOW2_CLUSTER_NORMAL,
|
||||
QCOW2_CLUSTER_COMPRESSED,
|
||||
QCOW2_CLUSTER_ZERO
|
||||
};
|
||||
|
||||
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
|
||||
|
||||
#define REFT_OFFSET_MASK 0xffffffffffffff00ULL
|
||||
|
||||
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
|
||||
{
|
||||
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
|
||||
@@ -224,34 +172,18 @@ static inline int64_t align_offset(int64_t offset, int n)
|
||||
return offset;
|
||||
}
|
||||
|
||||
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||
{
|
||||
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
||||
return QCOW2_CLUSTER_COMPRESSED;
|
||||
} else if (l2_entry & QCOW_OFLAG_ZERO) {
|
||||
return QCOW2_CLUSTER_ZERO;
|
||||
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
|
||||
return QCOW2_CLUSTER_UNALLOCATED;
|
||||
} else {
|
||||
return QCOW2_CLUSTER_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME Need qcow2_ prefix to global functions
|
||||
|
||||
/* qcow2.c functions */
|
||||
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
int qcow2_update_header(BlockDriverState *bs);
|
||||
|
||||
/* qcow2-refcount.c functions */
|
||||
int qcow2_refcount_init(BlockDriverState *bs);
|
||||
void qcow2_refcount_close(BlockDriverState *bs);
|
||||
|
||||
int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
|
||||
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_clusters);
|
||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
|
||||
void qcow2_free_clusters(BlockDriverState *bs,
|
||||
int64_t offset, int64_t size);
|
||||
@@ -283,7 +215,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors);
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
|
||||
|
||||
/* qcow2-snapshot.c functions */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
||||
|
||||
@@ -68,7 +68,6 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
|
||||
{
|
||||
BDRVQEDState *s = check->s;
|
||||
unsigned int i, num_invalid = 0;
|
||||
uint64_t last_offset = 0;
|
||||
|
||||
for (i = 0; i < s->table_nelems; i++) {
|
||||
uint64_t offset = table->offsets[i];
|
||||
@@ -77,11 +76,6 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
|
||||
qed_offset_is_zero_cluster(offset)) {
|
||||
continue;
|
||||
}
|
||||
check->result->bfi.allocated_clusters++;
|
||||
if (last_offset && (last_offset + s->header.cluster_size != offset)) {
|
||||
check->result->bfi.fragmented_clusters++;
|
||||
}
|
||||
last_offset = offset;
|
||||
|
||||
/* Detect invalid cluster offset */
|
||||
if (!qed_check_cluster_offset(s, offset)) {
|
||||
@@ -206,9 +200,6 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
check.used_clusters = g_malloc0(((check.nclusters + 31) / 32) *
|
||||
sizeof(check.used_clusters[0]));
|
||||
|
||||
check.result->bfi.total_clusters =
|
||||
(s->header.image_size + s->header.cluster_size - 1) /
|
||||
s->header.cluster_size;
|
||||
ret = qed_check_l1_table(&check, s->l1_table);
|
||||
if (ret == 0) {
|
||||
/* Only check for leaks if entire image was scanned successfully */
|
||||
|
||||
+4
-18
@@ -161,25 +161,11 @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Evict an unused cache entry so we have space. If all entries are in use
|
||||
* we can grow the cache temporarily and we try to shrink back down later.
|
||||
*/
|
||||
if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) {
|
||||
CachedL2Table *next;
|
||||
QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next) {
|
||||
if (entry->ref > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&l2_cache->entries, entry, node);
|
||||
l2_cache->n_entries--;
|
||||
qed_unref_l2_cache_entry(entry);
|
||||
|
||||
/* Stop evicting when we've shrunk back to max size */
|
||||
if (l2_cache->n_entries < MAX_L2_CACHE_SIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
entry = QTAILQ_FIRST(&l2_cache->entries);
|
||||
QTAILQ_REMOVE(&l2_cache->entries, entry, node);
|
||||
l2_cache->n_entries--;
|
||||
qed_unref_l2_cache_entry(entry);
|
||||
}
|
||||
|
||||
l2_cache->n_entries++;
|
||||
|
||||
+16
-8
@@ -29,7 +29,7 @@ static void qed_read_table_cb(void *opaque, int ret)
|
||||
{
|
||||
QEDReadTableCB *read_table_cb = opaque;
|
||||
QEDTable *table = read_table_cb->table;
|
||||
int noffsets = read_table_cb->qiov.size / sizeof(uint64_t);
|
||||
int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t);
|
||||
int i;
|
||||
|
||||
/* Handle I/O error */
|
||||
@@ -54,6 +54,7 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb),
|
||||
cb, opaque);
|
||||
QEMUIOVector *qiov = &read_table_cb->qiov;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
|
||||
trace_qed_read_table(s, offset, table);
|
||||
|
||||
@@ -63,9 +64,12 @@ 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, offset / BDRV_SECTOR_SIZE, qiov,
|
||||
qiov->size / BDRV_SECTOR_SIZE,
|
||||
qed_read_table_cb, read_table_cb);
|
||||
aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov,
|
||||
read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE,
|
||||
qed_read_table_cb, read_table_cb);
|
||||
if (!aiocb) {
|
||||
qed_read_table_cb(read_table_cb, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -123,6 +127,7 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QEDWriteTableCB *write_table_cb;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
|
||||
unsigned int start, end, i;
|
||||
size_t len_bytes;
|
||||
@@ -153,10 +158,13 @@ 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, offset / BDRV_SECTOR_SIZE,
|
||||
&write_table_cb->qiov,
|
||||
write_table_cb->qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_write_table_cb, write_table_cb);
|
||||
aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&write_table_cb->qiov,
|
||||
write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE,
|
||||
qed_write_table_cb, write_table_cb);
|
||||
if (!aiocb) {
|
||||
qed_write_table_cb(write_table_cb, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+79
-155
@@ -123,6 +123,7 @@ static void qed_write_header_read_cb(void *opaque, int ret)
|
||||
{
|
||||
QEDWriteHeaderCB *write_header_cb = opaque;
|
||||
BDRVQEDState *s = write_header_cb->s;
|
||||
BlockDriverAIOCB *acb;
|
||||
|
||||
if (ret) {
|
||||
qed_write_header_cb(write_header_cb, ret);
|
||||
@@ -132,9 +133,12 @@ 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, 0, &write_header_cb->qiov,
|
||||
write_header_cb->nsectors, qed_write_header_cb,
|
||||
write_header_cb);
|
||||
acb = bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov,
|
||||
write_header_cb->nsectors, qed_write_header_cb,
|
||||
write_header_cb);
|
||||
if (!acb) {
|
||||
qed_write_header_cb(write_header_cb, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +156,7 @@ static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
|
||||
* them, and write back.
|
||||
*/
|
||||
|
||||
BlockDriverAIOCB *acb;
|
||||
int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) /
|
||||
BDRV_SECTOR_SIZE;
|
||||
size_t len = nsectors * BDRV_SECTOR_SIZE;
|
||||
@@ -165,8 +170,11 @@ static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc 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, 0, &write_header_cb->qiov, nsectors,
|
||||
qed_write_header_read_cb, write_header_cb);
|
||||
acb = bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors,
|
||||
qed_write_header_read_cb, write_header_cb);
|
||||
if (!acb) {
|
||||
qed_write_header_cb(write_header_cb, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size)
|
||||
@@ -367,12 +375,6 @@ static void qed_cancel_need_check_timer(BDRVQEDState *s)
|
||||
qemu_del_timer(s->need_check_timer);
|
||||
}
|
||||
|
||||
static void bdrv_qed_rebind(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
s->bs = bs;
|
||||
}
|
||||
|
||||
static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
@@ -456,7 +458,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
* feature is no longer valid.
|
||||
*/
|
||||
if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 &&
|
||||
!bdrv_is_read_only(bs->file) && !(flags & BDRV_O_INCOMING)) {
|
||||
!bdrv_is_read_only(bs->file)) {
|
||||
s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK;
|
||||
|
||||
ret = qed_write_header_sync(s);
|
||||
@@ -483,8 +485,7 @@ static int bdrv_qed_open(BlockDriverState *bs, 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) &&
|
||||
!(flags & BDRV_O_INCOMING)) {
|
||||
if (!bdrv_is_read_only(bs->file)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
ret = qed_check(s, &result, true);
|
||||
@@ -504,6 +505,12 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
s->need_check_timer = qemu_new_timer_ns(vm_clock,
|
||||
qed_need_check_timer_cb, s);
|
||||
|
||||
error_set(&s->migration_blocker,
|
||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
"qed", bs->device_name, "live migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
qed_free_l2_cache(&s->l2_cache);
|
||||
@@ -516,6 +523,9 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
migrate_del_blocker(s->migration_blocker);
|
||||
error_free(s->migration_blocker);
|
||||
|
||||
qed_cancel_need_check_timer(s);
|
||||
qemu_free_timer(s->need_check_timer);
|
||||
|
||||
@@ -651,7 +661,6 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Coroutine *co;
|
||||
int is_allocated;
|
||||
int *pnum;
|
||||
} QEDIsAllocatedCB;
|
||||
@@ -661,14 +670,10 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l
|
||||
QEDIsAllocatedCB *cb = opaque;
|
||||
*cb->pnum = len / BDRV_SECTOR_SIZE;
|
||||
cb->is_allocated = (ret == QED_CLUSTER_FOUND || ret == QED_CLUSTER_ZERO);
|
||||
if (cb->co) {
|
||||
qemu_coroutine_enter(cb->co, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
|
||||
@@ -681,10 +686,8 @@ static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs,
|
||||
|
||||
qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb);
|
||||
|
||||
/* Now sleep if the callback wasn't invoked immediately */
|
||||
while (cb.is_allocated == -1) {
|
||||
cb.co = qemu_coroutine_self();
|
||||
qemu_coroutine_yield();
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
qed_unref_l2_cache_entry(request.l2_table);
|
||||
@@ -718,6 +721,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
QEMUIOVector *qiov,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BlockDriverAIOCB *aiocb;
|
||||
uint64_t backing_length = 0;
|
||||
size_t size;
|
||||
|
||||
@@ -749,8 +753,11 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
size = MIN((uint64_t)backing_length - pos, qiov->size);
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING);
|
||||
bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
|
||||
qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
|
||||
aiocb = bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
|
||||
qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
|
||||
if (!aiocb) {
|
||||
cb(opaque, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -772,6 +779,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
|
||||
{
|
||||
CopyFromBackingFileCB *copy_cb = opaque;
|
||||
BDRVQEDState *s = copy_cb->s;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
|
||||
if (ret) {
|
||||
qed_copy_from_backing_file_cb(copy_cb, ret);
|
||||
@@ -779,9 +787,13 @@ 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, copy_cb->offset / BDRV_SECTOR_SIZE,
|
||||
©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_copy_from_backing_file_cb, copy_cb);
|
||||
aiocb = 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);
|
||||
if (!aiocb) {
|
||||
qed_copy_from_backing_file_cb(copy_cb, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -873,12 +885,6 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
|
||||
qemu_iovec_destroy(&acb->cur_qiov);
|
||||
qed_unref_l2_cache_entry(acb->request.l2_table);
|
||||
|
||||
/* Free the buffer we may have allocated for zero writes */
|
||||
if (acb->flags & QED_AIOCB_ZERO) {
|
||||
qemu_vfree(acb->qiov->iov[0].iov_base);
|
||||
acb->qiov->iov[0].iov_base = NULL;
|
||||
}
|
||||
|
||||
/* Arrange for a bh to invoke the completion function */
|
||||
acb->bh_ret = ret;
|
||||
acb->bh = qemu_bh_new(qed_aio_complete_bh, acb);
|
||||
@@ -945,8 +951,9 @@ static void qed_aio_write_l1_update(void *opaque, int ret)
|
||||
/**
|
||||
* Update L2 table with new cluster offsets and write them out
|
||||
*/
|
||||
static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
|
||||
static void qed_aio_write_l2_update(void *opaque, int ret)
|
||||
{
|
||||
QEDAIOCB *acb = opaque;
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1;
|
||||
int index;
|
||||
@@ -962,7 +969,7 @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
|
||||
|
||||
index = qed_l2_index(s, acb->cur_pos);
|
||||
qed_update_l2_table(s, acb->request.l2_table->table, index, acb->cur_nclusters,
|
||||
offset);
|
||||
acb->cur_cluster);
|
||||
|
||||
if (need_alloc) {
|
||||
/* Write out the whole new L2 table */
|
||||
@@ -979,12 +986,6 @@ err:
|
||||
qed_aio_complete(acb, ret);
|
||||
}
|
||||
|
||||
static void qed_aio_write_l2_update_cb(void *opaque, int ret)
|
||||
{
|
||||
QEDAIOCB *acb = opaque;
|
||||
qed_aio_write_l2_update(acb, ret, acb->cur_cluster);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush new data clusters before updating the L2 table
|
||||
*
|
||||
@@ -999,7 +1000,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, qed_aio_write_l2_update_cb, opaque)) {
|
||||
if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update, opaque)) {
|
||||
qed_aio_complete(acb, -EIO);
|
||||
}
|
||||
}
|
||||
@@ -1014,6 +1015,7 @@ static void qed_aio_write_main(void *opaque, int ret)
|
||||
uint64_t offset = acb->cur_cluster +
|
||||
qed_offset_into_cluster(s, acb->cur_pos);
|
||||
BlockDriverCompletionFunc *next_fn;
|
||||
BlockDriverAIOCB *file_acb;
|
||||
|
||||
trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size);
|
||||
|
||||
@@ -1028,14 +1030,18 @@ static void qed_aio_write_main(void *opaque, int ret)
|
||||
if (s->bs->backing_hd) {
|
||||
next_fn = qed_aio_write_flush_before_l2_update;
|
||||
} else {
|
||||
next_fn = qed_aio_write_l2_update_cb;
|
||||
next_fn = qed_aio_write_l2_update;
|
||||
}
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
|
||||
bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
|
||||
next_fn, acb);
|
||||
file_acb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&acb->cur_qiov,
|
||||
acb->cur_qiov.size / BDRV_SECTOR_SIZE,
|
||||
next_fn, acb);
|
||||
if (!file_acb) {
|
||||
qed_aio_complete(acb, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1090,18 +1096,6 @@ static bool qed_should_set_need_check(BDRVQEDState *s)
|
||||
return !(s->header.features & QED_F_NEED_CHECK);
|
||||
}
|
||||
|
||||
static void qed_aio_write_zero_cluster(void *opaque, int ret)
|
||||
{
|
||||
QEDAIOCB *acb = opaque;
|
||||
|
||||
if (ret) {
|
||||
qed_aio_complete(acb, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
qed_aio_write_l2_update(acb, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write new data cluster
|
||||
*
|
||||
@@ -1113,7 +1107,6 @@ static void qed_aio_write_zero_cluster(void *opaque, int ret)
|
||||
static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
|
||||
{
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
BlockDriverCompletionFunc *cb;
|
||||
|
||||
/* Cancel timer when the first allocating request comes in */
|
||||
if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
|
||||
@@ -1131,26 +1124,14 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
|
||||
|
||||
acb->cur_nclusters = qed_bytes_to_clusters(s,
|
||||
qed_offset_into_cluster(s, acb->cur_pos) + len);
|
||||
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
|
||||
qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
|
||||
if (acb->flags & QED_AIOCB_ZERO) {
|
||||
/* Skip ahead if the clusters are already zero */
|
||||
if (acb->find_cluster_ret == QED_CLUSTER_ZERO) {
|
||||
qed_aio_next_io(acb, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
cb = qed_aio_write_zero_cluster;
|
||||
} else {
|
||||
cb = qed_aio_write_prefill;
|
||||
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
|
||||
}
|
||||
|
||||
if (qed_should_set_need_check(s)) {
|
||||
s->header.features |= QED_F_NEED_CHECK;
|
||||
qed_write_header(s, cb, acb);
|
||||
qed_write_header(s, qed_aio_write_prefill, acb);
|
||||
} else {
|
||||
cb(acb, 0);
|
||||
qed_aio_write_prefill(acb, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1165,16 +1146,6 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
|
||||
*/
|
||||
static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
|
||||
{
|
||||
/* Allocate buffer for zero writes */
|
||||
if (acb->flags & QED_AIOCB_ZERO) {
|
||||
struct iovec *iov = acb->qiov->iov;
|
||||
|
||||
if (!iov->iov_base) {
|
||||
iov->iov_base = qemu_blockalign(acb->common.bs, iov->iov_len);
|
||||
memset(iov->iov_base, 0, iov->iov_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the I/O vector */
|
||||
acb->cur_cluster = offset;
|
||||
qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
@@ -1237,6 +1208,7 @@ static void qed_aio_read_data(void *opaque, int ret,
|
||||
QEDAIOCB *acb = opaque;
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BlockDriverAIOCB *file_acb;
|
||||
|
||||
/* Adjust offset into cluster */
|
||||
offset += qed_offset_into_cluster(s, acb->cur_pos);
|
||||
@@ -1261,9 +1233,14 @@ static void qed_aio_read_data(void *opaque, int ret,
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_aio_next_io, acb);
|
||||
file_acb = bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
&acb->cur_qiov,
|
||||
acb->cur_qiov.size / BDRV_SECTOR_SIZE,
|
||||
qed_aio_next_io, acb);
|
||||
if (!file_acb) {
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
@@ -1277,8 +1254,8 @@ static void qed_aio_next_io(void *opaque, int ret)
|
||||
{
|
||||
QEDAIOCB *acb = opaque;
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ?
|
||||
qed_aio_write_data : qed_aio_read_data;
|
||||
QEDFindClusterFunc *io_fn =
|
||||
acb->is_write ? qed_aio_write_data : qed_aio_read_data;
|
||||
|
||||
trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size);
|
||||
|
||||
@@ -1308,14 +1285,14 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, int flags)
|
||||
void *opaque, bool is_write)
|
||||
{
|
||||
QEDAIOCB *acb = qemu_aio_get(&qed_aio_pool, bs, cb, opaque);
|
||||
|
||||
trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors,
|
||||
opaque, flags);
|
||||
opaque, is_write);
|
||||
|
||||
acb->flags = flags;
|
||||
acb->is_write = is_write;
|
||||
acb->finished = NULL;
|
||||
acb->qiov = qiov;
|
||||
acb->qiov_offset = 0;
|
||||
@@ -1335,7 +1312,7 @@ static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, false);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
|
||||
@@ -1344,55 +1321,14 @@ static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb,
|
||||
opaque, QED_AIOCB_WRITE);
|
||||
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, true);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Coroutine *co;
|
||||
int ret;
|
||||
bool done;
|
||||
} QEDWriteZeroesCB;
|
||||
|
||||
static void coroutine_fn qed_co_write_zeroes_cb(void *opaque, int ret)
|
||||
static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
QEDWriteZeroesCB *cb = opaque;
|
||||
|
||||
cb->done = true;
|
||||
cb->ret = ret;
|
||||
if (cb->co) {
|
||||
qemu_coroutine_enter(cb->co, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
BlockDriverAIOCB *blockacb;
|
||||
QEDWriteZeroesCB cb = { .done = false };
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov;
|
||||
|
||||
/* Zero writes start without an I/O buffer. If a buffer becomes necessary
|
||||
* then it will be allocated during request processing.
|
||||
*/
|
||||
iov.iov_base = NULL,
|
||||
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
|
||||
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
blockacb = qed_aio_setup(bs, sector_num, &qiov, nb_sectors,
|
||||
qed_co_write_zeroes_cb, &cb,
|
||||
QED_AIOCB_WRITE | QED_AIOCB_ZERO);
|
||||
if (!blockacb) {
|
||||
return -EIO;
|
||||
}
|
||||
if (!cb.done) {
|
||||
cb.co = qemu_coroutine_self();
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
assert(cb.done);
|
||||
return cb.ret;
|
||||
return bdrv_aio_flush(bs->file, cb, opaque);
|
||||
}
|
||||
|
||||
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
|
||||
@@ -1432,7 +1368,6 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
|
||||
memset(bdi, 0, sizeof(*bdi));
|
||||
bdi->cluster_size = s->header.cluster_size;
|
||||
bdi->is_dirty = s->header.features & QED_F_NEED_CHECK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1508,15 +1443,6 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
bdrv_qed_close(bs);
|
||||
memset(s, 0, sizeof(BDRVQEDState));
|
||||
bdrv_qed_open(bs, bs->open_flags);
|
||||
}
|
||||
|
||||
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
@@ -1556,20 +1482,18 @@ static BlockDriver bdrv_qed = {
|
||||
.create_options = qed_create_options,
|
||||
|
||||
.bdrv_probe = bdrv_qed_probe,
|
||||
.bdrv_rebind = bdrv_qed_rebind,
|
||||
.bdrv_open = bdrv_qed_open,
|
||||
.bdrv_close = bdrv_qed_close,
|
||||
.bdrv_create = bdrv_qed_create,
|
||||
.bdrv_co_is_allocated = bdrv_qed_co_is_allocated,
|
||||
.bdrv_is_allocated = bdrv_qed_is_allocated,
|
||||
.bdrv_make_empty = bdrv_qed_make_empty,
|
||||
.bdrv_aio_readv = bdrv_qed_aio_readv,
|
||||
.bdrv_aio_writev = bdrv_qed_aio_writev,
|
||||
.bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes,
|
||||
.bdrv_aio_flush = bdrv_qed_aio_flush,
|
||||
.bdrv_truncate = bdrv_qed_truncate,
|
||||
.bdrv_getlength = bdrv_qed_getlength,
|
||||
.bdrv_get_info = bdrv_qed_get_info,
|
||||
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
|
||||
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
|
||||
.bdrv_check = bdrv_qed_check,
|
||||
};
|
||||
|
||||
|
||||
+3
-6
@@ -123,17 +123,12 @@ typedef struct QEDRequest {
|
||||
CachedL2Table *l2_table;
|
||||
} QEDRequest;
|
||||
|
||||
enum {
|
||||
QED_AIOCB_WRITE = 0x0001, /* read or write? */
|
||||
QED_AIOCB_ZERO = 0x0002, /* zero write, used with QED_AIOCB_WRITE */
|
||||
};
|
||||
|
||||
typedef struct QEDAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int bh_ret; /* final return status for completion bh */
|
||||
QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */
|
||||
int flags; /* QED_AIOCB_* bits ORed together */
|
||||
bool is_write; /* false - read, true - write */
|
||||
bool *finished; /* signal for cancel completion */
|
||||
uint64_t end_pos; /* request end on block device, in bytes */
|
||||
|
||||
@@ -169,6 +164,8 @@ typedef struct {
|
||||
|
||||
/* Periodic flush and clear need check flag */
|
||||
QEMUTimer *need_check_timer;
|
||||
|
||||
Error *migration_blocker;
|
||||
} BDRVQEDState;
|
||||
|
||||
enum {
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
#ifndef QEMU_RAW_POSIX_AIO_H
|
||||
#define QEMU_RAW_POSIX_AIO_H
|
||||
|
||||
+9
-9
@@ -29,7 +29,7 @@
|
||||
#include "module.h"
|
||||
#include "block/raw-posix-aio.h"
|
||||
|
||||
#if defined(__APPLE__) && (__MACH__)
|
||||
#ifdef CONFIG_COCOA
|
||||
#include <paths.h>
|
||||
#include <sys/param.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
@@ -505,7 +505,7 @@ again:
|
||||
}
|
||||
if (size == 0)
|
||||
#endif
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#ifdef CONFIG_COCOA
|
||||
size = LONG_LONG_MAX;
|
||||
#else
|
||||
size = lseek(fd, 0LL, SEEK_END);
|
||||
@@ -650,7 +650,7 @@ static BlockDriver bdrv_file = {
|
||||
/***********************************************/
|
||||
/* host device */
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#ifdef CONFIG_COCOA
|
||||
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
|
||||
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
|
||||
|
||||
@@ -728,7 +728,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#ifdef CONFIG_COCOA
|
||||
if (strstart(filename, "/dev/cdrom", NULL)) {
|
||||
kern_return_t kernResult;
|
||||
io_iterator_t mediaIterator;
|
||||
@@ -994,7 +994,7 @@ static int floppy_media_changed(BlockDriverState *bs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void floppy_eject(BlockDriverState *bs, bool eject_flag)
|
||||
static void floppy_eject(BlockDriverState *bs, int eject_flag)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int fd;
|
||||
@@ -1084,7 +1084,7 @@ static int cdrom_is_inserted(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
|
||||
static void cdrom_eject(BlockDriverState *bs, int eject_flag)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
@@ -1153,7 +1153,7 @@ static int cdrom_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* make sure the door isn't locked at this time */
|
||||
/* make sure the door isnt locked at this time */
|
||||
ioctl(s->fd, CDIOCALLOW);
|
||||
return 0;
|
||||
}
|
||||
@@ -1184,7 +1184,7 @@ static int cdrom_reopen(BlockDriverState *bs)
|
||||
}
|
||||
s->fd = fd;
|
||||
|
||||
/* make sure the door isn't locked at this time */
|
||||
/* make sure the door isnt locked at this time */
|
||||
ioctl(s->fd, CDIOCALLOW);
|
||||
return 0;
|
||||
}
|
||||
@@ -1194,7 +1194,7 @@ static int cdrom_is_inserted(BlockDriverState *bs)
|
||||
return raw_getlength(bs) > 0;
|
||||
}
|
||||
|
||||
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
|
||||
static void cdrom_eject(BlockDriverState *bs, int eject_flag)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
|
||||
+7
-1
@@ -25,6 +25,11 @@ static void raw_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
static int64_t raw_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file);
|
||||
@@ -56,7 +61,7 @@ static int raw_media_changed(BlockDriverState *bs)
|
||||
return bdrv_media_changed(bs->file);
|
||||
}
|
||||
|
||||
static void raw_eject(BlockDriverState *bs, bool eject_flag)
|
||||
static void raw_eject(BlockDriverState *bs, int eject_flag)
|
||||
{
|
||||
bdrv_eject(bs->file, eject_flag);
|
||||
}
|
||||
@@ -108,6 +113,7 @@ static BlockDriver bdrv_raw = {
|
||||
|
||||
.bdrv_co_readv = raw_co_readv,
|
||||
.bdrv_co_writev = raw_co_writev,
|
||||
.bdrv_co_flush_to_disk = raw_co_flush,
|
||||
.bdrv_co_discard = raw_co_discard,
|
||||
|
||||
.bdrv_probe = raw_probe,
|
||||
|
||||
+22
-99
@@ -7,8 +7,6 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
@@ -44,13 +42,6 @@
|
||||
* leading "\".
|
||||
*/
|
||||
|
||||
/* rbd_aio_discard added in 0.1.2 */
|
||||
#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
|
||||
#define LIBRBD_SUPPORTS_DISCARD
|
||||
#else
|
||||
#undef LIBRBD_SUPPORTS_DISCARD
|
||||
#endif
|
||||
|
||||
#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER)
|
||||
|
||||
#define RBD_MAX_CONF_NAME_SIZE 128
|
||||
@@ -60,19 +51,13 @@
|
||||
#define RBD_MAX_SNAP_NAME_SIZE 128
|
||||
#define RBD_MAX_SNAPS 100
|
||||
|
||||
typedef enum {
|
||||
RBD_AIO_READ,
|
||||
RBD_AIO_WRITE,
|
||||
RBD_AIO_DISCARD
|
||||
} RBDAIOCmd;
|
||||
|
||||
typedef struct RBDAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int ret;
|
||||
QEMUIOVector *qiov;
|
||||
char *bounce;
|
||||
RBDAIOCmd cmd;
|
||||
int write;
|
||||
int64_t sector_num;
|
||||
int error;
|
||||
struct BDRVRBDState *s;
|
||||
@@ -384,8 +369,7 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
|
||||
r = rcb->ret;
|
||||
|
||||
if (acb->cmd == RBD_AIO_WRITE ||
|
||||
acb->cmd == RBD_AIO_DISCARD) {
|
||||
if (acb->write) {
|
||||
if (r < 0) {
|
||||
acb->ret = r;
|
||||
acb->error = 1;
|
||||
@@ -518,7 +502,7 @@ static int qemu_rbd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
|
||||
fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
|
||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader,
|
||||
NULL, qemu_rbd_aio_flush_cb, s);
|
||||
NULL, qemu_rbd_aio_flush_cb, NULL, s);
|
||||
|
||||
|
||||
return 0;
|
||||
@@ -539,7 +523,8 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
||||
|
||||
close(s->fds[0]);
|
||||
close(s->fds[1]);
|
||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
rbd_close(s->image);
|
||||
rados_ioctx_destroy(s->io_ctx);
|
||||
@@ -619,7 +604,7 @@ static void rbd_aio_bh_cb(void *opaque)
|
||||
{
|
||||
RBDAIOCB *acb = opaque;
|
||||
|
||||
if (acb->cmd == RBD_AIO_READ) {
|
||||
if (!acb->write) {
|
||||
qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
|
||||
}
|
||||
qemu_vfree(acb->bounce);
|
||||
@@ -630,25 +615,12 @@ static void rbd_aio_bh_cb(void *opaque)
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
||||
uint64_t off,
|
||||
uint64_t len,
|
||||
rbd_completion_t comp)
|
||||
{
|
||||
#ifdef LIBRBD_SUPPORTS_DISCARD
|
||||
return rbd_aio_discard(image, off, len, comp);
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque,
|
||||
RBDAIOCmd cmd)
|
||||
static BlockDriverAIOCB *rbd_aio_rw_vector(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, int write)
|
||||
{
|
||||
RBDAIOCB *acb;
|
||||
RADOSCB *rcb;
|
||||
@@ -660,20 +632,19 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
|
||||
acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque);
|
||||
acb->cmd = cmd;
|
||||
acb->qiov = qiov;
|
||||
if (cmd == RBD_AIO_DISCARD) {
|
||||
acb->bounce = NULL;
|
||||
} else {
|
||||
acb->bounce = qemu_blockalign(bs, qiov->size);
|
||||
if (!acb) {
|
||||
return NULL;
|
||||
}
|
||||
acb->write = write;
|
||||
acb->qiov = qiov;
|
||||
acb->bounce = qemu_blockalign(bs, qiov->size);
|
||||
acb->ret = 0;
|
||||
acb->error = 0;
|
||||
acb->s = s;
|
||||
acb->cancelled = 0;
|
||||
acb->bh = NULL;
|
||||
|
||||
if (cmd == RBD_AIO_WRITE) {
|
||||
if (write) {
|
||||
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
|
||||
}
|
||||
|
||||
@@ -695,18 +666,10 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case RBD_AIO_WRITE:
|
||||
if (write) {
|
||||
r = rbd_aio_write(s->image, off, size, buf, c);
|
||||
break;
|
||||
case RBD_AIO_READ:
|
||||
} else {
|
||||
r = rbd_aio_read(s->image, off, size, buf, c);
|
||||
break;
|
||||
case RBD_AIO_DISCARD:
|
||||
r = rbd_aio_discard_wrapper(s->image, off, size, c);
|
||||
break;
|
||||
default:
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
@@ -729,8 +692,7 @@ static BlockDriverAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
|
||||
RBD_AIO_READ);
|
||||
return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
|
||||
@@ -740,8 +702,7 @@ static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
|
||||
RBD_AIO_WRITE);
|
||||
return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
||||
}
|
||||
|
||||
static int qemu_rbd_co_flush(BlockDriverState *bs)
|
||||
@@ -829,26 +790,6 @@ static int qemu_rbd_snap_create(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qemu_rbd_snap_remove(BlockDriverState *bs,
|
||||
const char *snapshot_name)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r;
|
||||
|
||||
r = rbd_snap_remove(s->image, snapshot_name);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int qemu_rbd_snap_rollback(BlockDriverState *bs,
|
||||
const char *snapshot_name)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r;
|
||||
|
||||
r = rbd_snap_rollback(s->image, snapshot_name);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int qemu_rbd_snap_list(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_tab)
|
||||
{
|
||||
@@ -891,18 +832,6 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
|
||||
return snap_count;
|
||||
}
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_DISCARD
|
||||
static BlockDriverAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque,
|
||||
RBD_AIO_DISCARD);
|
||||
}
|
||||
#endif
|
||||
|
||||
static QEMUOptionParameter qemu_rbd_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
@@ -933,14 +862,8 @@ static BlockDriver bdrv_rbd = {
|
||||
.bdrv_aio_writev = qemu_rbd_aio_writev,
|
||||
.bdrv_co_flush_to_disk = qemu_rbd_co_flush,
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_DISCARD
|
||||
.bdrv_aio_discard = qemu_rbd_aio_discard,
|
||||
#endif
|
||||
|
||||
.bdrv_snapshot_create = qemu_rbd_snap_create,
|
||||
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
|
||||
.bdrv_snapshot_list = qemu_rbd_snap_list,
|
||||
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
|
||||
};
|
||||
|
||||
static void bdrv_rbd_init(void)
|
||||
|
||||
+298
-247
File diff suppressed because it is too large.
Load diff
-280
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Image streaming
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "trace.h"
|
||||
#include "block_int.h"
|
||||
|
||||
enum {
|
||||
/*
|
||||
* Size of data buffer for populating the image file. This should be large
|
||||
* enough to process multiple clusters in a single call, so that populating
|
||||
* contiguous regions of the image is efficient.
|
||||
*/
|
||||
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct {
|
||||
int64_t next_slice_time;
|
||||
uint64_t slice_quota;
|
||||
uint64_t dispatched;
|
||||
} RateLimit;
|
||||
|
||||
static int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
|
||||
{
|
||||
int64_t now = qemu_get_clock_ns(rt_clock);
|
||||
|
||||
if (limit->next_slice_time < now) {
|
||||
limit->next_slice_time = now + SLICE_TIME;
|
||||
limit->dispatched = 0;
|
||||
}
|
||||
if (limit->dispatched == 0 || limit->dispatched + n <= limit->slice_quota) {
|
||||
limit->dispatched += n;
|
||||
return 0;
|
||||
} else {
|
||||
limit->dispatched = n;
|
||||
return limit->next_slice_time - now;
|
||||
}
|
||||
}
|
||||
|
||||
static void ratelimit_set_speed(RateLimit *limit, uint64_t speed)
|
||||
{
|
||||
limit->slice_quota = speed / (1000000000ULL / SLICE_TIME);
|
||||
}
|
||||
|
||||
typedef struct StreamBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *base;
|
||||
char backing_file_id[1024];
|
||||
} StreamBlockJob;
|
||||
|
||||
static int coroutine_fn stream_populate(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
void *buf)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
|
||||
};
|
||||
QEMUIOVector qiov;
|
||||
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
/* Copy-on-read the unallocated clusters */
|
||||
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;
|
||||
|
||||
while (intermediate) {
|
||||
BlockDriverState *unused;
|
||||
|
||||
/* reached base */
|
||||
if (intermediate == base) {
|
||||
break;
|
||||
}
|
||||
|
||||
unused = intermediate;
|
||||
intermediate = intermediate->backing_hd;
|
||||
unused->backing_hd = NULL;
|
||||
bdrv_delete(unused);
|
||||
}
|
||||
top->backing_hd = base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an image chain: [BASE] -> [INTER1] -> [INTER2] -> [TOP]
|
||||
*
|
||||
* Return true if the given sector is allocated in top.
|
||||
* Return false if the given sector is allocated in intermediate images.
|
||||
* Return true otherwise.
|
||||
*
|
||||
* 'pnum' is set to the number of sectors (including and immediately following
|
||||
* the specified sector) that are known to be in the same
|
||||
* allocated/unallocated state.
|
||||
*
|
||||
*/
|
||||
static int coroutine_fn is_allocated_base(BlockDriverState *top,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BlockDriverState *intermediate;
|
||||
int ret, n;
|
||||
|
||||
ret = bdrv_co_is_allocated(top, sector_num, nb_sectors, &n);
|
||||
if (ret) {
|
||||
*pnum = n;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the unallocated chunk [sector_num, n] also
|
||||
* unallocated between base and top?
|
||||
*/
|
||||
intermediate = top->backing_hd;
|
||||
|
||||
while (intermediate != base) {
|
||||
int pnum_inter;
|
||||
|
||||
ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors,
|
||||
&pnum_inter);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
} else if (ret) {
|
||||
*pnum = pnum_inter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* [sector_num, nb_sectors] is unallocated on top but intermediate
|
||||
* might have
|
||||
*
|
||||
* [sector_num+x, nr_sectors] allocated.
|
||||
*/
|
||||
if (n > pnum_inter) {
|
||||
n = pnum_inter;
|
||||
}
|
||||
|
||||
intermediate = intermediate->backing_hd;
|
||||
}
|
||||
|
||||
*pnum = n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void coroutine_fn stream_run(void *opaque)
|
||||
{
|
||||
StreamBlockJob *s = opaque;
|
||||
BlockDriverState *bs = s->common.bs;
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t sector_num, end;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
void *buf;
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
block_job_complete(&s->common, s->common.len);
|
||||
return;
|
||||
}
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
|
||||
|
||||
/* Turn on copy-on-read for the whole block device so that guest read
|
||||
* requests help us make progress. Only do this when copying the entire
|
||||
* backing chain since the copy-on-read operation does not take base into
|
||||
* account.
|
||||
*/
|
||||
if (!base) {
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
uint64_t delay_ns = 0;
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = is_allocated_base(bs, base, sector_num,
|
||||
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
|
||||
trace_stream_one_iteration(s, sector_num, n, ret);
|
||||
if (ret == 0) {
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
if (delay_ns > 0) {
|
||||
goto wait;
|
||||
}
|
||||
}
|
||||
ret = stream_populate(bs, sector_num, n, buf);
|
||||
}
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
/* Publish progress */
|
||||
s->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
if (!base) {
|
||||
bdrv_disable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
base_id = s->backing_file_id;
|
||||
if (base->drv) {
|
||||
base_fmt = base->drv->format_name;
|
||||
}
|
||||
}
|
||||
ret = bdrv_change_backing_file(bs, base_id, base_fmt);
|
||||
close_unused_images(bs, base, base_id);
|
||||
}
|
||||
|
||||
qemu_vfree(buf);
|
||||
block_job_complete(&s->common, ret);
|
||||
}
|
||||
|
||||
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
static BlockJobType stream_job_type = {
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = "stream",
|
||||
.set_speed = stream_set_speed,
|
||||
};
|
||||
|
||||
void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
const char *base_id, int64_t speed,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s;
|
||||
|
||||
s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->base = base;
|
||||
if (base_id) {
|
||||
pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
|
||||
}
|
||||
|
||||
s->common.co = qemu_coroutine_create(stream_run);
|
||||
trace_stream_start(bs, base, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
}
|
||||
+370
-131
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Block driver for the Virtual Disk Image (VDI) format
|
||||
*
|
||||
* Copyright (c) 2009, 2012 Stefan Weil
|
||||
* Copyright (c) 2009 Stefan Weil
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -143,6 +143,29 @@ void uuid_unparse(const uuid_t uu, char *out)
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
BlockDriverAIOCB common;
|
||||
int64_t sector_num;
|
||||
QEMUIOVector *qiov;
|
||||
uint8_t *buf;
|
||||
/* Total number of sectors. */
|
||||
int nb_sectors;
|
||||
/* Number of sectors for current AIO. */
|
||||
int n_sectors;
|
||||
/* New allocated block map entry. */
|
||||
uint32_t bmap_first;
|
||||
uint32_t bmap_last;
|
||||
/* Buffer for new allocated block. */
|
||||
void *block_buffer;
|
||||
void *orig_buf;
|
||||
bool is_write;
|
||||
int header_modified;
|
||||
BlockDriverAIOCB *hd_aiocb;
|
||||
struct iovec hd_iov;
|
||||
QEMUIOVector hd_qiov;
|
||||
QEMUBH *bh;
|
||||
} VdiAIOCB;
|
||||
|
||||
typedef struct {
|
||||
char text[0x40];
|
||||
uint32_t signature;
|
||||
@@ -449,8 +472,8 @@ static int vdi_open(BlockDriverState *bs, int flags)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
/* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
|
||||
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
|
||||
@@ -466,150 +489,358 @@ static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
|
||||
return VDI_IS_ALLOCATED(bmap_entry);
|
||||
}
|
||||
|
||||
static int vdi_co_read(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors)
|
||||
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
uint32_t bmap_entry;
|
||||
uint32_t block_index;
|
||||
uint32_t sector_in_block;
|
||||
uint32_t n_sectors;
|
||||
int ret = 0;
|
||||
|
||||
/* TODO: This code is untested. How can I get it executed? */
|
||||
VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common);
|
||||
logout("\n");
|
||||
|
||||
while (ret >= 0 && nb_sectors > 0) {
|
||||
block_index = sector_num / s->block_sectors;
|
||||
sector_in_block = sector_num % s->block_sectors;
|
||||
n_sectors = s->block_sectors - sector_in_block;
|
||||
if (n_sectors > nb_sectors) {
|
||||
n_sectors = nb_sectors;
|
||||
}
|
||||
|
||||
logout("will read %u sectors starting at sector %" PRIu64 "\n",
|
||||
n_sectors, sector_num);
|
||||
|
||||
/* prepare next AIO request */
|
||||
bmap_entry = le32_to_cpu(s->bmap[block_index]);
|
||||
if (!VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
/* Block not allocated, return zeros, no need to wait. */
|
||||
memset(buf, 0, n_sectors * SECTOR_SIZE);
|
||||
ret = 0;
|
||||
} else {
|
||||
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors +
|
||||
sector_in_block;
|
||||
ret = bdrv_read(bs->file, offset, buf, n_sectors);
|
||||
}
|
||||
logout("%u sectors read\n", n_sectors);
|
||||
|
||||
nb_sectors -= n_sectors;
|
||||
sector_num += n_sectors;
|
||||
buf += n_sectors * SECTOR_SIZE;
|
||||
if (acb->hd_aiocb) {
|
||||
bdrv_aio_cancel(acb->hd_aiocb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static int vdi_co_write(BlockDriverState *bs,
|
||||
int64_t sector_num, const uint8_t *buf, int nb_sectors)
|
||||
static AIOPool vdi_aio_pool = {
|
||||
.aiocb_size = sizeof(VdiAIOCB),
|
||||
.cancel = vdi_aio_cancel,
|
||||
};
|
||||
|
||||
static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int is_write)
|
||||
{
|
||||
VdiAIOCB *acb;
|
||||
|
||||
logout("%p, %" PRId64 ", %p, %d, %p, %p, %d\n",
|
||||
bs, sector_num, qiov, nb_sectors, cb, opaque, is_write);
|
||||
|
||||
acb = qemu_aio_get(&vdi_aio_pool, bs, cb, opaque);
|
||||
if (acb) {
|
||||
acb->hd_aiocb = NULL;
|
||||
acb->sector_num = sector_num;
|
||||
acb->qiov = qiov;
|
||||
acb->is_write = is_write;
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
acb->buf = qemu_blockalign(bs, qiov->size);
|
||||
acb->orig_buf = acb->buf;
|
||||
if (is_write) {
|
||||
qemu_iovec_to_buffer(qiov, acb->buf);
|
||||
}
|
||||
} else {
|
||||
acb->buf = (uint8_t *)qiov->iov->iov_base;
|
||||
}
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->n_sectors = 0;
|
||||
acb->bmap_first = VDI_UNALLOCATED;
|
||||
acb->bmap_last = VDI_UNALLOCATED;
|
||||
acb->block_buffer = NULL;
|
||||
acb->header_modified = 0;
|
||||
}
|
||||
return acb;
|
||||
}
|
||||
|
||||
static int vdi_schedule_bh(QEMUBHFunc *cb, VdiAIOCB *acb)
|
||||
{
|
||||
logout("\n");
|
||||
|
||||
if (acb->bh) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
acb->bh = qemu_bh_new(cb, acb);
|
||||
if (!acb->bh) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qemu_bh_schedule(acb->bh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vdi_aio_read_cb(void *opaque, int ret);
|
||||
static void vdi_aio_write_cb(void *opaque, int ret);
|
||||
|
||||
static void vdi_aio_rw_bh(void *opaque)
|
||||
{
|
||||
VdiAIOCB *acb = opaque;
|
||||
logout("\n");
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
|
||||
if (acb->is_write) {
|
||||
vdi_aio_write_cb(opaque, 0);
|
||||
} else {
|
||||
vdi_aio_read_cb(opaque, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void vdi_aio_read_cb(void *opaque, int ret)
|
||||
{
|
||||
VdiAIOCB *acb = opaque;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
uint32_t bmap_entry;
|
||||
uint32_t block_index;
|
||||
uint32_t sector_in_block;
|
||||
uint32_t n_sectors;
|
||||
uint32_t bmap_first = VDI_UNALLOCATED;
|
||||
uint32_t bmap_last = VDI_UNALLOCATED;
|
||||
uint8_t *block = NULL;
|
||||
int ret = 0;
|
||||
|
||||
logout("%u sectors read\n", acb->n_sectors);
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
acb->nb_sectors -= acb->n_sectors;
|
||||
|
||||
if (acb->nb_sectors == 0) {
|
||||
/* request completed */
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
acb->sector_num += acb->n_sectors;
|
||||
acb->buf += acb->n_sectors * SECTOR_SIZE;
|
||||
|
||||
block_index = acb->sector_num / s->block_sectors;
|
||||
sector_in_block = acb->sector_num % s->block_sectors;
|
||||
n_sectors = s->block_sectors - sector_in_block;
|
||||
if (n_sectors > acb->nb_sectors) {
|
||||
n_sectors = acb->nb_sectors;
|
||||
}
|
||||
|
||||
logout("will read %u sectors starting at sector %" PRIu64 "\n",
|
||||
n_sectors, acb->sector_num);
|
||||
|
||||
/* prepare next AIO request */
|
||||
acb->n_sectors = n_sectors;
|
||||
bmap_entry = le32_to_cpu(s->bmap[block_index]);
|
||||
if (!VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
/* Block not allocated, return zeros, no need to wait. */
|
||||
memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
|
||||
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors +
|
||||
sector_in_block;
|
||||
acb->hd_iov.iov_base = (void *)acb->buf;
|
||||
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
|
||||
acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov,
|
||||
n_sectors, vdi_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
return;
|
||||
done:
|
||||
if (acb->qiov->niov > 1) {
|
||||
qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
|
||||
qemu_vfree(acb->orig_buf);
|
||||
}
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
VdiAIOCB *acb;
|
||||
int ret;
|
||||
|
||||
logout("\n");
|
||||
|
||||
while (ret >= 0 && nb_sectors > 0) {
|
||||
block_index = sector_num / s->block_sectors;
|
||||
sector_in_block = sector_num % s->block_sectors;
|
||||
n_sectors = s->block_sectors - sector_in_block;
|
||||
if (n_sectors > nb_sectors) {
|
||||
n_sectors = nb_sectors;
|
||||
}
|
||||
|
||||
logout("will write %u sectors starting at sector %" PRIu64 "\n",
|
||||
n_sectors, sector_num);
|
||||
|
||||
/* prepare next AIO request */
|
||||
bmap_entry = le32_to_cpu(s->bmap[block_index]);
|
||||
if (!VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
/* Allocate new block and write to it. */
|
||||
uint64_t offset;
|
||||
bmap_entry = s->header.blocks_allocated;
|
||||
s->bmap[block_index] = cpu_to_le32(bmap_entry);
|
||||
s->header.blocks_allocated++;
|
||||
offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors;
|
||||
if (block == NULL) {
|
||||
block = g_malloc(s->block_size);
|
||||
bmap_first = block_index;
|
||||
}
|
||||
bmap_last = block_index;
|
||||
/* Copy data to be written to new block and zero unused parts. */
|
||||
memset(block, 0, sector_in_block * SECTOR_SIZE);
|
||||
memcpy(block + sector_in_block * SECTOR_SIZE,
|
||||
buf, n_sectors * SECTOR_SIZE);
|
||||
memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0,
|
||||
(s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE);
|
||||
ret = bdrv_write(bs->file, offset, block, s->block_sectors);
|
||||
} else {
|
||||
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors +
|
||||
sector_in_block;
|
||||
ret = bdrv_write(bs->file, offset, buf, n_sectors);
|
||||
}
|
||||
|
||||
nb_sectors -= n_sectors;
|
||||
sector_num += n_sectors;
|
||||
buf += n_sectors * SECTOR_SIZE;
|
||||
|
||||
logout("%u sectors written\n", n_sectors);
|
||||
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
if (!acb) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
logout("finished data write\n");
|
||||
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (block) {
|
||||
/* One or more new blocks were allocated. */
|
||||
VdiHeader *header = (VdiHeader *) block;
|
||||
uint8_t *base;
|
||||
uint64_t offset;
|
||||
|
||||
logout("now writing modified header\n");
|
||||
assert(VDI_IS_ALLOCATED(bmap_first));
|
||||
*header = s->header;
|
||||
vdi_header_to_le(header);
|
||||
ret = bdrv_write(bs->file, 0, block, 1);
|
||||
g_free(block);
|
||||
block = NULL;
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (acb->qiov->niov > 1) {
|
||||
qemu_vfree(acb->orig_buf);
|
||||
}
|
||||
|
||||
logout("now writing modified block map entry %u...%u\n",
|
||||
bmap_first, bmap_last);
|
||||
/* Write modified sectors from block map. */
|
||||
bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
|
||||
bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
|
||||
n_sectors = bmap_last - bmap_first + 1;
|
||||
offset = s->bmap_sector + bmap_first;
|
||||
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, offset, base, n_sectors);
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void vdi_aio_write_cb(void *opaque, int ret)
|
||||
{
|
||||
VdiAIOCB *acb = opaque;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
uint32_t bmap_entry;
|
||||
uint32_t block_index;
|
||||
uint32_t sector_in_block;
|
||||
uint32_t n_sectors;
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
acb->nb_sectors -= acb->n_sectors;
|
||||
acb->sector_num += acb->n_sectors;
|
||||
acb->buf += acb->n_sectors * SECTOR_SIZE;
|
||||
|
||||
if (acb->nb_sectors == 0) {
|
||||
logout("finished data write\n");
|
||||
acb->n_sectors = 0;
|
||||
if (acb->header_modified) {
|
||||
VdiHeader *header = acb->block_buffer;
|
||||
logout("now writing modified header\n");
|
||||
assert(VDI_IS_ALLOCATED(acb->bmap_first));
|
||||
*header = s->header;
|
||||
vdi_header_to_le(header);
|
||||
acb->header_modified = 0;
|
||||
acb->hd_iov.iov_base = acb->block_buffer;
|
||||
acb->hd_iov.iov_len = SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
|
||||
acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1,
|
||||
vdi_aio_write_cb, acb);
|
||||
if (acb->hd_aiocb == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
return;
|
||||
} else if (VDI_IS_ALLOCATED(acb->bmap_first)) {
|
||||
/* One or more new blocks were allocated. */
|
||||
uint64_t offset;
|
||||
uint32_t bmap_first;
|
||||
uint32_t bmap_last;
|
||||
g_free(acb->block_buffer);
|
||||
acb->block_buffer = NULL;
|
||||
bmap_first = acb->bmap_first;
|
||||
bmap_last = acb->bmap_last;
|
||||
logout("now writing modified block map entry %u...%u\n",
|
||||
bmap_first, bmap_last);
|
||||
/* Write modified sectors from block map. */
|
||||
bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
|
||||
bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
|
||||
n_sectors = bmap_last - bmap_first + 1;
|
||||
offset = s->bmap_sector + bmap_first;
|
||||
acb->bmap_first = VDI_UNALLOCATED;
|
||||
acb->hd_iov.iov_base = (void *)((uint8_t *)&s->bmap[0] +
|
||||
bmap_first * SECTOR_SIZE);
|
||||
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
|
||||
logout("will write %u block map sectors starting from entry %u\n",
|
||||
n_sectors, bmap_first);
|
||||
acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
|
||||
n_sectors, vdi_aio_write_cb, acb);
|
||||
if (acb->hd_aiocb == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
return;
|
||||
}
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
logout("%u sectors written\n", acb->n_sectors);
|
||||
|
||||
block_index = acb->sector_num / s->block_sectors;
|
||||
sector_in_block = acb->sector_num % s->block_sectors;
|
||||
n_sectors = s->block_sectors - sector_in_block;
|
||||
if (n_sectors > acb->nb_sectors) {
|
||||
n_sectors = acb->nb_sectors;
|
||||
}
|
||||
|
||||
logout("will write %u sectors starting at sector %" PRIu64 "\n",
|
||||
n_sectors, acb->sector_num);
|
||||
|
||||
/* prepare next AIO request */
|
||||
acb->n_sectors = n_sectors;
|
||||
bmap_entry = le32_to_cpu(s->bmap[block_index]);
|
||||
if (!VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
/* Allocate new block and write to it. */
|
||||
uint64_t offset;
|
||||
uint8_t *block;
|
||||
bmap_entry = s->header.blocks_allocated;
|
||||
s->bmap[block_index] = cpu_to_le32(bmap_entry);
|
||||
s->header.blocks_allocated++;
|
||||
offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors;
|
||||
block = acb->block_buffer;
|
||||
if (block == NULL) {
|
||||
block = g_malloc0(s->block_size);
|
||||
acb->block_buffer = block;
|
||||
acb->bmap_first = block_index;
|
||||
assert(!acb->header_modified);
|
||||
acb->header_modified = 1;
|
||||
}
|
||||
acb->bmap_last = block_index;
|
||||
memcpy(block + sector_in_block * SECTOR_SIZE,
|
||||
acb->buf, n_sectors * SECTOR_SIZE);
|
||||
acb->hd_iov.iov_base = (void *)block;
|
||||
acb->hd_iov.iov_len = s->block_size;
|
||||
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
|
||||
acb->hd_aiocb = bdrv_aio_writev(bs->file, offset,
|
||||
&acb->hd_qiov, s->block_sectors,
|
||||
vdi_aio_write_cb, acb);
|
||||
if (acb->hd_aiocb == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
|
||||
(uint64_t)bmap_entry * s->block_sectors +
|
||||
sector_in_block;
|
||||
acb->hd_iov.iov_base = (void *)acb->buf;
|
||||
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
|
||||
acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
|
||||
n_sectors, vdi_aio_write_cb, acb);
|
||||
if (acb->hd_aiocb == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
done:
|
||||
if (acb->qiov->niov > 1) {
|
||||
qemu_vfree(acb->orig_buf);
|
||||
}
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
VdiAIOCB *acb;
|
||||
int ret;
|
||||
|
||||
logout("\n");
|
||||
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
||||
if (!acb) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
|
||||
if (ret < 0) {
|
||||
if (acb->qiov->niov > 1) {
|
||||
qemu_vfree(acb->orig_buf);
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int vdi_create(const char *filename, QEMUOptionParameter *options)
|
||||
@@ -725,6 +956,13 @@ static void vdi_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static coroutine_fn int vdi_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
logout("\n");
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
|
||||
static QEMUOptionParameter vdi_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
@@ -757,12 +995,13 @@ static BlockDriver bdrv_vdi = {
|
||||
.bdrv_open = vdi_open,
|
||||
.bdrv_close = vdi_close,
|
||||
.bdrv_create = vdi_create,
|
||||
.bdrv_co_is_allocated = vdi_co_is_allocated,
|
||||
.bdrv_co_flush_to_disk = vdi_co_flush,
|
||||
.bdrv_is_allocated = vdi_is_allocated,
|
||||
.bdrv_make_empty = vdi_make_empty,
|
||||
|
||||
.bdrv_read = vdi_co_read,
|
||||
.bdrv_aio_readv = vdi_aio_readv,
|
||||
#if defined(CONFIG_VDI_WRITE)
|
||||
.bdrv_write = vdi_co_write,
|
||||
.bdrv_aio_writev = vdi_aio_writev,
|
||||
#endif
|
||||
|
||||
.bdrv_get_info = vdi_get_info,
|
||||
|
||||
+7
-65
@@ -35,7 +35,6 @@
|
||||
#define VMDK4_FLAG_RGD (1 << 1)
|
||||
#define VMDK4_FLAG_COMPRESS (1 << 16)
|
||||
#define VMDK4_FLAG_MARKER (1 << 17)
|
||||
#define VMDK4_GD_AT_END 0xffffffffffffffffULL
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
@@ -58,8 +57,8 @@ typedef struct {
|
||||
int64_t desc_offset;
|
||||
int64_t desc_size;
|
||||
int32_t num_gtes_per_gte;
|
||||
int64_t rgd_offset;
|
||||
int64_t gd_offset;
|
||||
int64_t rgd_offset;
|
||||
int64_t grain_offset;
|
||||
char filler[1];
|
||||
char check_bytes[4];
|
||||
@@ -116,13 +115,6 @@ typedef struct VmdkGrainMarker {
|
||||
uint8_t data[0];
|
||||
} VmdkGrainMarker;
|
||||
|
||||
enum {
|
||||
MARKER_END_OF_STREAM = 0,
|
||||
MARKER_GRAIN_TABLE = 1,
|
||||
MARKER_GRAIN_DIRECTORY = 2,
|
||||
MARKER_FOOTER = 3,
|
||||
};
|
||||
|
||||
static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
uint32_t magic;
|
||||
@@ -459,57 +451,9 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
if (header.capacity == 0 && header.desc_offset) {
|
||||
return vmdk_open_desc_file(bs, flags, header.desc_offset << 9);
|
||||
}
|
||||
|
||||
if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) {
|
||||
/*
|
||||
* The footer takes precedence over the header, so read it in. The
|
||||
* footer starts at offset -1024 from the end: One sector for the
|
||||
* footer, and another one for the end-of-stream marker.
|
||||
*/
|
||||
struct {
|
||||
struct {
|
||||
uint64_t val;
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
uint8_t pad[512 - 16];
|
||||
} QEMU_PACKED footer_marker;
|
||||
|
||||
uint32_t magic;
|
||||
VMDK4Header header;
|
||||
uint8_t pad[512 - 4 - sizeof(VMDK4Header)];
|
||||
|
||||
struct {
|
||||
uint64_t val;
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
uint8_t pad[512 - 16];
|
||||
} QEMU_PACKED eos_marker;
|
||||
} QEMU_PACKED footer;
|
||||
|
||||
ret = bdrv_pread(file,
|
||||
bs->file->total_sectors * 512 - 1536,
|
||||
&footer, sizeof(footer));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Some sanity checks for the footer */
|
||||
if (be32_to_cpu(footer.magic) != VMDK4_MAGIC ||
|
||||
le32_to_cpu(footer.footer_marker.size) != 0 ||
|
||||
le32_to_cpu(footer.footer_marker.type) != MARKER_FOOTER ||
|
||||
le64_to_cpu(footer.eos_marker.val) != 0 ||
|
||||
le32_to_cpu(footer.eos_marker.size) != 0 ||
|
||||
le32_to_cpu(footer.eos_marker.type) != MARKER_END_OF_STREAM)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
header = footer.header;
|
||||
}
|
||||
|
||||
l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte)
|
||||
* le64_to_cpu(header.granularity);
|
||||
if (l1_entry_sectors == 0) {
|
||||
if (l1_entry_sectors <= 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
|
||||
@@ -917,8 +861,8 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int64_t index_in_cluster, n, ret;
|
||||
@@ -929,10 +873,8 @@ static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs,
|
||||
if (!extent) {
|
||||
return 0;
|
||||
}
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num * 512, 0, &offset);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
/* get_cluster_offset returning 0 means success */
|
||||
ret = !ret;
|
||||
|
||||
@@ -1581,10 +1523,10 @@ static void vmdk_close(BlockDriverState *bs)
|
||||
|
||||
static coroutine_fn int vmdk_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
int i, ret, err;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i, err;
|
||||
int ret = 0;
|
||||
|
||||
ret = bdrv_co_flush(bs->file);
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
err = bdrv_co_flush(s->extents[i].file);
|
||||
if (err < 0) {
|
||||
@@ -1654,7 +1596,7 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_close = vmdk_close,
|
||||
.bdrv_create = vmdk_create,
|
||||
.bdrv_co_flush_to_disk = vmdk_co_flush,
|
||||
.bdrv_co_is_allocated = vmdk_co_is_allocated,
|
||||
.bdrv_is_allocated = vmdk_is_allocated,
|
||||
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
||||
|
||||
.create_options = vmdk_create_options,
|
||||
|
||||
+105
-202
@@ -161,27 +161,13 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
||||
uint8_t buf[HEADER_SIZE];
|
||||
uint32_t checksum;
|
||||
int err = -1;
|
||||
int disk_type = VHD_DYNAMIC;
|
||||
|
||||
if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
footer = (struct vhd_footer*) s->footer_buf;
|
||||
if (strncmp(footer->creator, "conectix", 8)) {
|
||||
int64_t offset = bdrv_getlength(bs->file);
|
||||
if (offset < HEADER_SIZE) {
|
||||
goto fail;
|
||||
}
|
||||
/* If a fixed disk, the footer is found only at the end of the file */
|
||||
if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE)
|
||||
!= HEADER_SIZE) {
|
||||
goto fail;
|
||||
}
|
||||
if (strncmp(footer->creator, "conectix", 8)) {
|
||||
goto fail;
|
||||
}
|
||||
disk_type = VHD_FIXED;
|
||||
}
|
||||
if (strncmp(footer->creator, "conectix", 8))
|
||||
goto fail;
|
||||
|
||||
checksum = be32_to_cpu(footer->checksum);
|
||||
footer->checksum = 0;
|
||||
@@ -189,9 +175,6 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
||||
fprintf(stderr, "block-vpc: The header checksum of '%s' is "
|
||||
"incorrect.\n", bs->filename);
|
||||
|
||||
/* Write 'checksum' back to footer, or else will leave it with zero. */
|
||||
footer->checksum = be32_to_cpu(checksum);
|
||||
|
||||
// The visible size of a image in Virtual PC depends on the geometry
|
||||
// rather than on the size stored in the footer (the size in the footer
|
||||
// is too large usually)
|
||||
@@ -203,54 +186,49 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf,
|
||||
HEADER_SIZE) != HEADER_SIZE) {
|
||||
goto fail;
|
||||
if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
|
||||
!= HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
dyndisk_header = (struct vhd_dyndisk_header*) buf;
|
||||
|
||||
if (strncmp(dyndisk_header->magic, "cxsparse", 8))
|
||||
goto fail;
|
||||
|
||||
|
||||
s->block_size = be32_to_cpu(dyndisk_header->block_size);
|
||||
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
|
||||
|
||||
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
|
||||
s->pagetable = g_malloc(s->max_table_entries * 4);
|
||||
|
||||
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||
if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
|
||||
s->max_table_entries * 4) != s->max_table_entries * 4)
|
||||
goto fail;
|
||||
|
||||
s->free_data_block_offset =
|
||||
(s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
|
||||
|
||||
for (i = 0; i < s->max_table_entries; i++) {
|
||||
be32_to_cpus(&s->pagetable[i]);
|
||||
if (s->pagetable[i] != 0xFFFFFFFF) {
|
||||
int64_t next = (512 * (int64_t) s->pagetable[i]) +
|
||||
s->bitmap_size + s->block_size;
|
||||
|
||||
if (next> s->free_data_block_offset)
|
||||
s->free_data_block_offset = next;
|
||||
}
|
||||
}
|
||||
|
||||
dyndisk_header = (struct vhd_dyndisk_header *) buf;
|
||||
|
||||
if (strncmp(dyndisk_header->magic, "cxsparse", 8)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->block_size = be32_to_cpu(dyndisk_header->block_size);
|
||||
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
|
||||
|
||||
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
|
||||
s->pagetable = g_malloc(s->max_table_entries * 4);
|
||||
|
||||
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||
if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
|
||||
s->max_table_entries * 4) != s->max_table_entries * 4) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->free_data_block_offset =
|
||||
(s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
|
||||
|
||||
for (i = 0; i < s->max_table_entries; i++) {
|
||||
be32_to_cpus(&s->pagetable[i]);
|
||||
if (s->pagetable[i] != 0xFFFFFFFF) {
|
||||
int64_t next = (512 * (int64_t) s->pagetable[i]) +
|
||||
s->bitmap_size + s->block_size;
|
||||
|
||||
if (next > s->free_data_block_offset) {
|
||||
s->free_data_block_offset = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->last_bitmap_offset = (int64_t) -1;
|
||||
s->last_bitmap_offset = (int64_t) -1;
|
||||
|
||||
#ifdef CACHE
|
||||
s->pageentry_u8 = g_malloc(512);
|
||||
s->pageentry_u32 = s->pageentry_u8;
|
||||
s->pageentry_u16 = s->pageentry_u8;
|
||||
s->last_pagetable = -1;
|
||||
s->pageentry_u8 = g_malloc(512);
|
||||
s->pageentry_u32 = s->pageentry_u8;
|
||||
s->pageentry_u16 = s->pageentry_u8;
|
||||
s->last_pagetable = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
@@ -417,11 +395,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
int ret;
|
||||
int64_t offset;
|
||||
int64_t sectors, sectors_per_block;
|
||||
struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf;
|
||||
|
||||
if (cpu_to_be32(footer->type) == VHD_FIXED) {
|
||||
return bdrv_read(bs->file, sector_num, buf, nb_sectors);
|
||||
}
|
||||
while (nb_sectors > 0) {
|
||||
offset = get_sector_offset(bs, sector_num, 0);
|
||||
|
||||
@@ -466,11 +440,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
|
||||
int64_t offset;
|
||||
int64_t sectors, sectors_per_block;
|
||||
int ret;
|
||||
struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf;
|
||||
|
||||
if (cpu_to_be32(footer->type) == VHD_FIXED) {
|
||||
return bdrv_write(bs->file, sector_num, buf, nb_sectors);
|
||||
}
|
||||
while (nb_sectors > 0) {
|
||||
offset = get_sector_offset(bs, sector_num, 1);
|
||||
|
||||
@@ -510,6 +480,11 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int vpc_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_co_flush(bs->file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the number of cylinders, heads and sectors per cylinder
|
||||
* based on a given number of sectors. This is the algorithm described
|
||||
@@ -558,14 +533,70 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
|
||||
static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
struct vhd_footer* footer = (struct vhd_footer*) buf;
|
||||
struct vhd_dyndisk_header* dyndisk_header =
|
||||
(struct vhd_dyndisk_header*) buf;
|
||||
int fd, i;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
size_t block_size, num_bat_entries;
|
||||
int i;
|
||||
int64_t total_sectors = 0;
|
||||
int ret = -EIO;
|
||||
|
||||
// Read out options
|
||||
total_sectors = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n /
|
||||
BDRV_SECTOR_SIZE;
|
||||
|
||||
// Create the file
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0)
|
||||
return -EIO;
|
||||
|
||||
/* Calculate matching total_size and geometry. Increase the number of
|
||||
sectors requested until we get enough (or fail). */
|
||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||
if (calculate_geometry(total_sectors + i,
|
||||
&cyls, &heads, &secs_per_cyl)) {
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||
|
||||
// Prepare the Hard Disk Footer
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
// TODO Check if "qemu" creator_app is ok for VPC
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
memcpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = be32_to_cpu(0x02);
|
||||
footer->version = be32_to_cpu(0x00010000);
|
||||
footer->data_offset = be64_to_cpu(HEADER_SIZE);
|
||||
footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
|
||||
|
||||
// Version of Virtual PC 2007
|
||||
footer->major = be16_to_cpu(0x0005);
|
||||
footer->minor =be16_to_cpu(0x0003);
|
||||
|
||||
footer->orig_size = be64_to_cpu(total_sectors * 512);
|
||||
footer->size = be64_to_cpu(total_sectors * 512);
|
||||
|
||||
footer->cyls = be16_to_cpu(cyls);
|
||||
footer->heads = heads;
|
||||
footer->secs_per_cyl = secs_per_cyl;
|
||||
|
||||
footer->type = be32_to_cpu(VHD_DYNAMIC);
|
||||
|
||||
// TODO uuid is missing
|
||||
|
||||
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
// Write the footer (twice: at the beginning and at the end)
|
||||
block_size = 0x200000;
|
||||
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
|
||||
@@ -593,6 +624,7 @@ static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prepare the Dynamic Disk Header
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
@@ -620,129 +652,6 @@ static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
|
||||
{
|
||||
int ret = -EIO;
|
||||
|
||||
/* Add footer to total size */
|
||||
total_size += 512;
|
||||
if (ftruncate(fd, total_size) != 0) {
|
||||
ret = -errno;
|
||||
goto fail;
|
||||
}
|
||||
if (lseek(fd, -512, SEEK_END) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
struct vhd_footer *footer = (struct vhd_footer *) buf;
|
||||
QEMUOptionParameter *disk_type_param;
|
||||
int fd, i;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
int64_t total_sectors;
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
|
||||
/* Read out options */
|
||||
total_size = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n;
|
||||
|
||||
disk_type_param = get_option_parameter(options, BLOCK_OPT_SUBFMT);
|
||||
if (disk_type_param && disk_type_param->value.s) {
|
||||
if (!strcmp(disk_type_param->value.s, "dynamic")) {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
} else if (!strcmp(disk_type_param->value.s, "fixed")) {
|
||||
disk_type = VHD_FIXED;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
}
|
||||
|
||||
/* Create the file */
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate matching total_size and geometry. Increase the number of
|
||||
* sectors requested until we get enough (or fail). This ensures that
|
||||
* qemu-img convert doesn't truncate images, but rather rounds up.
|
||||
*/
|
||||
total_sectors = total_size / BDRV_SECTOR_SIZE;
|
||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||
if (calculate_geometry(total_sectors + i, &cyls, &heads,
|
||||
&secs_per_cyl))
|
||||
{
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||
|
||||
/* Prepare the Hard Disk Footer */
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
/* TODO Check if "qemu" creator_app is ok for VPC */
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
memcpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = be32_to_cpu(0x02);
|
||||
footer->version = be32_to_cpu(0x00010000);
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
footer->data_offset = be64_to_cpu(HEADER_SIZE);
|
||||
} else {
|
||||
footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
|
||||
}
|
||||
footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
|
||||
|
||||
/* Version of Virtual PC 2007 */
|
||||
footer->major = be16_to_cpu(0x0005);
|
||||
footer->minor = be16_to_cpu(0x0003);
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
footer->orig_size = be64_to_cpu(total_sectors * 512);
|
||||
footer->size = be64_to_cpu(total_sectors * 512);
|
||||
} else {
|
||||
footer->orig_size = be64_to_cpu(total_size);
|
||||
footer->size = be64_to_cpu(total_size);
|
||||
}
|
||||
footer->cyls = be16_to_cpu(cyls);
|
||||
footer->heads = heads;
|
||||
footer->secs_per_cyl = secs_per_cyl;
|
||||
|
||||
footer->type = be32_to_cpu(disk_type);
|
||||
|
||||
/* TODO uuid is missing */
|
||||
|
||||
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
ret = create_dynamic_disk(fd, buf, total_sectors);
|
||||
} else {
|
||||
ret = create_fixed_disk(fd, buf, total_size);
|
||||
}
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return ret;
|
||||
@@ -766,13 +675,6 @@ static QEMUOptionParameter vpc_create_options[] = {
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SUBFMT,
|
||||
.type = OPT_STRING,
|
||||
.help =
|
||||
"Type of virtual hard disk format. Supported formats are "
|
||||
"{dynamic (default) | fixed} "
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -787,6 +689,7 @@ static BlockDriver bdrv_vpc = {
|
||||
|
||||
.bdrv_read = vpc_co_read,
|
||||
.bdrv_write = vpc_co_write,
|
||||
.bdrv_co_flush_to_disk = vpc_co_flush,
|
||||
|
||||
.create_options = vpc_create_options,
|
||||
};
|
||||
|
||||
+3
-18
@@ -982,12 +982,6 @@ static BDRVVVFATState *vvv = NULL;
|
||||
static int enable_write_target(BDRVVVFATState *s);
|
||||
static int is_consistent(BDRVVVFATState *s);
|
||||
|
||||
static void vvfat_rebind(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
s->bs = bs;
|
||||
}
|
||||
|
||||
static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
@@ -2224,7 +2218,6 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
}
|
||||
if (offset > 0) {
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return -3;
|
||||
}
|
||||
@@ -2245,13 +2238,11 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
(uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
|
||||
|
||||
if (ret < 0) {
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (write(fd, cluster, rest_size) < 0) {
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return -2;
|
||||
}
|
||||
@@ -2767,7 +2758,7 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs,
|
||||
static int vvfat_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int* n)
|
||||
{
|
||||
BDRVVVFATState* s = bs->opaque;
|
||||
@@ -2808,12 +2799,7 @@ static int enable_write_target(BDRVVVFATState *s)
|
||||
array_init(&(s->commits), sizeof(commit_t));
|
||||
|
||||
s->qcow_filename = g_malloc(1024);
|
||||
ret = get_tmp_filename(s->qcow_filename, 1024);
|
||||
if (ret < 0) {
|
||||
g_free(s->qcow_filename);
|
||||
s->qcow_filename = NULL;
|
||||
return ret;
|
||||
}
|
||||
get_tmp_filename(s->qcow_filename, 1024);
|
||||
|
||||
bdrv_qcow = bdrv_find_format("qcow");
|
||||
options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
|
||||
@@ -2866,11 +2852,10 @@ static BlockDriver bdrv_vvfat = {
|
||||
.format_name = "vvfat",
|
||||
.instance_size = sizeof(BDRVVVFATState),
|
||||
.bdrv_file_open = vvfat_open,
|
||||
.bdrv_rebind = vvfat_rebind,
|
||||
.bdrv_read = vvfat_co_read,
|
||||
.bdrv_write = vvfat_co_write,
|
||||
.bdrv_close = vvfat_close,
|
||||
.bdrv_co_is_allocated = vvfat_co_is_allocated,
|
||||
.bdrv_is_allocated = vvfat_is_allocated,
|
||||
.protocol_name = "fat",
|
||||
};
|
||||
|
||||
|
||||
+29
-234
@@ -34,13 +34,6 @@
|
||||
#define BLOCK_FLAG_ENCRYPT 1
|
||||
#define BLOCK_FLAG_COMPAT6 4
|
||||
|
||||
#define BLOCK_IO_LIMIT_READ 0
|
||||
#define BLOCK_IO_LIMIT_WRITE 1
|
||||
#define BLOCK_IO_LIMIT_TOTAL 2
|
||||
|
||||
#define BLOCK_IO_SLICE_TIME 100000000
|
||||
#define NANOSECONDS_PER_SECOND 1000000000.0
|
||||
|
||||
#define BLOCK_OPT_SIZE "size"
|
||||
#define BLOCK_OPT_ENCRYPT "encryption"
|
||||
#define BLOCK_OPT_COMPAT6 "compat6"
|
||||
@@ -50,86 +43,12 @@
|
||||
#define BLOCK_OPT_TABLE_SIZE "table_size"
|
||||
#define BLOCK_OPT_PREALLOC "preallocation"
|
||||
#define BLOCK_OPT_SUBFMT "subformat"
|
||||
#define BLOCK_OPT_COMPAT_LEVEL "compat"
|
||||
|
||||
typedef struct BdrvTrackedRequest BdrvTrackedRequest;
|
||||
|
||||
typedef struct BlockIOLimit {
|
||||
int64_t bps[3];
|
||||
int64_t iops[3];
|
||||
} BlockIOLimit;
|
||||
|
||||
typedef struct BlockIOBaseValue {
|
||||
uint64_t bytes[2];
|
||||
uint64_t ios[2];
|
||||
} BlockIOBaseValue;
|
||||
|
||||
typedef struct BlockJob BlockJob;
|
||||
|
||||
/**
|
||||
* BlockJobType:
|
||||
*
|
||||
* A class type for block job objects.
|
||||
*/
|
||||
typedef struct BlockJobType {
|
||||
/** Derived BlockJob struct size */
|
||||
size_t instance_size;
|
||||
|
||||
/** String describing the operation, part of query-block-jobs QMP API */
|
||||
const char *job_type;
|
||||
|
||||
/** Optional callback for job types that support setting a speed limit */
|
||||
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
|
||||
} BlockJobType;
|
||||
|
||||
/**
|
||||
* BlockJob:
|
||||
*
|
||||
* Long-running operation on a BlockDriverState.
|
||||
*/
|
||||
struct BlockJob {
|
||||
/** The job type, including the job vtable. */
|
||||
const BlockJobType *job_type;
|
||||
|
||||
/** The block device on which the job is operating. */
|
||||
BlockDriverState *bs;
|
||||
|
||||
/**
|
||||
* The coroutine that executes the job. If not NULL, it is
|
||||
* reentered when busy is false and the job is cancelled.
|
||||
*/
|
||||
Coroutine *co;
|
||||
|
||||
/**
|
||||
* Set to true if the job should cancel itself. The flag must
|
||||
* always be tested just before toggling the busy flag from false
|
||||
* to true. After a job has been cancelled, it should only yield
|
||||
* if #qemu_aio_wait will ("sooner or later") reenter the coroutine.
|
||||
*/
|
||||
bool cancelled;
|
||||
|
||||
/**
|
||||
* Set to false by the job while it is in a quiescent state, where
|
||||
* no I/O is pending and the job has yielded on any condition
|
||||
* that is not detected by #qemu_aio_wait, such as a timer.
|
||||
*/
|
||||
bool busy;
|
||||
|
||||
/** Offset that is published by the query-block-jobs QMP API */
|
||||
int64_t offset;
|
||||
|
||||
/** Length that is published by the query-block-jobs QMP API */
|
||||
int64_t len;
|
||||
|
||||
/** Speed that was set with @block_job_set_speed. */
|
||||
int64_t speed;
|
||||
|
||||
/** The completion function that will be called when the job completes. */
|
||||
BlockDriverCompletionFunc *cb;
|
||||
|
||||
/** The opaque value that is passed to the completion function. */
|
||||
void *opaque;
|
||||
};
|
||||
typedef struct AIOPool {
|
||||
void (*cancel)(BlockDriverAIOCB *acb);
|
||||
int aiocb_size;
|
||||
BlockDriverAIOCB *free_aiocb;
|
||||
} AIOPool;
|
||||
|
||||
struct BlockDriver {
|
||||
const char *format_name;
|
||||
@@ -143,8 +62,9 @@ struct BlockDriver {
|
||||
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
void (*bdrv_close)(BlockDriverState *bs);
|
||||
void (*bdrv_rebind)(BlockDriverState *bs);
|
||||
int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
|
||||
int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
|
||||
int (*bdrv_make_empty)(BlockDriverState *bs);
|
||||
/* aio */
|
||||
@@ -164,18 +84,8 @@ struct BlockDriver {
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
|
||||
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
|
||||
/*
|
||||
* Efficiently zero a region of the disk image. Typically an image format
|
||||
* would use a compact metadata representation to implement this. This
|
||||
* function pointer may be NULL and .bdrv_co_writev() will be called
|
||||
* instead.
|
||||
*/
|
||||
int coroutine_fn (*bdrv_co_write_zeroes)(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
int coroutine_fn (*bdrv_co_is_allocated)(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum);
|
||||
|
||||
/*
|
||||
* Invalidate any cached meta-data.
|
||||
@@ -195,6 +105,12 @@ struct BlockDriver {
|
||||
*/
|
||||
int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs);
|
||||
|
||||
int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
|
||||
int num_reqs);
|
||||
int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a,
|
||||
BlockRequest *b);
|
||||
|
||||
|
||||
const char *protocol_name;
|
||||
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
|
||||
int64_t (*bdrv_getlength)(BlockDriverState *bs);
|
||||
@@ -224,7 +140,7 @@ struct BlockDriver {
|
||||
/* removable device specific */
|
||||
int (*bdrv_is_inserted)(BlockDriverState *bs);
|
||||
int (*bdrv_media_changed)(BlockDriverState *bs);
|
||||
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
|
||||
void (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
|
||||
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
|
||||
|
||||
/* to control generic scsi devices */
|
||||
@@ -254,12 +170,6 @@ struct BlockDriver {
|
||||
QLIST_ENTRY(BlockDriver) list;
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: the function bdrv_append() copies and swaps contents of
|
||||
* BlockDriverStates, so if you add new fields to this struct, please
|
||||
* inspect bdrv_append() to determine if the new fields need to be
|
||||
* copied as well.
|
||||
*/
|
||||
struct BlockDriverState {
|
||||
int64_t total_sectors; /* if we are reading a disk image, give its
|
||||
size in sectors */
|
||||
@@ -269,8 +179,6 @@ struct BlockDriverState {
|
||||
int encrypted; /* if true, the media is encrypted */
|
||||
int valid_key; /* if true, a valid encryption key has been set */
|
||||
int sg; /* if true, the device is a /dev/sg* */
|
||||
int copy_on_read; /* if true, copy read backing sectors into image
|
||||
note this is a reference count */
|
||||
|
||||
BlockDriver *drv; /* NULL means no media */
|
||||
void *opaque;
|
||||
@@ -289,18 +197,9 @@ struct BlockDriverState {
|
||||
BlockDriverState *backing_hd;
|
||||
BlockDriverState *file;
|
||||
|
||||
/* number of in-flight copy-on-read requests */
|
||||
unsigned int copy_on_read_in_flight;
|
||||
/* async read/write emulation */
|
||||
|
||||
/* the time for latest disk I/O */
|
||||
int64_t slice_time;
|
||||
int64_t slice_start;
|
||||
int64_t slice_end;
|
||||
BlockIOLimit io_limits;
|
||||
BlockIOBaseValue io_base;
|
||||
CoQueue throttled_reqs;
|
||||
QEMUTimer *block_timer;
|
||||
bool io_limits_enabled;
|
||||
void *sync_aiocb;
|
||||
|
||||
/* I/O stats (display with "info blockstats"). */
|
||||
uint64_t nr_bytes[BDRV_MAX_IOTYPE];
|
||||
@@ -328,129 +227,25 @@ struct BlockDriverState {
|
||||
int64_t dirty_count;
|
||||
int in_use; /* users other than guest access, eg. block migration */
|
||||
QTAILQ_ENTRY(BlockDriverState) list;
|
||||
|
||||
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
|
||||
|
||||
/* long-running background operation */
|
||||
BlockJob *job;
|
||||
void *private;
|
||||
};
|
||||
|
||||
int get_tmp_filename(char *filename, int size);
|
||||
struct BlockDriverAIOCB {
|
||||
AIOPool *pool;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
BlockDriverAIOCB *next;
|
||||
};
|
||||
|
||||
void bdrv_set_io_limits(BlockDriverState *bs,
|
||||
BlockIOLimit *io_limits);
|
||||
void get_tmp_filename(char *filename, int size);
|
||||
|
||||
void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void qemu_aio_release(void *p);
|
||||
|
||||
#ifdef _WIN32
|
||||
int is_windows_drive(const char *filename);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* block_job_create:
|
||||
* @job_type: The class object for the newly-created job.
|
||||
* @bs: The block
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
* @cb: Completion function for the job.
|
||||
* @opaque: Opaque pointer value passed to @cb.
|
||||
* @errp: Error object.
|
||||
*
|
||||
* Create a new long-running block device job and return it. The job
|
||||
* will call @cb asynchronously when the job completes. Note that
|
||||
* @bs may have been closed at the time the @cb it is called. If
|
||||
* this is the case, the job may be reported as either cancelled or
|
||||
* completed.
|
||||
*
|
||||
* This function is not part of the public job interface; it should be
|
||||
* called from a wrapper that is specific to the job type.
|
||||
*/
|
||||
void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
|
||||
int64_t speed, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
/**
|
||||
* block_job_sleep_ns:
|
||||
* @job: The job that calls the function.
|
||||
* @clock: The clock to sleep on.
|
||||
* @ns: How many nanoseconds to stop for.
|
||||
*
|
||||
* Put the job to sleep (assuming that it wasn't canceled) for @ns
|
||||
* nanoseconds. Canceling the job will interrupt the wait immediately.
|
||||
*/
|
||||
void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns);
|
||||
|
||||
/**
|
||||
* block_job_complete:
|
||||
* @job: The job being completed.
|
||||
* @ret: The status code.
|
||||
*
|
||||
* Call the completion function that was registered at creation time, and
|
||||
* free @job.
|
||||
*/
|
||||
void block_job_complete(BlockJob *job, int ret);
|
||||
|
||||
/**
|
||||
* block_job_set_speed:
|
||||
* @job: The job to set the speed for.
|
||||
* @speed: The new value
|
||||
* @errp: Error object.
|
||||
*
|
||||
* Set a rate-limiting parameter for the job; the actual meaning may
|
||||
* vary depending on the job type.
|
||||
*/
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
|
||||
|
||||
/**
|
||||
* block_job_cancel:
|
||||
* @job: The job to be canceled.
|
||||
*
|
||||
* Asynchronously cancel the specified job.
|
||||
*/
|
||||
void block_job_cancel(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_is_cancelled:
|
||||
* @job: The job being queried.
|
||||
*
|
||||
* Returns whether the job is scheduled for cancellation.
|
||||
*/
|
||||
bool block_job_is_cancelled(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_cancel:
|
||||
* @job: The job to be canceled.
|
||||
*
|
||||
* Asynchronously cancel the job and wait for it to reach a quiescent
|
||||
* state. Note that the completion callback will still be called
|
||||
* asynchronously, hence it is *not* valid to call #bdrv_delete
|
||||
* immediately after #block_job_cancel_sync. Users of block jobs
|
||||
* will usually protect the BlockDriverState objects with a reference
|
||||
* count, should this be a concern.
|
||||
*
|
||||
* Returns the return value from the job if the job actually completed
|
||||
* during the call, or -ECANCELED if it was canceled.
|
||||
*/
|
||||
int block_job_cancel_sync(BlockJob *job);
|
||||
|
||||
/**
|
||||
* stream_start:
|
||||
* @bs: Block device to operate on.
|
||||
* @base: Block device that will become the new base, or %NULL to
|
||||
* flatten the whole backing file chain onto @bs.
|
||||
* @base_id: The file name that will be written to @bs as the new
|
||||
* backing file if the job completes. Ignored if @base is %NULL.
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
* @cb: Completion function for the job.
|
||||
* @opaque: Opaque pointer value passed to @cb.
|
||||
* @errp: Error object.
|
||||
*
|
||||
* Start a streaming operation on @bs. Clusters that are unallocated
|
||||
* in @bs, but allocated in any image between @base and @bs (both
|
||||
* exclusive) will be written to @bs. At the end of a successful
|
||||
* streaming job, the backing file of @bs will be changed to
|
||||
* @base_id in the written image and to @base in the live BlockDriverState.
|
||||
*/
|
||||
void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
const char *base_id, int64_t speed,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
#endif /* BLOCK_INT_H */
|
||||
+150
-570
@@ -13,12 +13,8 @@
|
||||
#include "qerror.h"
|
||||
#include "qemu-option.h"
|
||||
#include "qemu-config.h"
|
||||
#include "qemu-objects.h"
|
||||
#include "sysemu.h"
|
||||
#include "block_int.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
#include "arch_init.h"
|
||||
|
||||
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
||||
|
||||
@@ -64,9 +60,6 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
|
||||
{
|
||||
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
||||
|
||||
if (bs->job) {
|
||||
block_job_cancel(bs->job);
|
||||
}
|
||||
if (dinfo) {
|
||||
dinfo->auto_del = 1;
|
||||
}
|
||||
@@ -206,37 +199,6 @@ void drive_get_ref(DriveInfo *dinfo)
|
||||
dinfo->refcount++;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
QEMUBH *bh;
|
||||
DriveInfo *dinfo;
|
||||
} DrivePutRefBH;
|
||||
|
||||
static void drive_put_ref_bh(void *opaque)
|
||||
{
|
||||
DrivePutRefBH *s = opaque;
|
||||
|
||||
drive_put_ref(s->dinfo);
|
||||
qemu_bh_delete(s->bh);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a drive reference in a BH
|
||||
*
|
||||
* It is not possible to use drive_put_ref() from a callback function when the
|
||||
* callers still need the drive. In such cases we schedule a BH to release the
|
||||
* reference.
|
||||
*/
|
||||
static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
|
||||
{
|
||||
DrivePutRefBH *s;
|
||||
|
||||
s = g_new(DrivePutRefBH, 1);
|
||||
s->bh = qemu_bh_new(drive_put_ref_bh, s);
|
||||
s->dinfo = dinfo;
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
|
||||
static int parse_block_error_action(const char *buf, int is_read)
|
||||
{
|
||||
if (!strcmp(buf, "ignore")) {
|
||||
@@ -254,26 +216,6 @@ static int parse_block_error_action(const char *buf, int is_read)
|
||||
}
|
||||
}
|
||||
|
||||
static bool do_check_io_limits(BlockIOLimit *io_limits)
|
||||
{
|
||||
bool bps_flag;
|
||||
bool iops_flag;
|
||||
|
||||
assert(io_limits);
|
||||
|
||||
bps_flag = (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] != 0)
|
||||
&& ((io_limits->bps[BLOCK_IO_LIMIT_READ] != 0)
|
||||
|| (io_limits->bps[BLOCK_IO_LIMIT_WRITE] != 0));
|
||||
iops_flag = (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] != 0)
|
||||
&& ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
|
||||
|| (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
|
||||
if (bps_flag || iops_flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
{
|
||||
const char *buf;
|
||||
@@ -293,9 +235,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
int on_read_error, on_write_error;
|
||||
const char *devaddr;
|
||||
DriveInfo *dinfo;
|
||||
BlockIOLimit io_limits;
|
||||
int snapshot = 0;
|
||||
bool copy_on_read;
|
||||
int ret;
|
||||
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
@@ -312,7 +252,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
|
||||
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
||||
ro = qemu_opt_get_bool(opts, "readonly", 0);
|
||||
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
|
||||
|
||||
file = qemu_opt_get(opts, "file");
|
||||
serial = qemu_opt_get(opts, "serial");
|
||||
@@ -414,26 +353,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
}
|
||||
}
|
||||
|
||||
/* disk I/O throttling */
|
||||
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] =
|
||||
qemu_opt_get_number(opts, "bps", 0);
|
||||
io_limits.bps[BLOCK_IO_LIMIT_READ] =
|
||||
qemu_opt_get_number(opts, "bps_rd", 0);
|
||||
io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
|
||||
qemu_opt_get_number(opts, "bps_wr", 0);
|
||||
io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
|
||||
qemu_opt_get_number(opts, "iops", 0);
|
||||
io_limits.iops[BLOCK_IO_LIMIT_READ] =
|
||||
qemu_opt_get_number(opts, "iops_rd", 0);
|
||||
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
|
||||
qemu_opt_get_number(opts, "iops_wr", 0);
|
||||
|
||||
if (!do_check_io_limits(&io_limits)) {
|
||||
error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
|
||||
"cannot be used at the same time");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
on_write_error = BLOCK_ERR_STOP_ENOSPC;
|
||||
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
||||
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
|
||||
@@ -535,16 +454,12 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->opts = opts;
|
||||
dinfo->refcount = 1;
|
||||
if (serial) {
|
||||
pstrcpy(dinfo->serial, sizeof(dinfo->serial), serial);
|
||||
}
|
||||
if (serial)
|
||||
strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
|
||||
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
||||
|
||||
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
||||
|
||||
/* disk I/O throttling */
|
||||
bdrv_set_io_limits(dinfo->bdrv, &io_limits);
|
||||
|
||||
switch(type) {
|
||||
case IF_IDE:
|
||||
case IF_SCSI:
|
||||
@@ -570,11 +485,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
case IF_VIRTIO:
|
||||
/* add virtio block device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-pci");
|
||||
}
|
||||
qemu_opt_set(opts, "driver", "virtio-blk");
|
||||
qemu_opt_set(opts, "drive", dinfo->id);
|
||||
if (devaddr)
|
||||
qemu_opt_set(opts, "addr", devaddr);
|
||||
@@ -591,20 +502,11 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
||||
}
|
||||
|
||||
if (copy_on_read) {
|
||||
bdrv_flags |= BDRV_O_COPY_ON_READ;
|
||||
}
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INCOMING;
|
||||
}
|
||||
|
||||
if (media == MEDIA_CDROM) {
|
||||
/* CDROM is fine for any interface, don't check. */
|
||||
ro = 1;
|
||||
} else if (ro == 1) {
|
||||
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
|
||||
type != IF_NONE && type != IF_PFLASH) {
|
||||
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
|
||||
error_report("readonly not supported by this bus type");
|
||||
goto err;
|
||||
}
|
||||
@@ -635,354 +537,182 @@ void do_commit(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *device = qdict_get_str(qdict, "device");
|
||||
BlockDriverState *bs;
|
||||
int ret;
|
||||
|
||||
if (!strcmp(device, "all")) {
|
||||
ret = bdrv_commit_all();
|
||||
if (ret == -EBUSY) {
|
||||
qerror_report(QERR_DEVICE_IN_USE, device);
|
||||
return;
|
||||
}
|
||||
bdrv_commit_all();
|
||||
} else {
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
}
|
||||
ret = bdrv_commit(bs);
|
||||
if (ret == -EBUSY) {
|
||||
qerror_report(QERR_DEVICE_IN_USE, device);
|
||||
return;
|
||||
}
|
||||
bdrv_commit(bs);
|
||||
}
|
||||
}
|
||||
|
||||
static void blockdev_do_action(int kind, void *data, Error **errp)
|
||||
{
|
||||
BlockdevAction action;
|
||||
BlockdevActionList list;
|
||||
|
||||
action.kind = kind;
|
||||
action.data = data;
|
||||
list.value = &action;
|
||||
list.next = NULL;
|
||||
qmp_transaction(&list, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
|
||||
bool has_format, const char *format,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevSnapshot snapshot = {
|
||||
.device = (char *) device,
|
||||
.snapshot_file = (char *) snapshot_file,
|
||||
.has_format = has_format,
|
||||
.format = (char *) format,
|
||||
.has_mode = has_mode,
|
||||
.mode = mode,
|
||||
};
|
||||
blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot,
|
||||
errp);
|
||||
}
|
||||
|
||||
|
||||
/* New and old BlockDriverState structs for group snapshots */
|
||||
typedef struct BlkTransactionStates {
|
||||
BlockDriverState *old_bs;
|
||||
BlockDriverState *new_bs;
|
||||
QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
|
||||
} BlkTransactionStates;
|
||||
|
||||
/*
|
||||
* 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail
|
||||
* then we do not pivot any of the devices in the group, and abandon the
|
||||
* snapshots
|
||||
*/
|
||||
void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
BlockdevActionList *dev_entry = dev_list;
|
||||
BlkTransactionStates *states, *next;
|
||||
|
||||
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
|
||||
QSIMPLEQ_INIT(&snap_bdrv_states);
|
||||
|
||||
/* drain all i/o before any snapshots */
|
||||
bdrv_drain_all();
|
||||
|
||||
/* We don't do anything in this loop that commits us to the snapshot */
|
||||
while (NULL != dev_entry) {
|
||||
BlockdevAction *dev_info = NULL;
|
||||
BlockDriver *proto_drv;
|
||||
BlockDriver *drv;
|
||||
int flags;
|
||||
enum NewImageMode mode;
|
||||
const char *new_image_file;
|
||||
const char *device;
|
||||
const char *format = "qcow2";
|
||||
|
||||
dev_info = dev_entry->value;
|
||||
dev_entry = dev_entry->next;
|
||||
|
||||
states = g_malloc0(sizeof(BlkTransactionStates));
|
||||
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
|
||||
|
||||
switch (dev_info->kind) {
|
||||
case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
|
||||
device = dev_info->blockdev_snapshot_sync->device;
|
||||
if (!dev_info->blockdev_snapshot_sync->has_mode) {
|
||||
dev_info->blockdev_snapshot_sync->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
new_image_file = dev_info->blockdev_snapshot_sync->snapshot_file;
|
||||
if (dev_info->blockdev_snapshot_sync->has_format) {
|
||||
format = dev_info->blockdev_snapshot_sync->format;
|
||||
}
|
||||
mode = dev_info->blockdev_snapshot_sync->mode;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
drv = bdrv_find_format(format);
|
||||
if (!drv) {
|
||||
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
|
||||
states->old_bs = bdrv_find(device);
|
||||
if (!states->old_bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
|
||||
if (!bdrv_is_inserted(states->old_bs)) {
|
||||
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
|
||||
if (bdrv_in_use(states->old_bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, device);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(states->old_bs)) {
|
||||
if (bdrv_flush(states->old_bs)) {
|
||||
error_set(errp, QERR_IO_ERROR);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
}
|
||||
|
||||
flags = states->old_bs->open_flags;
|
||||
|
||||
proto_drv = bdrv_find_protocol(new_image_file);
|
||||
if (!proto_drv) {
|
||||
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
|
||||
/* create new image w/backing file */
|
||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||
ret = bdrv_img_create(new_image_file, format,
|
||||
states->old_bs->filename,
|
||||
states->old_bs->drv->format_name,
|
||||
NULL, -1, flags);
|
||||
if (ret) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* We will manually add the backing_hd field to the bs later */
|
||||
states->new_bs = bdrv_new("");
|
||||
ret = bdrv_open(states->new_bs, new_image_file,
|
||||
flags | BDRV_O_NO_BACKING, drv);
|
||||
if (ret != 0) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Now we are going to do the actual pivot. Everything up to this point
|
||||
* is reversible, but we are committed at this point */
|
||||
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
|
||||
/* This removes our old bs from the bdrv_states, and adds the new bs */
|
||||
bdrv_append(states->new_bs, states->old_bs);
|
||||
}
|
||||
|
||||
/* success */
|
||||
goto exit;
|
||||
|
||||
delete_and_fail:
|
||||
/*
|
||||
* failure, and it is all-or-none; abandon each new bs, and keep using
|
||||
* the original bs for all images
|
||||
*/
|
||||
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
|
||||
if (states->new_bs) {
|
||||
bdrv_delete(states->new_bs);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
|
||||
g_free(states);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void eject_device(BlockDriverState *bs, int force, Error **errp)
|
||||
{
|
||||
if (bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
||||
return;
|
||||
}
|
||||
if (!bdrv_dev_has_removable_media(bs)) {
|
||||
error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
|
||||
bdrv_dev_eject_request(bs, force);
|
||||
if (!force) {
|
||||
error_set(errp, QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_close(bs);
|
||||
}
|
||||
|
||||
void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
|
||||
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
const char *device = qdict_get_str(qdict, "device");
|
||||
const char *filename = qdict_get_try_str(qdict, "snapshot-file");
|
||||
const char *format = qdict_get_try_str(qdict, "format");
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv, *old_drv, *proto_drv;
|
||||
int ret = 0;
|
||||
int flags;
|
||||
char old_filename[1024];
|
||||
|
||||
if (!filename) {
|
||||
qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
eject_device(bs, force, errp);
|
||||
pstrcpy(old_filename, sizeof(old_filename), bs->filename);
|
||||
|
||||
old_drv = bs->drv;
|
||||
flags = bs->open_flags;
|
||||
|
||||
if (!format) {
|
||||
format = "qcow2";
|
||||
}
|
||||
|
||||
drv = bdrv_find_format(format);
|
||||
if (!drv) {
|
||||
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
proto_drv = bdrv_find_protocol(filename);
|
||||
if (!proto_drv) {
|
||||
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_img_create(filename, format, bs->filename,
|
||||
bs->drv->format_name, NULL, -1, flags);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_aio_flush();
|
||||
bdrv_flush(bs);
|
||||
|
||||
bdrv_close(bs);
|
||||
ret = bdrv_open(bs, filename, flags, drv);
|
||||
/*
|
||||
* If reopening the image file we just created fails, fall back
|
||||
* and try to re-open the original image. If that fails too, we
|
||||
* are in serious trouble.
|
||||
*/
|
||||
if (ret != 0) {
|
||||
ret = bdrv_open(bs, old_filename, flags, old_drv);
|
||||
if (ret != 0) {
|
||||
qerror_report(QERR_OPEN_FILE_FAILED, old_filename);
|
||||
} else {
|
||||
qerror_report(QERR_OPEN_FILE_FAILED, filename);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (ret) {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void qmp_block_passwd(const char *device, const char *password, Error **errp)
|
||||
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
|
||||
{
|
||||
if (!bdrv_dev_has_removable_media(bs)) {
|
||||
qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
|
||||
bdrv_dev_eject_request(bs, force);
|
||||
if (!force) {
|
||||
qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
bdrv_close(bs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
int force = qdict_get_try_bool(qdict, "force", 0);
|
||||
const char *filename = qdict_get_str(qdict, "device");
|
||||
|
||||
bs = bdrv_find(filename);
|
||||
if (!bs) {
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, filename);
|
||||
return -1;
|
||||
}
|
||||
return eject_device(mon, bs, force);
|
||||
}
|
||||
|
||||
int do_block_set_passwd(Monitor *mon, const QDict *qdict,
|
||||
QObject **ret_data)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
int err;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
bs = bdrv_find(qdict_get_str(qdict, "device"));
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bdrv_set_key(bs, password);
|
||||
err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
|
||||
if (err == -EINVAL) {
|
||||
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
|
||||
return;
|
||||
qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
|
||||
return -1;
|
||||
} else if (err < 0) {
|
||||
error_set(errp, QERR_INVALID_PASSWORD);
|
||||
return;
|
||||
qerror_report(QERR_INVALID_PASSWORD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
|
||||
int bdrv_flags, BlockDriver *drv,
|
||||
const char *password, Error **errp)
|
||||
{
|
||||
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_key_required(bs)) {
|
||||
if (password) {
|
||||
if (bdrv_set_key(bs, password) < 0) {
|
||||
error_set(errp, QERR_INVALID_PASSWORD);
|
||||
}
|
||||
} else {
|
||||
error_set(errp, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs),
|
||||
bdrv_get_encrypted_filename(bs));
|
||||
}
|
||||
} else if (password) {
|
||||
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_change_blockdev(const char *device, const char *filename,
|
||||
bool has_format, const char *format, Error **errp)
|
||||
int do_change_block(Monitor *mon, const char *device,
|
||||
const char *filename, const char *fmt)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv = NULL;
|
||||
int bdrv_flags;
|
||||
Error *err = NULL;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (format) {
|
||||
drv = bdrv_find_whitelisted_format(format);
|
||||
if (fmt) {
|
||||
drv = bdrv_find_whitelisted_format(fmt);
|
||||
if (!drv) {
|
||||
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
||||
return;
|
||||
qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
eject_device(bs, 0, &err);
|
||||
if (error_is_set(&err)) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
if (eject_device(mon, bs, 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
|
||||
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
|
||||
|
||||
qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp);
|
||||
}
|
||||
|
||||
/* throttling disk I/O limits */
|
||||
void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
int64_t bps_wr, int64_t iops, int64_t iops_rd,
|
||||
int64_t iops_wr, Error **errp)
|
||||
{
|
||||
BlockIOLimit io_limits;
|
||||
BlockDriverState *bs;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
}
|
||||
|
||||
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = bps;
|
||||
io_limits.bps[BLOCK_IO_LIMIT_READ] = bps_rd;
|
||||
io_limits.bps[BLOCK_IO_LIMIT_WRITE] = bps_wr;
|
||||
io_limits.iops[BLOCK_IO_LIMIT_TOTAL]= iops;
|
||||
io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
|
||||
io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
|
||||
|
||||
if (!do_check_io_limits(&io_limits)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
|
||||
return;
|
||||
}
|
||||
|
||||
bs->io_limits = io_limits;
|
||||
bs->slice_time = BLOCK_IO_SLICE_TIME;
|
||||
|
||||
if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) {
|
||||
bdrv_io_limits_enable(bs);
|
||||
} else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) {
|
||||
bdrv_io_limits_disable(bs);
|
||||
} else {
|
||||
if (bs->block_timer) {
|
||||
qemu_mod_timer(bs->block_timer, qemu_get_clock_ns(vm_clock));
|
||||
}
|
||||
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
|
||||
qerror_report(QERR_OPEN_FILE_FAILED, filename);
|
||||
return -1;
|
||||
}
|
||||
return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
|
||||
}
|
||||
|
||||
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
@@ -1001,7 +731,7 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
}
|
||||
|
||||
/* quiesce block driver; prevent further io */
|
||||
bdrv_drain_all();
|
||||
qemu_aio_flush();
|
||||
bdrv_flush(bs);
|
||||
bdrv_close(bs);
|
||||
|
||||
@@ -1019,182 +749,32 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qmp_block_resize(const char *device, int64_t size, Error **errp)
|
||||
/*
|
||||
* XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the
|
||||
* existing QERR_ macro mess is cleaned up. A good example for better
|
||||
* error reports can be found in the qemu-img resize code.
|
||||
*/
|
||||
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
const char *device = qdict_get_str(qdict, "device");
|
||||
int64_t size = qdict_get_int(qdict, "size");
|
||||
BlockDriverState *bs;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
|
||||
return;
|
||||
qerror_report(QERR_UNDEFINED_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (bdrv_truncate(bs, size)) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOMEDIUM:
|
||||
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
||||
break;
|
||||
case -ENOTSUP:
|
||||
error_set(errp, QERR_UNSUPPORTED);
|
||||
break;
|
||||
case -EACCES:
|
||||
error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
|
||||
break;
|
||||
case -EBUSY:
|
||||
error_set(errp, QERR_DEVICE_IN_USE, device);
|
||||
break;
|
||||
default:
|
||||
error_set(errp, QERR_UNDEFINED_ERROR);
|
||||
break;
|
||||
if (bdrv_truncate(bs, size)) {
|
||||
qerror_report(QERR_UNDEFINED_ERROR);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static QObject *qobject_from_block_job(BlockJob *job)
|
||||
{
|
||||
return qobject_from_jsonf("{ 'type': %s,"
|
||||
"'device': %s,"
|
||||
"'len': %" PRId64 ","
|
||||
"'offset': %" PRId64 ","
|
||||
"'speed': %" PRId64 " }",
|
||||
job->job_type->job_type,
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
job->speed);
|
||||
}
|
||||
|
||||
static void block_stream_cb(void *opaque, int ret)
|
||||
{
|
||||
BlockDriverState *bs = opaque;
|
||||
QObject *obj;
|
||||
|
||||
trace_block_stream_cb(bs, bs->job, ret);
|
||||
|
||||
assert(bs->job);
|
||||
obj = qobject_from_block_job(bs->job);
|
||||
if (ret < 0) {
|
||||
QDict *dict = qobject_to_qdict(obj);
|
||||
qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
|
||||
}
|
||||
|
||||
if (block_job_is_cancelled(bs->job)) {
|
||||
monitor_protocol_event(QEVENT_BLOCK_JOB_CANCELLED, obj);
|
||||
} else {
|
||||
monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj);
|
||||
}
|
||||
qobject_decref(obj);
|
||||
|
||||
drive_put_ref_bh_schedule(drive_get_by_blockdev(bs));
|
||||
}
|
||||
|
||||
void qmp_block_stream(const char *device, bool has_base,
|
||||
const char *base, bool has_speed,
|
||||
int64_t speed, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *base_bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (base) {
|
||||
base_bs = bdrv_find_backing_image(bs, base);
|
||||
if (base_bs == NULL) {
|
||||
error_set(errp, QERR_BASE_NOT_FOUND, base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stream_start(bs, base_bs, base, has_speed ? speed : 0,
|
||||
block_stream_cb, bs, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
||||
* underneath us.
|
||||
*/
|
||||
drive_get_ref(drive_get_by_blockdev(bs));
|
||||
|
||||
trace_qmp_block_stream(bs, bs->job);
|
||||
}
|
||||
|
||||
static BlockJob *find_block_job(const char *device)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs || !bs->job) {
|
||||
return NULL;
|
||||
}
|
||||
return bs->job;
|
||||
}
|
||||
|
||||
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
|
||||
{
|
||||
BlockJob *job = find_block_job(device);
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
block_job_set_speed(job, speed, errp);
|
||||
}
|
||||
|
||||
void qmp_block_job_cancel(const char *device, Error **errp)
|
||||
{
|
||||
BlockJob *job = find_block_job(device);
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_cancel(job);
|
||||
block_job_cancel(job);
|
||||
}
|
||||
|
||||
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
|
||||
{
|
||||
BlockJobInfoList **prev = opaque;
|
||||
BlockJob *job = bs->job;
|
||||
|
||||
if (job) {
|
||||
BlockJobInfoList *elem;
|
||||
BlockJobInfo *info = g_new(BlockJobInfo, 1);
|
||||
*info = (BlockJobInfo){
|
||||
.type = g_strdup(job->job_type->job_type),
|
||||
.device = g_strdup(bdrv_get_device_name(bs)),
|
||||
.len = job->len,
|
||||
.offset = job->offset,
|
||||
.speed = job->speed,
|
||||
};
|
||||
|
||||
elem = g_new0(BlockJobInfoList, 1);
|
||||
elem->value = info;
|
||||
|
||||
(*prev)->next = elem;
|
||||
*prev = elem;
|
||||
}
|
||||
}
|
||||
|
||||
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
||||
{
|
||||
/* Dummy is a fake list element for holding the head pointer */
|
||||
BlockJobInfoList dummy = {};
|
||||
BlockJobInfoList *prev = &dummy;
|
||||
bdrv_iterate(do_qmp_query_block_jobs_one, &prev);
|
||||
return dummy.next;
|
||||
return 0;
|
||||
}
|
||||
+7
-3
@@ -11,7 +11,6 @@
|
||||
#define BLOCKDEV_H
|
||||
|
||||
#include "block.h"
|
||||
#include "error.h"
|
||||
#include "qemu-queue.h"
|
||||
|
||||
void blockdev_mark_auto_del(BlockDriverState *bs);
|
||||
@@ -58,8 +57,13 @@ DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi);
|
||||
|
||||
DriveInfo *add_init_drive(const char *opts);
|
||||
|
||||
void qmp_change_blockdev(const char *device, const char *filename,
|
||||
bool has_format, const char *format, Error **errp);
|
||||
void do_commit(Monitor *mon, const QDict *qdict);
|
||||
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
int do_change_block(Monitor *mon, const char *device,
|
||||
const char *filename, const char *fmt);
|
||||
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -196,7 +196,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
|
||||
|
||||
/* Something went wrong, return the inode and free the argument pages*/
|
||||
for (i=0 ; i<MAX_ARG_PAGES ; i++) {
|
||||
g_free(bprm.page[i]);
|
||||
free(bprm.page[i]);
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
+10
-6
@@ -641,7 +641,8 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
|
||||
offset = p % TARGET_PAGE_SIZE;
|
||||
pag = (char *)page[p/TARGET_PAGE_SIZE];
|
||||
if (!pag) {
|
||||
pag = g_try_malloc0(TARGET_PAGE_SIZE);
|
||||
pag = (char *)malloc(TARGET_PAGE_SIZE);
|
||||
memset(pag, 0, TARGET_PAGE_SIZE);
|
||||
page[p/TARGET_PAGE_SIZE] = pag;
|
||||
if (!pag)
|
||||
return 0;
|
||||
@@ -695,7 +696,7 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
|
||||
info->rss++;
|
||||
/* FIXME - check return value of memcpy_to_target() for failure */
|
||||
memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
|
||||
g_free(bprm->page[i]);
|
||||
free(bprm->page[i]);
|
||||
}
|
||||
stack_base += TARGET_PAGE_SIZE;
|
||||
}
|
||||
@@ -993,12 +994,12 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
|
||||
static int symfind(const void *s0, const void *s1)
|
||||
{
|
||||
target_ulong addr = *(target_ulong *)s0;
|
||||
struct elf_sym *key = (struct elf_sym *)s0;
|
||||
struct elf_sym *sym = (struct elf_sym *)s1;
|
||||
int result = 0;
|
||||
if (addr < sym->st_value) {
|
||||
if (key->st_value < sym->st_value) {
|
||||
result = -1;
|
||||
} else if (addr >= sym->st_value + sym->st_size) {
|
||||
} else if (key->st_value > sym->st_value + sym->st_size) {
|
||||
result = 1;
|
||||
}
|
||||
return result;
|
||||
@@ -1013,9 +1014,12 @@ static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr)
|
||||
#endif
|
||||
|
||||
// binary search
|
||||
struct elf_sym key;
|
||||
struct elf_sym *sym;
|
||||
|
||||
sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), symfind);
|
||||
key.st_value = orig_addr;
|
||||
|
||||
sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind);
|
||||
if (sym != NULL) {
|
||||
return s->disas_strtab + sym->st_name;
|
||||
}
|
||||
|
||||
+7
-10
@@ -41,7 +41,6 @@ int singlestep;
|
||||
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;
|
||||
@@ -64,18 +63,18 @@ void gemu_log(const char *fmt, ...)
|
||||
}
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
int cpu_get_pic_interrupt(CPUX86State *env)
|
||||
int cpu_get_pic_interrupt(CPUState *env)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are no-ops because we are not threadsafe. */
|
||||
static inline void cpu_exec_start(CPUArchState *env)
|
||||
static inline void cpu_exec_start(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cpu_exec_end(CPUArchState *env)
|
||||
static inline void cpu_exec_end(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -110,7 +109,7 @@ void cpu_list_unlock(void)
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
|
||||
void cpu_smm_update(CPUX86State *env)
|
||||
void cpu_smm_update(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -714,7 +713,7 @@ static void usage(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
THREAD CPUArchState *thread_env;
|
||||
THREAD CPUState *thread_env;
|
||||
|
||||
/* Assumes contents are already zeroed. */
|
||||
void init_task_state(TaskState *ts)
|
||||
@@ -738,7 +737,7 @@ int main(int argc, char **argv)
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
struct image_info info1, *info = &info1;
|
||||
TaskState ts1, *ts = &ts1;
|
||||
CPUArchState *env;
|
||||
CPUState *env;
|
||||
int optind;
|
||||
const char *r;
|
||||
int gdbstub_port = 0;
|
||||
@@ -749,8 +748,6 @@ int main(int argc, char **argv)
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
|
||||
if ((envlist = envlist_create()) == NULL) {
|
||||
(void) fprintf(stderr, "Unable to allocate envlist\n");
|
||||
exit(1);
|
||||
@@ -918,7 +915,7 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
cpu_state_reset(env);
|
||||
cpu_reset(env);
|
||||
#endif
|
||||
thread_env = env;
|
||||
|
||||
|
||||
+6
-6
@@ -139,8 +139,8 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6);
|
||||
void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
|
||||
extern THREAD CPUArchState *thread_env;
|
||||
void cpu_loop(CPUArchState *env);
|
||||
extern THREAD CPUState *thread_env;
|
||||
void cpu_loop(CPUState *env);
|
||||
char *target_strerror(int err);
|
||||
int get_osversion(void);
|
||||
void fork_start(void);
|
||||
@@ -167,13 +167,13 @@ void print_openbsd_syscall_ret(int num, abi_long ret);
|
||||
extern int do_strace;
|
||||
|
||||
/* signal.c */
|
||||
void process_pending_signals(CPUArchState *cpu_env);
|
||||
void process_pending_signals(CPUState *cpu_env);
|
||||
void signal_init(void);
|
||||
//int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info);
|
||||
//int queue_signal(CPUState *env, int sig, target_siginfo_t *info);
|
||||
//void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
|
||||
//void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
|
||||
long do_sigreturn(CPUArchState *env);
|
||||
long do_rt_sigreturn(CPUArchState *env);
|
||||
long do_sigreturn(CPUState *env);
|
||||
long do_rt_sigreturn(CPUState *env);
|
||||
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
|
||||
|
||||
/* mmap.c */
|
||||
|
||||
+1
-1
@@ -33,6 +33,6 @@ void signal_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void process_pending_signals(CPUArchState *cpu_env)
|
||||
void process_pending_signals(CPUState *cpu_env)
|
||||
{
|
||||
}
|
||||
@@ -8,7 +8,7 @@ struct target_pt_regs {
|
||||
abi_ulong r12;
|
||||
abi_ulong rbp;
|
||||
abi_ulong rbx;
|
||||
/* arguments: non interrupts/non tracing syscalls only save up to here */
|
||||
/* arguments: non interrupts/non tracing syscalls only save upto here*/
|
||||
abi_ulong r11;
|
||||
abi_ulong r10;
|
||||
abi_ulong r9;
|
||||
|
||||
@@ -130,7 +130,6 @@ static void bt_host_read(void *opaque)
|
||||
pktlen = MIN(pkt[2] + 3, s->len);
|
||||
s->len -= pktlen;
|
||||
pkt += pktlen;
|
||||
break;
|
||||
|
||||
default:
|
||||
bad_pkt:
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
+1
-4
@@ -2,9 +2,6 @@
|
||||
#define QEMU_CACHE_UTILS_H
|
||||
|
||||
#if defined(_ARCH_PPC)
|
||||
|
||||
#include <stdint.h> /* uintptr_t */
|
||||
|
||||
struct qemu_cache_conf {
|
||||
unsigned long dcache_bsize;
|
||||
unsigned long icache_bsize;
|
||||
@@ -15,7 +12,7 @@ extern struct qemu_cache_conf qemu_cache_conf;
|
||||
void qemu_cache_utils_init(char **envp);
|
||||
|
||||
/* mildly adjusted code from tcg-dyngen.c */
|
||||
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned long p, start1, stop1;
|
||||
unsigned long dsize = qemu_cache_conf.dcache_bsize;
|
||||
|
||||
+134
-110
@@ -9,7 +9,7 @@
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#include <glib.h>
|
||||
#include <check.h>
|
||||
|
||||
#include "qint.h"
|
||||
#include "qdict.h"
|
||||
@@ -22,21 +22,22 @@
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
static void qdict_new_test(void)
|
||||
START_TEST(qdict_new_test)
|
||||
{
|
||||
QDict *qdict;
|
||||
|
||||
qdict = qdict_new();
|
||||
g_assert(qdict != NULL);
|
||||
g_assert(qdict_size(qdict) == 0);
|
||||
g_assert(qdict->base.refcnt == 1);
|
||||
g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
|
||||
fail_unless(qdict != NULL);
|
||||
fail_unless(qdict_size(qdict) == 0);
|
||||
fail_unless(qdict->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
g_free(qdict);
|
||||
free(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_put_obj_test(void)
|
||||
START_TEST(qdict_put_obj_test)
|
||||
{
|
||||
QInt *qi;
|
||||
QDict *qdict;
|
||||
@@ -48,10 +49,10 @@ static void qdict_put_obj_test(void)
|
||||
// key "" will have tdb hash 12345
|
||||
qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num)));
|
||||
|
||||
g_assert(qdict_size(qdict) == 1);
|
||||
fail_unless(qdict_size(qdict) == 1);
|
||||
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
|
||||
qi = qobject_to_qint(ent->value);
|
||||
g_assert(qint_get_int(qi) == num);
|
||||
fail_unless(qint_get_int(qi) == num);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
QDECREF(qi);
|
||||
@@ -59,8 +60,9 @@ static void qdict_put_obj_test(void)
|
||||
g_free(ent);
|
||||
g_free(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_destroy_simple_test(void)
|
||||
START_TEST(qdict_destroy_simple_test)
|
||||
{
|
||||
QDict *qdict;
|
||||
|
||||
@@ -70,138 +72,134 @@ static void qdict_destroy_simple_test(void)
|
||||
|
||||
QDECREF(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_get_test(void)
|
||||
static QDict *tests_dict = NULL;
|
||||
|
||||
static void qdict_setup(void)
|
||||
{
|
||||
tests_dict = qdict_new();
|
||||
fail_unless(tests_dict != NULL);
|
||||
}
|
||||
|
||||
static void qdict_teardown(void)
|
||||
{
|
||||
QDECREF(tests_dict);
|
||||
tests_dict = NULL;
|
||||
}
|
||||
|
||||
START_TEST(qdict_get_test)
|
||||
{
|
||||
QInt *qi;
|
||||
QObject *obj;
|
||||
const int value = -42;
|
||||
const char *key = "test";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(value));
|
||||
|
||||
obj = qdict_get(tests_dict, key);
|
||||
g_assert(obj != NULL);
|
||||
fail_unless(obj != NULL);
|
||||
|
||||
qi = qobject_to_qint(obj);
|
||||
g_assert(qint_get_int(qi) == value);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qint_get_int(qi) == value);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_get_int_test(void)
|
||||
START_TEST(qdict_get_int_test)
|
||||
{
|
||||
int ret;
|
||||
const int value = 100;
|
||||
const char *key = "int";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(value));
|
||||
|
||||
ret = qdict_get_int(tests_dict, key);
|
||||
g_assert(ret == value);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(ret == value);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_get_try_int_test(void)
|
||||
START_TEST(qdict_get_try_int_test)
|
||||
{
|
||||
int ret;
|
||||
const int value = 100;
|
||||
const char *key = "int";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(value));
|
||||
|
||||
ret = qdict_get_try_int(tests_dict, key, 0);
|
||||
g_assert(ret == value);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(ret == value);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_get_str_test(void)
|
||||
START_TEST(qdict_get_str_test)
|
||||
{
|
||||
const char *p;
|
||||
const char *key = "key";
|
||||
const char *str = "string";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qstring_from_str(str));
|
||||
|
||||
p = qdict_get_str(tests_dict, key);
|
||||
g_assert(p != NULL);
|
||||
g_assert(strcmp(p, str) == 0);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(p != NULL);
|
||||
fail_unless(strcmp(p, str) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_get_try_str_test(void)
|
||||
START_TEST(qdict_get_try_str_test)
|
||||
{
|
||||
const char *p;
|
||||
const char *key = "key";
|
||||
const char *str = "string";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qstring_from_str(str));
|
||||
|
||||
p = qdict_get_try_str(tests_dict, key);
|
||||
g_assert(p != NULL);
|
||||
g_assert(strcmp(p, str) == 0);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(p != NULL);
|
||||
fail_unless(strcmp(p, str) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_haskey_not_test(void)
|
||||
START_TEST(qdict_haskey_not_test)
|
||||
{
|
||||
QDict *tests_dict = qdict_new();
|
||||
g_assert(qdict_haskey(tests_dict, "test") == 0);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qdict_haskey(tests_dict, "test") == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_haskey_test(void)
|
||||
START_TEST(qdict_haskey_test)
|
||||
{
|
||||
const char *key = "test";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(0));
|
||||
g_assert(qdict_haskey(tests_dict, key) == 1);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qdict_haskey(tests_dict, key) == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_del_test(void)
|
||||
START_TEST(qdict_del_test)
|
||||
{
|
||||
const char *key = "key test";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qstring_from_str("foo"));
|
||||
g_assert(qdict_size(tests_dict) == 1);
|
||||
fail_unless(qdict_size(tests_dict) == 1);
|
||||
|
||||
qdict_del(tests_dict, key);
|
||||
|
||||
g_assert(qdict_size(tests_dict) == 0);
|
||||
g_assert(qdict_haskey(tests_dict, key) == 0);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qdict_size(tests_dict) == 0);
|
||||
fail_unless(qdict_haskey(tests_dict, key) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qobject_to_qdict_test(void)
|
||||
START_TEST(qobject_to_qdict_test)
|
||||
{
|
||||
QDict *tests_dict = qdict_new();
|
||||
g_assert(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_iterapi_test(void)
|
||||
START_TEST(qdict_iterapi_test)
|
||||
{
|
||||
int count;
|
||||
const QDictEntry *ent;
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
g_assert(qdict_first(tests_dict) == NULL);
|
||||
fail_unless(qdict_first(tests_dict) == NULL);
|
||||
|
||||
qdict_put(tests_dict, "key1", qint_from_int(1));
|
||||
qdict_put(tests_dict, "key2", qint_from_int(2));
|
||||
@@ -209,52 +207,47 @@ static void qdict_iterapi_test(void)
|
||||
|
||||
count = 0;
|
||||
for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
|
||||
g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
|
||||
fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
|
||||
count++;
|
||||
}
|
||||
|
||||
g_assert(count == qdict_size(tests_dict));
|
||||
fail_unless(count == qdict_size(tests_dict));
|
||||
|
||||
/* Do it again to test restarting */
|
||||
count = 0;
|
||||
for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
|
||||
g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
|
||||
fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
|
||||
count++;
|
||||
}
|
||||
|
||||
g_assert(count == qdict_size(tests_dict));
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(count == qdict_size(tests_dict));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*
|
||||
* Errors test-cases
|
||||
*/
|
||||
|
||||
static void qdict_put_exists_test(void)
|
||||
START_TEST(qdict_put_exists_test)
|
||||
{
|
||||
int value;
|
||||
const char *key = "exists";
|
||||
QDict *tests_dict = qdict_new();
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(1));
|
||||
qdict_put(tests_dict, key, qint_from_int(2));
|
||||
|
||||
value = qdict_get_int(tests_dict, key);
|
||||
g_assert(value == 2);
|
||||
fail_unless(value == 2);
|
||||
|
||||
g_assert(qdict_size(tests_dict) == 1);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qdict_size(tests_dict) == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qdict_get_not_exists_test(void)
|
||||
START_TEST(qdict_get_not_exists_test)
|
||||
{
|
||||
QDict *tests_dict = qdict_new();
|
||||
g_assert(qdict_get(tests_dict, "foo") == NULL);
|
||||
|
||||
QDECREF(tests_dict);
|
||||
fail_unless(qdict_get(tests_dict, "foo") == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*
|
||||
* Stress test-case
|
||||
@@ -283,7 +276,7 @@ static QString *read_line(FILE *file, char *key)
|
||||
|
||||
#define reset_file(file) fseek(file, 0L, SEEK_SET)
|
||||
|
||||
static void qdict_stress_test(void)
|
||||
START_TEST(qdict_stress_test)
|
||||
{
|
||||
size_t lines;
|
||||
char key[128];
|
||||
@@ -293,11 +286,11 @@ static void qdict_stress_test(void)
|
||||
const char *test_file_path = "qdict-test-data.txt";
|
||||
|
||||
test_file = fopen(test_file_path, "r");
|
||||
g_assert(test_file != NULL);
|
||||
fail_unless(test_file != NULL);
|
||||
|
||||
// Create the dict
|
||||
qdict = qdict_new();
|
||||
g_assert(qdict != NULL);
|
||||
fail_unless(qdict != NULL);
|
||||
|
||||
// Add everything from the test file
|
||||
for (lines = 0;; lines++) {
|
||||
@@ -307,7 +300,7 @@ static void qdict_stress_test(void)
|
||||
|
||||
qdict_put(qdict, key, value);
|
||||
}
|
||||
g_assert(qdict_size(qdict) == lines);
|
||||
fail_unless(qdict_size(qdict) == lines);
|
||||
|
||||
// Check if everything is really in there
|
||||
reset_file(test_file);
|
||||
@@ -321,9 +314,9 @@ static void qdict_stress_test(void)
|
||||
str1 = qstring_get_str(value);
|
||||
|
||||
str2 = qdict_get_str(qdict, key);
|
||||
g_assert(str2 != NULL);
|
||||
fail_unless(str2 != NULL);
|
||||
|
||||
g_assert(strcmp(str1, str2) == 0);
|
||||
fail_unless(strcmp(str1, str2) == 0);
|
||||
|
||||
QDECREF(value);
|
||||
}
|
||||
@@ -338,41 +331,72 @@ static void qdict_stress_test(void)
|
||||
qdict_del(qdict, key);
|
||||
QDECREF(value);
|
||||
|
||||
g_assert(qdict_haskey(qdict, key) == 0);
|
||||
fail_unless(qdict_haskey(qdict, key) == 0);
|
||||
}
|
||||
fclose(test_file);
|
||||
|
||||
g_assert(qdict_size(qdict) == 0);
|
||||
fail_unless(qdict_size(qdict) == 0);
|
||||
QDECREF(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static Suite *qdict_suite(void)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
Suite *s;
|
||||
TCase *qdict_public_tcase;
|
||||
TCase *qdict_public2_tcase;
|
||||
TCase *qdict_stress_tcase;
|
||||
TCase *qdict_errors_tcase;
|
||||
|
||||
g_test_add_func("/public/new", qdict_new_test);
|
||||
g_test_add_func("/public/put_obj", qdict_put_obj_test);
|
||||
g_test_add_func("/public/destroy_simple", qdict_destroy_simple_test);
|
||||
s = suite_create("QDict test-suite");
|
||||
|
||||
qdict_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qdict_public_tcase);
|
||||
tcase_add_test(qdict_public_tcase, qdict_new_test);
|
||||
tcase_add_test(qdict_public_tcase, qdict_put_obj_test);
|
||||
tcase_add_test(qdict_public_tcase, qdict_destroy_simple_test);
|
||||
|
||||
/* Continue, but now with fixtures */
|
||||
g_test_add_func("/public/get", qdict_get_test);
|
||||
g_test_add_func("/public/get_int", qdict_get_int_test);
|
||||
g_test_add_func("/public/get_try_int", qdict_get_try_int_test);
|
||||
g_test_add_func("/public/get_str", qdict_get_str_test);
|
||||
g_test_add_func("/public/get_try_str", qdict_get_try_str_test);
|
||||
g_test_add_func("/public/haskey_not", qdict_haskey_not_test);
|
||||
g_test_add_func("/public/haskey", qdict_haskey_test);
|
||||
g_test_add_func("/public/del", qdict_del_test);
|
||||
g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
|
||||
g_test_add_func("/public/iterapi", qdict_iterapi_test);
|
||||
qdict_public2_tcase = tcase_create("Public Interface (2)");
|
||||
suite_add_tcase(s, qdict_public2_tcase);
|
||||
tcase_add_checked_fixture(qdict_public2_tcase, qdict_setup, qdict_teardown);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_int_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_try_int_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_str_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_try_str_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_haskey_not_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_haskey_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_del_test);
|
||||
tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_iterapi_test);
|
||||
|
||||
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
|
||||
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
|
||||
qdict_errors_tcase = tcase_create("Errors");
|
||||
suite_add_tcase(s, qdict_errors_tcase);
|
||||
tcase_add_checked_fixture(qdict_errors_tcase, qdict_setup, qdict_teardown);
|
||||
tcase_add_test(qdict_errors_tcase, qdict_put_exists_test);
|
||||
tcase_add_test(qdict_errors_tcase, qdict_get_not_exists_test);
|
||||
|
||||
/* The Big one */
|
||||
if (g_test_slow()) {
|
||||
g_test_add_func("/stress/test", qdict_stress_test);
|
||||
}
|
||||
qdict_stress_tcase = tcase_create("Stress Test");
|
||||
suite_add_tcase(s, qdict_stress_tcase);
|
||||
tcase_add_test(qdict_stress_tcase, qdict_stress_test);
|
||||
|
||||
return g_test_run();
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qdict_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* QFloat unit-tests.
|
||||
*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qfloat.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qfloat_from_double_test)
|
||||
{
|
||||
QFloat *qf;
|
||||
const double value = -42.23423;
|
||||
|
||||
qf = qfloat_from_double(value);
|
||||
fail_unless(qf != NULL);
|
||||
fail_unless(qf->value == value);
|
||||
fail_unless(qf->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qf)) == QTYPE_QFLOAT);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
g_free(qf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qfloat_destroy_test)
|
||||
{
|
||||
QFloat *qf = qfloat_from_double(0.0);
|
||||
QDECREF(qf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qfloat_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qfloat_public_tcase;
|
||||
|
||||
s = suite_create("QFloat test-suite");
|
||||
|
||||
qfloat_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qfloat_public_tcase);
|
||||
tcase_add_test(qfloat_public_tcase, qfloat_from_double_test);
|
||||
tcase_add_test(qfloat_public_tcase, qfloat_destroy_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qfloat_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* QInt unit-tests.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qint.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qint_from_int_test)
|
||||
{
|
||||
QInt *qi;
|
||||
const int value = -42;
|
||||
|
||||
qi = qint_from_int(value);
|
||||
fail_unless(qi != NULL);
|
||||
fail_unless(qi->value == value);
|
||||
fail_unless(qi->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qi)) == QTYPE_QINT);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
g_free(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qint_destroy_test)
|
||||
{
|
||||
QInt *qi = qint_from_int(0);
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qint_from_int64_test)
|
||||
{
|
||||
QInt *qi;
|
||||
const int64_t value = 0x1234567890abcdefLL;
|
||||
|
||||
qi = qint_from_int(value);
|
||||
fail_unless((int64_t) qi->value == value);
|
||||
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qint_get_int_test)
|
||||
{
|
||||
QInt *qi;
|
||||
const int value = 123456;
|
||||
|
||||
qi = qint_from_int(value);
|
||||
fail_unless(qint_get_int(qi) == value);
|
||||
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qobject_to_qint_test)
|
||||
{
|
||||
QInt *qi;
|
||||
|
||||
qi = qint_from_int(0);
|
||||
fail_unless(qobject_to_qint(QOBJECT(qi)) == qi);
|
||||
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qint_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qint_public_tcase;
|
||||
|
||||
s = suite_create("QInt test-suite");
|
||||
|
||||
qint_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qint_public_tcase);
|
||||
tcase_add_test(qint_public_tcase, qint_from_int_test);
|
||||
tcase_add_test(qint_public_tcase, qint_destroy_test);
|
||||
tcase_add_test(qint_public_tcase, qint_from_int64_test);
|
||||
tcase_add_test(qint_public_tcase, qint_get_int_test);
|
||||
tcase_add_test(qint_public_tcase, qobject_to_qint_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qint_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
+194
-180
@@ -8,7 +8,7 @@
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include <glib.h>
|
||||
#include <check.h>
|
||||
|
||||
#include "qstring.h"
|
||||
#include "qint.h"
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
static void escaped_string(void)
|
||||
START_TEST(escaped_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -52,23 +52,28 @@ static void escaped_string(void)
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QSTRING);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0,
|
||||
"%s != %s\n", qstring_get_str(str), test_cases[i].decoded);
|
||||
|
||||
if (test_cases[i].skip == 0) {
|
||||
str = qobject_to_json(obj);
|
||||
g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
|
||||
fail_unless(strcmp(qstring_get_str(str),test_cases[i].encoded) == 0,
|
||||
"%s != %s\n", qstring_get_str(str),
|
||||
test_cases[i].encoded);
|
||||
|
||||
qobject_decref(obj);
|
||||
}
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void simple_string(void)
|
||||
START_TEST(simple_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -87,22 +92,23 @@ static void simple_string(void)
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QSTRING);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
|
||||
qobject_decref(obj);
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void single_quote_string(void)
|
||||
START_TEST(single_quote_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -121,17 +127,18 @@ static void single_quote_string(void)
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QSTRING);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void vararg_string(void)
|
||||
START_TEST(vararg_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -148,17 +155,18 @@ static void vararg_string(void)
|
||||
|
||||
obj = qobject_from_jsonf("%s", test_cases[i].decoded);
|
||||
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QSTRING);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void simple_number(void)
|
||||
START_TEST(simple_number)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -179,24 +187,25 @@ static void simple_number(void)
|
||||
QInt *qint;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QINT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QINT);
|
||||
|
||||
qint = qobject_to_qint(obj);
|
||||
g_assert(qint_get_int(qint) == test_cases[i].decoded);
|
||||
fail_unless(qint_get_int(qint) == test_cases[i].decoded);
|
||||
if (test_cases[i].skip == 0) {
|
||||
QString *str;
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
QDECREF(str);
|
||||
}
|
||||
|
||||
QDECREF(qint);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void float_number(void)
|
||||
START_TEST(float_number)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -216,25 +225,26 @@ static void float_number(void)
|
||||
QFloat *qfloat;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QFLOAT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
|
||||
|
||||
qfloat = qobject_to_qfloat(obj);
|
||||
g_assert(qfloat_get_double(qfloat) == test_cases[i].decoded);
|
||||
fail_unless(qfloat_get_double(qfloat) == test_cases[i].decoded);
|
||||
|
||||
if (test_cases[i].skip == 0) {
|
||||
QString *str;
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
QDECREF(str);
|
||||
}
|
||||
|
||||
QDECREF(qfloat);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void vararg_number(void)
|
||||
START_TEST(vararg_number)
|
||||
{
|
||||
QObject *obj;
|
||||
QInt *qint;
|
||||
@@ -244,83 +254,85 @@ static void vararg_number(void)
|
||||
double valuef = 2.323423423;
|
||||
|
||||
obj = qobject_from_jsonf("%d", value);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QINT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QINT);
|
||||
|
||||
qint = qobject_to_qint(obj);
|
||||
g_assert(qint_get_int(qint) == value);
|
||||
fail_unless(qint_get_int(qint) == value);
|
||||
|
||||
QDECREF(qint);
|
||||
|
||||
obj = qobject_from_jsonf("%" PRId64, value64);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QINT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QINT);
|
||||
|
||||
qint = qobject_to_qint(obj);
|
||||
g_assert(qint_get_int(qint) == value64);
|
||||
fail_unless(qint_get_int(qint) == value64);
|
||||
|
||||
QDECREF(qint);
|
||||
|
||||
obj = qobject_from_jsonf("%f", valuef);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QFLOAT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
|
||||
|
||||
qfloat = qobject_to_qfloat(obj);
|
||||
g_assert(qfloat_get_double(qfloat) == valuef);
|
||||
fail_unless(qfloat_get_double(qfloat) == valuef);
|
||||
|
||||
QDECREF(qfloat);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void keyword_literal(void)
|
||||
START_TEST(keyword_literal)
|
||||
{
|
||||
QObject *obj;
|
||||
QBool *qbool;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json("true");
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QBOOL);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
g_assert(qbool_get_int(qbool) != 0);
|
||||
fail_unless(qbool_get_int(qbool) != 0);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), "true") == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), "true") == 0);
|
||||
QDECREF(str);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_json("false");
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QBOOL);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
g_assert(qbool_get_int(qbool) == 0);
|
||||
fail_unless(qbool_get_int(qbool) == 0);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
g_assert(strcmp(qstring_get_str(str), "false") == 0);
|
||||
fail_unless(strcmp(qstring_get_str(str), "false") == 0);
|
||||
QDECREF(str);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_jsonf("%i", false);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QBOOL);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
g_assert(qbool_get_int(qbool) == 0);
|
||||
fail_unless(qbool_get_int(qbool) == 0);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_jsonf("%i", true);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QBOOL);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
g_assert(qbool_get_int(qbool) != 0);
|
||||
fail_unless(qbool_get_int(qbool) != 0);
|
||||
|
||||
QDECREF(qbool);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
typedef struct LiteralQDictEntry LiteralQDictEntry;
|
||||
typedef struct LiteralQObject LiteralQObject;
|
||||
@@ -414,7 +426,7 @@ static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void simple_dict(void)
|
||||
START_TEST(simple_dict)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -448,77 +460,26 @@ static void simple_dict(void)
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QDICT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QDICT);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
qobject_decref(obj);
|
||||
|
||||
obj = qobject_from_json(qstring_get_str(str));
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QDICT);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QDICT);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
qobject_decref(obj);
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*
|
||||
* this generates json of the form:
|
||||
* a(0,m) = [0, 1, ..., m-1]
|
||||
* a(n,m) = {
|
||||
* 'key0': a(0,m),
|
||||
* 'key1': a(1,m),
|
||||
* ...
|
||||
* 'key(n-1)': a(n-1,m)
|
||||
* }
|
||||
*/
|
||||
static void gen_test_json(GString *gstr, int nest_level_max,
|
||||
int elem_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
g_assert(gstr);
|
||||
if (nest_level_max == 0) {
|
||||
g_string_append(gstr, "[");
|
||||
for (i = 0; i < elem_count; i++) {
|
||||
g_string_append_printf(gstr, "%d", i);
|
||||
if (i < elem_count - 1) {
|
||||
g_string_append_printf(gstr, ", ");
|
||||
}
|
||||
}
|
||||
g_string_append(gstr, "]");
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append(gstr, "{");
|
||||
for (i = 0; i < nest_level_max; i++) {
|
||||
g_string_append_printf(gstr, "'key%d': ", i);
|
||||
gen_test_json(gstr, i, elem_count);
|
||||
if (i < nest_level_max - 1) {
|
||||
g_string_append(gstr, ",");
|
||||
}
|
||||
}
|
||||
g_string_append(gstr, "}");
|
||||
}
|
||||
|
||||
static void large_dict(void)
|
||||
{
|
||||
GString *gstr = g_string_new("");
|
||||
QObject *obj;
|
||||
|
||||
gen_test_json(gstr, 10, 100);
|
||||
obj = qobject_from_json(gstr->str);
|
||||
g_assert(obj != NULL);
|
||||
|
||||
qobject_decref(obj);
|
||||
g_string_free(gstr, true);
|
||||
}
|
||||
|
||||
static void simple_list(void)
|
||||
START_TEST(simple_list)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -563,25 +524,26 @@ static void simple_list(void)
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QLIST);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
qobject_decref(obj);
|
||||
|
||||
obj = qobject_from_json(qstring_get_str(str));
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QLIST);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
qobject_decref(obj);
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void simple_whitespace(void)
|
||||
START_TEST(simple_whitespace)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
@@ -631,26 +593,27 @@ static void simple_whitespace(void)
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QLIST);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
qobject_decref(obj);
|
||||
|
||||
obj = qobject_from_json(qstring_get_str(str));
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QLIST);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
qobject_decref(obj);
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void simple_varargs(void)
|
||||
START_TEST(simple_varargs)
|
||||
{
|
||||
QObject *embedded_obj;
|
||||
QObject *obj;
|
||||
@@ -664,118 +627,169 @@ static void simple_varargs(void)
|
||||
{}}));
|
||||
|
||||
embedded_obj = qobject_from_json("[32, 42]");
|
||||
g_assert(embedded_obj != NULL);
|
||||
fail_unless(embedded_obj != NULL);
|
||||
|
||||
obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj);
|
||||
g_assert(obj != NULL);
|
||||
fail_unless(obj != NULL);
|
||||
|
||||
g_assert(compare_litqobj_to_qobj(&decoded, obj) == 1);
|
||||
fail_unless(compare_litqobj_to_qobj(&decoded, obj) == 1);
|
||||
|
||||
qobject_decref(obj);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void empty_input(void)
|
||||
START_TEST(empty_input)
|
||||
{
|
||||
const char *empty = "";
|
||||
|
||||
QObject *obj = qobject_from_json(empty);
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_string(void)
|
||||
START_TEST(unterminated_string)
|
||||
{
|
||||
QObject *obj = qobject_from_json("\"abc");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_sq_string(void)
|
||||
START_TEST(unterminated_sq_string)
|
||||
{
|
||||
QObject *obj = qobject_from_json("'abc");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_escape(void)
|
||||
START_TEST(unterminated_escape)
|
||||
{
|
||||
QObject *obj = qobject_from_json("\"abc\\\"");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_array(void)
|
||||
START_TEST(unterminated_array)
|
||||
{
|
||||
QObject *obj = qobject_from_json("[32");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_array_comma(void)
|
||||
START_TEST(unterminated_array_comma)
|
||||
{
|
||||
QObject *obj = qobject_from_json("[32,");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void invalid_array_comma(void)
|
||||
START_TEST(invalid_array_comma)
|
||||
{
|
||||
QObject *obj = qobject_from_json("[32,}");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_dict(void)
|
||||
START_TEST(unterminated_dict)
|
||||
{
|
||||
QObject *obj = qobject_from_json("{'abc':32");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_dict_comma(void)
|
||||
START_TEST(unterminated_dict_comma)
|
||||
{
|
||||
QObject *obj = qobject_from_json("{'abc':32,");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void invalid_dict_comma(void)
|
||||
#if 0
|
||||
START_TEST(invalid_dict_comma)
|
||||
{
|
||||
QObject *obj = qobject_from_json("{'abc':32,}");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void unterminated_literal(void)
|
||||
START_TEST(unterminated_literal)
|
||||
{
|
||||
QObject *obj = qobject_from_json("nul");
|
||||
g_assert(obj == NULL);
|
||||
fail_unless(obj == NULL);
|
||||
}
|
||||
END_TEST
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static Suite *qjson_suite(void)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
Suite *suite;
|
||||
TCase *string_literals, *number_literals, *keyword_literals;
|
||||
TCase *dicts, *lists, *whitespace, *varargs, *errors;
|
||||
|
||||
g_test_add_func("/literals/string/simple", simple_string);
|
||||
g_test_add_func("/literals/string/escaped", escaped_string);
|
||||
g_test_add_func("/literals/string/single_quote", single_quote_string);
|
||||
g_test_add_func("/literals/string/vararg", vararg_string);
|
||||
string_literals = tcase_create("String Literals");
|
||||
tcase_add_test(string_literals, simple_string);
|
||||
tcase_add_test(string_literals, escaped_string);
|
||||
tcase_add_test(string_literals, single_quote_string);
|
||||
tcase_add_test(string_literals, vararg_string);
|
||||
|
||||
g_test_add_func("/literals/number/simple", simple_number);
|
||||
g_test_add_func("/literals/number/float", float_number);
|
||||
g_test_add_func("/literals/number/vararg", vararg_number);
|
||||
number_literals = tcase_create("Number Literals");
|
||||
tcase_add_test(number_literals, simple_number);
|
||||
tcase_add_test(number_literals, float_number);
|
||||
tcase_add_test(number_literals, vararg_number);
|
||||
|
||||
g_test_add_func("/literals/keyword", keyword_literal);
|
||||
keyword_literals = tcase_create("Keywords");
|
||||
tcase_add_test(keyword_literals, keyword_literal);
|
||||
dicts = tcase_create("Objects");
|
||||
tcase_add_test(dicts, simple_dict);
|
||||
lists = tcase_create("Lists");
|
||||
tcase_add_test(lists, simple_list);
|
||||
|
||||
g_test_add_func("/dicts/simple_dict", simple_dict);
|
||||
g_test_add_func("/dicts/large_dict", large_dict);
|
||||
g_test_add_func("/lists/simple_list", simple_list);
|
||||
whitespace = tcase_create("Whitespace");
|
||||
tcase_add_test(whitespace, simple_whitespace);
|
||||
|
||||
g_test_add_func("/whitespace/simple_whitespace", simple_whitespace);
|
||||
varargs = tcase_create("Varargs");
|
||||
tcase_add_test(varargs, simple_varargs);
|
||||
|
||||
g_test_add_func("/varargs/simple_varargs", simple_varargs);
|
||||
errors = tcase_create("Invalid JSON");
|
||||
tcase_add_test(errors, empty_input);
|
||||
tcase_add_test(errors, unterminated_string);
|
||||
tcase_add_test(errors, unterminated_escape);
|
||||
tcase_add_test(errors, unterminated_sq_string);
|
||||
tcase_add_test(errors, unterminated_array);
|
||||
tcase_add_test(errors, unterminated_array_comma);
|
||||
tcase_add_test(errors, invalid_array_comma);
|
||||
tcase_add_test(errors, unterminated_dict);
|
||||
tcase_add_test(errors, unterminated_dict_comma);
|
||||
#if 0
|
||||
/* FIXME: this print parse error messages on stderr. */
|
||||
tcase_add_test(errors, invalid_dict_comma);
|
||||
tcase_add_test(errors, unterminated_literal);
|
||||
#endif
|
||||
|
||||
g_test_add_func("/errors/empty_input", empty_input);
|
||||
g_test_add_func("/errors/unterminated/string", unterminated_string);
|
||||
g_test_add_func("/errors/unterminated/escape", unterminated_escape);
|
||||
g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string);
|
||||
g_test_add_func("/errors/unterminated/array", unterminated_array);
|
||||
g_test_add_func("/errors/unterminated/array_comma", unterminated_array_comma);
|
||||
g_test_add_func("/errors/unterminated/dict", unterminated_dict);
|
||||
g_test_add_func("/errors/unterminated/dict_comma", unterminated_dict_comma);
|
||||
g_test_add_func("/errors/invalid_array_comma", invalid_array_comma);
|
||||
g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma);
|
||||
g_test_add_func("/errors/unterminated/literal", unterminated_literal);
|
||||
suite = suite_create("QJSON test-suite");
|
||||
suite_add_tcase(suite, string_literals);
|
||||
suite_add_tcase(suite, number_literals);
|
||||
suite_add_tcase(suite, keyword_literals);
|
||||
suite_add_tcase(suite, dicts);
|
||||
suite_add_tcase(suite, lists);
|
||||
suite_add_tcase(suite, whitespace);
|
||||
suite_add_tcase(suite, varargs);
|
||||
suite_add_tcase(suite, errors);
|
||||
|
||||
return g_test_run();
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qjson_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#include <glib.h>
|
||||
#include <check.h>
|
||||
|
||||
#include "qint.h"
|
||||
#include "qlist.h"
|
||||
@@ -20,20 +20,21 @@
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
static void qlist_new_test(void)
|
||||
START_TEST(qlist_new_test)
|
||||
{
|
||||
QList *qlist;
|
||||
|
||||
qlist = qlist_new();
|
||||
g_assert(qlist != NULL);
|
||||
g_assert(qlist->base.refcnt == 1);
|
||||
g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
|
||||
fail_unless(qlist != NULL);
|
||||
fail_unless(qlist->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
|
||||
|
||||
// destroy doesn't exist yet
|
||||
g_free(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qlist_append_test(void)
|
||||
START_TEST(qlist_append_test)
|
||||
{
|
||||
QInt *qi;
|
||||
QList *qlist;
|
||||
@@ -45,28 +46,30 @@ static void qlist_append_test(void)
|
||||
qlist_append(qlist, qi);
|
||||
|
||||
entry = QTAILQ_FIRST(&qlist->head);
|
||||
g_assert(entry != NULL);
|
||||
g_assert(entry->value == QOBJECT(qi));
|
||||
fail_unless(entry != NULL);
|
||||
fail_unless(entry->value == QOBJECT(qi));
|
||||
|
||||
// destroy doesn't exist yet
|
||||
QDECREF(qi);
|
||||
g_free(entry);
|
||||
g_free(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qobject_to_qlist_test(void)
|
||||
START_TEST(qobject_to_qlist_test)
|
||||
{
|
||||
QList *qlist;
|
||||
|
||||
qlist = qlist_new();
|
||||
|
||||
g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist);
|
||||
fail_unless(qobject_to_qlist(QOBJECT(qlist)) == qlist);
|
||||
|
||||
// destroy doesn't exist yet
|
||||
g_free(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void qlist_destroy_test(void)
|
||||
START_TEST(qlist_destroy_test)
|
||||
{
|
||||
int i;
|
||||
QList *qlist;
|
||||
@@ -78,6 +81,7 @@ static void qlist_destroy_test(void)
|
||||
|
||||
QDECREF(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static int iter_called;
|
||||
static const int iter_max = 42;
|
||||
@@ -86,16 +90,16 @@ static void iter_func(QObject *obj, void *opaque)
|
||||
{
|
||||
QInt *qi;
|
||||
|
||||
g_assert(opaque == NULL);
|
||||
fail_unless(opaque == NULL);
|
||||
|
||||
qi = qobject_to_qint(obj);
|
||||
g_assert(qi != NULL);
|
||||
g_assert((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
|
||||
fail_unless(qi != NULL);
|
||||
fail_unless((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
|
||||
|
||||
iter_called++;
|
||||
}
|
||||
|
||||
static void qlist_iter_test(void)
|
||||
START_TEST(qlist_iter_test)
|
||||
{
|
||||
int i;
|
||||
QList *qlist;
|
||||
@@ -108,20 +112,42 @@ static void qlist_iter_test(void)
|
||||
iter_called = 0;
|
||||
qlist_iter(qlist, iter_func, NULL);
|
||||
|
||||
g_assert(iter_called == iter_max);
|
||||
fail_unless(iter_called == iter_max);
|
||||
|
||||
QDECREF(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static Suite *QList_suite(void)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
Suite *s;
|
||||
TCase *qlist_public_tcase;
|
||||
|
||||
g_test_add_func("/public/new", qlist_new_test);
|
||||
g_test_add_func("/public/append", qlist_append_test);
|
||||
g_test_add_func("/public/to_qlist", qobject_to_qlist_test);
|
||||
g_test_add_func("/public/destroy", qlist_destroy_test);
|
||||
g_test_add_func("/public/iter", qlist_iter_test);
|
||||
s = suite_create("QList suite");
|
||||
|
||||
return g_test_run();
|
||||
qlist_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qlist_public_tcase);
|
||||
tcase_add_test(qlist_public_tcase, qlist_new_test);
|
||||
tcase_add_test(qlist_public_tcase, qlist_append_test);
|
||||
tcase_add_test(qlist_public_tcase, qobject_to_qlist_test);
|
||||
tcase_add_test(qlist_public_tcase, qlist_destroy_test);
|
||||
tcase_add_test(qlist_public_tcase, qlist_iter_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = QList_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* QString unit-tests.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qstring.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qstring_from_str_test)
|
||||
{
|
||||
QString *qstring;
|
||||
const char *str = "QEMU";
|
||||
|
||||
qstring = qstring_from_str(str);
|
||||
fail_unless(qstring != NULL);
|
||||
fail_unless(qstring->base.refcnt == 1);
|
||||
fail_unless(strcmp(str, qstring->string) == 0);
|
||||
fail_unless(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
g_free(qstring->string);
|
||||
g_free(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_destroy_test)
|
||||
{
|
||||
QString *qstring = qstring_from_str("destroy test");
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_get_str_test)
|
||||
{
|
||||
QString *qstring;
|
||||
const char *ret_str;
|
||||
const char *str = "QEMU/KVM";
|
||||
|
||||
qstring = qstring_from_str(str);
|
||||
ret_str = qstring_get_str(qstring);
|
||||
fail_unless(strcmp(ret_str, str) == 0);
|
||||
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_append_chr_test)
|
||||
{
|
||||
int i;
|
||||
QString *qstring;
|
||||
const char *str = "qstring append char unit-test";
|
||||
|
||||
qstring = qstring_new();
|
||||
|
||||
for (i = 0; str[i]; i++)
|
||||
qstring_append_chr(qstring, str[i]);
|
||||
|
||||
fail_unless(strcmp(str, qstring_get_str(qstring)) == 0);
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_from_substr_test)
|
||||
{
|
||||
QString *qs;
|
||||
|
||||
qs = qstring_from_substr("virtualization", 3, 9);
|
||||
fail_unless(qs != NULL);
|
||||
fail_unless(strcmp(qstring_get_str(qs), "tualiza") == 0);
|
||||
|
||||
QDECREF(qs);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
START_TEST(qobject_to_qstring_test)
|
||||
{
|
||||
QString *qstring;
|
||||
|
||||
qstring = qstring_from_str("foo");
|
||||
fail_unless(qobject_to_qstring(QOBJECT(qstring)) == qstring);
|
||||
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qstring_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qstring_public_tcase;
|
||||
|
||||
s = suite_create("QString test-suite");
|
||||
|
||||
qstring_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qstring_public_tcase);
|
||||
tcase_add_test(qstring_public_tcase, qstring_from_str_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_destroy_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_get_str_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_append_chr_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_from_substr_test);
|
||||
tcase_add_test(qstring_public_tcase, qobject_to_qstring_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qstring_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "cmd.h"
|
||||
#include "qemu-aio.h"
|
||||
#include "main-loop.h"
|
||||
|
||||
#define _(x) x /* not gettext support yet */
|
||||
|
||||
@@ -147,7 +146,7 @@ static void prep_fetchline(void *opaque)
|
||||
{
|
||||
int *fetchable = opaque;
|
||||
|
||||
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
|
||||
*fetchable= 1;
|
||||
}
|
||||
|
||||
@@ -194,11 +193,12 @@ void command_loop(void)
|
||||
if (!prompted) {
|
||||
printf("%s", get_prompt());
|
||||
fflush(stdout);
|
||||
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
|
||||
qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL,
|
||||
NULL, &fetchable);
|
||||
prompted = 1;
|
||||
}
|
||||
|
||||
main_loop_wait(false);
|
||||
qemu_aio_wait();
|
||||
|
||||
if (!fetchable) {
|
||||
continue;
|
||||
@@ -221,7 +221,7 @@ void command_loop(void)
|
||||
prompted = 0;
|
||||
fetchable = 0;
|
||||
}
|
||||
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
|
||||
qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* from libxcmd/input.c */
|
||||
@@ -418,37 +418,31 @@ cvtstr(
|
||||
char *str,
|
||||
size_t size)
|
||||
{
|
||||
char *trim;
|
||||
const char *suffix;
|
||||
const char *fmt;
|
||||
int precise;
|
||||
|
||||
precise = ((double)value * 1000 == (double)(int)value * 1000);
|
||||
|
||||
if (value >= EXABYTES(1)) {
|
||||
suffix = " EiB";
|
||||
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
|
||||
fmt = precise ? "%.f EiB" : "%.3f EiB";
|
||||
snprintf(str, size, fmt, TO_EXABYTES(value));
|
||||
} else if (value >= PETABYTES(1)) {
|
||||
suffix = " PiB";
|
||||
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
|
||||
fmt = precise ? "%.f PiB" : "%.3f PiB";
|
||||
snprintf(str, size, fmt, TO_PETABYTES(value));
|
||||
} else if (value >= TERABYTES(1)) {
|
||||
suffix = " TiB";
|
||||
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
|
||||
fmt = precise ? "%.f TiB" : "%.3f TiB";
|
||||
snprintf(str, size, fmt, TO_TERABYTES(value));
|
||||
} else if (value >= GIGABYTES(1)) {
|
||||
suffix = " GiB";
|
||||
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
|
||||
fmt = precise ? "%.f GiB" : "%.3f GiB";
|
||||
snprintf(str, size, fmt, TO_GIGABYTES(value));
|
||||
} else if (value >= MEGABYTES(1)) {
|
||||
suffix = " MiB";
|
||||
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
|
||||
fmt = precise ? "%.f MiB" : "%.3f MiB";
|
||||
snprintf(str, size, fmt, TO_MEGABYTES(value));
|
||||
} else if (value >= KILOBYTES(1)) {
|
||||
suffix = " KiB";
|
||||
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
|
||||
fmt = precise ? "%.f KiB" : "%.3f KiB";
|
||||
snprintf(str, size, fmt, TO_KILOBYTES(value));
|
||||
} else {
|
||||
suffix = " bytes";
|
||||
snprintf(str, size - 6, "%f", value);
|
||||
}
|
||||
|
||||
trim = strstr(str, ".000");
|
||||
if (trim) {
|
||||
strcpy(trim, suffix);
|
||||
} else {
|
||||
strcat(str, suffix);
|
||||
snprintf(str, size, "%f bytes", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
+1
-3
@@ -30,10 +30,8 @@
|
||||
# define QEMU_PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#define cat(x,y) x ## y
|
||||
#define cat2(x,y) cat(x,y)
|
||||
#define QEMU_BUILD_BUG_ON(x) \
|
||||
typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1];
|
||||
typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1];
|
||||
|
||||
#if defined __GNUC__
|
||||
# if !QEMU_GNUC_PREREQ(4, 4)
|
||||
|
||||
@@ -176,23 +176,17 @@ void vga_hw_invalidate(void)
|
||||
void vga_hw_screen_dump(const char *filename)
|
||||
{
|
||||
TextConsole *previous_active_console;
|
||||
bool cswitch;
|
||||
|
||||
previous_active_console = active_console;
|
||||
cswitch = previous_active_console && previous_active_console->index != 0;
|
||||
|
||||
/* There is currently no way of specifying which screen we want to dump,
|
||||
so always dump the first one. */
|
||||
if (cswitch) {
|
||||
console_select(0);
|
||||
}
|
||||
console_select(0);
|
||||
if (consoles[0] && consoles[0]->hw_screen_dump) {
|
||||
consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch);
|
||||
} else {
|
||||
error_report("screen dump not implemented");
|
||||
consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
|
||||
}
|
||||
|
||||
if (cswitch) {
|
||||
if (previous_active_console) {
|
||||
console_select(previous_active_console->index);
|
||||
}
|
||||
}
|
||||
@@ -847,26 +841,6 @@ static void console_clear_xy(TextConsole *s, int x, int y)
|
||||
update_xy(s, x, y);
|
||||
}
|
||||
|
||||
/* set cursor, checking bounds */
|
||||
static void set_cursor(TextConsole *s, int x, int y)
|
||||
{
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
if (y >= s->height) {
|
||||
y = s->height - 1;
|
||||
}
|
||||
if (x >= s->width) {
|
||||
x = s->width - 1;
|
||||
}
|
||||
|
||||
s->x = x;
|
||||
s->y = y;
|
||||
}
|
||||
|
||||
static void console_putchar(TextConsole *s, int ch)
|
||||
{
|
||||
TextCell *c;
|
||||
@@ -938,8 +912,7 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
s->esc_params[s->nb_esc_params] * 10 + ch - '0';
|
||||
}
|
||||
} else {
|
||||
if (s->nb_esc_params < MAX_ESC_PARAMS)
|
||||
s->nb_esc_params++;
|
||||
s->nb_esc_params++;
|
||||
if (ch == ';')
|
||||
break;
|
||||
#ifdef DEBUG_CONSOLE
|
||||
@@ -953,37 +926,59 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
set_cursor(s, s->x, s->y - s->esc_params[0]);
|
||||
s->y -= s->esc_params[0];
|
||||
if (s->y < 0) {
|
||||
s->y = 0;
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
/* move cursor down */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
set_cursor(s, s->x, s->y + s->esc_params[0]);
|
||||
s->y += s->esc_params[0];
|
||||
if (s->y >= s->height) {
|
||||
s->y = s->height - 1;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
/* move cursor right */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
set_cursor(s, s->x + s->esc_params[0], s->y);
|
||||
s->x += s->esc_params[0];
|
||||
if (s->x >= s->width) {
|
||||
s->x = s->width - 1;
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
/* move cursor left */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
set_cursor(s, s->x - s->esc_params[0], s->y);
|
||||
s->x -= s->esc_params[0];
|
||||
if (s->x < 0) {
|
||||
s->x = 0;
|
||||
}
|
||||
break;
|
||||
case 'G':
|
||||
/* move cursor to column */
|
||||
set_cursor(s, s->esc_params[0] - 1, s->y);
|
||||
s->x = s->esc_params[0] - 1;
|
||||
if (s->x < 0) {
|
||||
s->x = 0;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'H':
|
||||
/* move cursor to row, column */
|
||||
set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
|
||||
s->x = s->esc_params[1] - 1;
|
||||
if (s->x < 0) {
|
||||
s->x = 0;
|
||||
}
|
||||
s->y = s->esc_params[0] - 1;
|
||||
if (s->y < 0) {
|
||||
s->y = 0;
|
||||
}
|
||||
break;
|
||||
case 'J':
|
||||
switch (s->esc_params[0]) {
|
||||
@@ -1016,17 +1011,16 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
console_clear_xy(s, x, y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'K':
|
||||
switch (s->esc_params[0]) {
|
||||
case 0:
|
||||
/* clear to eol */
|
||||
for(x = s->x; x < s->width; x++) {
|
||||
/* clear to eol */
|
||||
for(x = s->x; x < s->width; x++) {
|
||||
console_clear_xy(s, x, s->y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* clear from beginning of line */
|
||||
for (x = 0; x <= s->x; x++) {
|
||||
@@ -1038,12 +1032,12 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
for(x = 0; x < s->width; x++) {
|
||||
console_clear_xy(s, x, s->y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
console_handle_escape(s);
|
||||
break;
|
||||
console_handle_escape(s);
|
||||
break;
|
||||
case 'n':
|
||||
/* report cursor position */
|
||||
/* TODO: send ESC[row;colR */
|
||||
@@ -1446,6 +1440,9 @@ void console_color_init(DisplayState *ds)
|
||||
}
|
||||
}
|
||||
|
||||
static int n_text_consoles;
|
||||
static CharDriverState *text_consoles[128];
|
||||
|
||||
static void text_console_set_echo(CharDriverState *chr, bool echo)
|
||||
{
|
||||
TextConsole *s = chr->opaque;
|
||||
@@ -1512,7 +1509,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
|
||||
chr->init(chr);
|
||||
}
|
||||
|
||||
CharDriverState *text_console_init(QemuOpts *opts)
|
||||
int text_console_init(QemuOpts *opts, CharDriverState **_chr)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
TextConsole *s;
|
||||
@@ -1521,6 +1518,13 @@ CharDriverState *text_console_init(QemuOpts *opts)
|
||||
|
||||
chr = g_malloc0(sizeof(CharDriverState));
|
||||
|
||||
if (n_text_consoles == 128) {
|
||||
fprintf(stderr, "Too many text consoles\n");
|
||||
exit(1);
|
||||
}
|
||||
text_consoles[n_text_consoles] = chr;
|
||||
n_text_consoles++;
|
||||
|
||||
width = qemu_opt_get_number(opts, "width", 0);
|
||||
if (width == 0)
|
||||
width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH;
|
||||
@@ -1537,7 +1541,7 @@ CharDriverState *text_console_init(QemuOpts *opts)
|
||||
|
||||
if (!s) {
|
||||
g_free(chr);
|
||||
return NULL;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
s->chr = chr;
|
||||
@@ -1545,18 +1549,20 @@ CharDriverState *text_console_init(QemuOpts *opts)
|
||||
s->g_height = height;
|
||||
chr->opaque = s;
|
||||
chr->chr_set_echo = text_console_set_echo;
|
||||
return chr;
|
||||
|
||||
*_chr = chr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void text_consoles_set_display(DisplayState *ds)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nb_consoles; i++) {
|
||||
if (consoles[i]->console_type != GRAPHIC_CONSOLE) {
|
||||
text_console_do_init(consoles[i]->chr, ds);
|
||||
}
|
||||
for (i = 0; i < n_text_consoles; i++) {
|
||||
text_console_do_init(text_consoles[i], ds);
|
||||
}
|
||||
|
||||
n_text_consoles = 0;
|
||||
}
|
||||
|
||||
void qemu_console_resize(DisplayState *ds, int width, int height)
|
||||
@@ -1683,7 +1689,6 @@ PixelFormat qemu_default_pixelformat(int bpp)
|
||||
pf.rbits = 8;
|
||||
pf.gbits = 8;
|
||||
pf.bbits = 8;
|
||||
break;
|
||||
case 32:
|
||||
pf.rmask = 0x00FF0000;
|
||||
pf.gmask = 0x0000FF00;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "qemu-char.h"
|
||||
#include "qdict.h"
|
||||
#include "notify.h"
|
||||
#include "qerror.h"
|
||||
#include "monitor.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* keyboard/mouse support */
|
||||
|
||||
@@ -74,6 +74,8 @@ struct MouseTransformInfo {
|
||||
int a[7];
|
||||
};
|
||||
|
||||
void do_info_mice_print(Monitor *mon, const QObject *data);
|
||||
void do_info_mice(Monitor *mon, QObject **ret_data);
|
||||
void do_mouse_set(Monitor *mon, const QDict *qdict);
|
||||
|
||||
/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
|
||||
@@ -203,13 +205,11 @@ static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int w
|
||||
|
||||
static inline DisplaySurface* qemu_resize_displaysurface(DisplayState *ds, int width, int height)
|
||||
{
|
||||
trace_displaysurface_resize(ds, ds->surface, width, height);
|
||||
return ds->allocator->resize_displaysurface(ds->surface, width, height);
|
||||
}
|
||||
|
||||
static inline void qemu_free_displaysurface(DisplayState *ds)
|
||||
{
|
||||
trace_displaysurface_free(ds, ds->surface);
|
||||
ds->allocator->free_displaysurface(ds->surface);
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
|
||||
|
||||
typedef void (*vga_hw_update_ptr)(void *);
|
||||
typedef void (*vga_hw_invalidate_ptr)(void *);
|
||||
typedef void (*vga_hw_screen_dump_ptr)(void *, const char *, bool cswitch);
|
||||
typedef void (*vga_hw_screen_dump_ptr)(void *, const char *);
|
||||
typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *);
|
||||
|
||||
DisplayState *graphic_console_init(vga_hw_update_ptr update,
|
||||
@@ -359,7 +359,7 @@ void vga_hw_text_update(console_ch_t *chardata);
|
||||
|
||||
int is_graphic_console(void);
|
||||
int is_fixedsize_console(void);
|
||||
CharDriverState *text_console_init(QemuOpts *opts);
|
||||
int text_console_init(QemuOpts *opts, CharDriverState **_chr);
|
||||
void text_consoles_set_display(DisplayState *ds);
|
||||
void console_select(unsigned int index);
|
||||
void console_color_init(DisplayState *ds);
|
||||
@@ -386,10 +386,12 @@ int vnc_display_pw_expire(DisplayState *ds, time_t expires);
|
||||
#else
|
||||
static inline int vnc_display_password(DisplayState *ds, const char *password)
|
||||
{
|
||||
qerror_report(QERR_FEATURE_DISABLED, "vnc");
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires)
|
||||
{
|
||||
qerror_report(QERR_FEATURE_DISABLED, "vnc");
|
||||
return -ENODEV;
|
||||
};
|
||||
#endif
|
||||
|
||||
+9
-92
@@ -26,105 +26,20 @@ typedef struct {
|
||||
Coroutine base;
|
||||
GThread *thread;
|
||||
bool runnable;
|
||||
bool free_on_thread_exit;
|
||||
CoroutineAction action;
|
||||
} CoroutineGThread;
|
||||
|
||||
static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
|
||||
|
||||
/* GLib 2.31 and beyond deprecated various parts of the thread API,
|
||||
* but the new interfaces are not available in older GLib versions
|
||||
* so we have to cope with both.
|
||||
*/
|
||||
#if GLIB_CHECK_VERSION(2, 31, 0)
|
||||
/* Default zero-initialisation is sufficient for 2.31+ GCond */
|
||||
static GCond the_coroutine_cond;
|
||||
static GCond *coroutine_cond = &the_coroutine_cond;
|
||||
static inline void init_coroutine_cond(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Awkwardly, the GPrivate API doesn't provide a way to update the
|
||||
* GDestroyNotify handler for the coroutine key dynamically. So instead
|
||||
* we track whether or not the CoroutineGThread should be freed on
|
||||
* thread exit / coroutine key update using the free_on_thread_exit
|
||||
* field.
|
||||
*/
|
||||
static void coroutine_destroy_notify(gpointer data)
|
||||
{
|
||||
CoroutineGThread *co = data;
|
||||
if (co && co->free_on_thread_exit) {
|
||||
g_free(co);
|
||||
}
|
||||
}
|
||||
|
||||
static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify);
|
||||
|
||||
static inline CoroutineGThread *get_coroutine_key(void)
|
||||
{
|
||||
return g_private_get(&coroutine_key);
|
||||
}
|
||||
|
||||
static inline void set_coroutine_key(CoroutineGThread *co,
|
||||
bool free_on_thread_exit)
|
||||
{
|
||||
/* Unlike g_static_private_set() this does not call the GDestroyNotify
|
||||
* if the previous value of the key was NULL. Fortunately we only need
|
||||
* the GDestroyNotify in the non-NULL key case.
|
||||
*/
|
||||
co->free_on_thread_exit = free_on_thread_exit;
|
||||
g_private_replace(&coroutine_key, co);
|
||||
}
|
||||
|
||||
static inline GThread *create_thread(GThreadFunc func, gpointer data)
|
||||
{
|
||||
return g_thread_new("coroutine", func, data);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Handle older GLib versions */
|
||||
static GCond *coroutine_cond;
|
||||
static inline void init_coroutine_cond(void)
|
||||
{
|
||||
coroutine_cond = g_cond_new();
|
||||
}
|
||||
|
||||
static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
|
||||
static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
|
||||
|
||||
static inline CoroutineGThread *get_coroutine_key(void)
|
||||
{
|
||||
return g_static_private_get(&coroutine_key);
|
||||
}
|
||||
|
||||
static inline void set_coroutine_key(CoroutineGThread *co,
|
||||
bool free_on_thread_exit)
|
||||
{
|
||||
g_static_private_set(&coroutine_key, co,
|
||||
free_on_thread_exit ? (GDestroyNotify)g_free : NULL);
|
||||
}
|
||||
|
||||
static inline GThread *create_thread(GThreadFunc func, gpointer data)
|
||||
{
|
||||
return g_thread_create_full(func, data, 0, TRUE, TRUE,
|
||||
G_THREAD_PRIORITY_NORMAL, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void __attribute__((constructor)) coroutine_init(void)
|
||||
{
|
||||
if (!g_thread_supported()) {
|
||||
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
||||
g_thread_init(NULL);
|
||||
#else
|
||||
fprintf(stderr, "glib threading failed to initialize.\n");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
init_coroutine_cond();
|
||||
coroutine_cond = g_cond_new();
|
||||
}
|
||||
|
||||
static void coroutine_wait_runnable_locked(CoroutineGThread *co)
|
||||
@@ -145,7 +60,7 @@ static gpointer coroutine_thread(gpointer opaque)
|
||||
{
|
||||
CoroutineGThread *co = opaque;
|
||||
|
||||
set_coroutine_key(co, false);
|
||||
g_static_private_set(&coroutine_key, co, NULL);
|
||||
coroutine_wait_runnable(co);
|
||||
co->base.entry(co->base.entry_arg);
|
||||
qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE);
|
||||
@@ -157,7 +72,8 @@ Coroutine *qemu_coroutine_new(void)
|
||||
CoroutineGThread *co;
|
||||
|
||||
co = g_malloc0(sizeof(*co));
|
||||
co->thread = create_thread(coroutine_thread, co);
|
||||
co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE,
|
||||
G_THREAD_PRIORITY_NORMAL, NULL);
|
||||
if (!co->thread) {
|
||||
g_free(co);
|
||||
return NULL;
|
||||
@@ -196,11 +112,12 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_,
|
||||
|
||||
Coroutine *qemu_coroutine_self(void)
|
||||
{
|
||||
CoroutineGThread *co = get_coroutine_key();
|
||||
CoroutineGThread *co = g_static_private_get(&coroutine_key);
|
||||
|
||||
if (!co) {
|
||||
co = g_malloc0(sizeof(*co));
|
||||
co->runnable = true;
|
||||
set_coroutine_key(co, true);
|
||||
g_static_private_set(&coroutine_key, co, (GDestroyNotify)g_free);
|
||||
}
|
||||
|
||||
return &co->base;
|
||||
@@ -208,7 +125,7 @@ Coroutine *qemu_coroutine_self(void)
|
||||
|
||||
bool qemu_in_coroutine(void)
|
||||
{
|
||||
CoroutineGThread *co = get_coroutine_key();
|
||||
CoroutineGThread *co = g_static_private_get(&coroutine_key);
|
||||
|
||||
return co && co->base.caller;
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* sigaltstack coroutine initialization code
|
||||
*
|
||||
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
|
||||
* Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com>
|
||||
* Copyright (C) 2012 Alex Barcelo <abarcelo@ac.upc.edu>
|
||||
** This file is partly based on pth_mctx.c, from the GNU Portable Threads
|
||||
** Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com>
|
||||
*
|
||||
* 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.1 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/>.
|
||||
*/
|
||||
|
||||
/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
|
||||
#ifdef _FORTIFY_SOURCE
|
||||
#undef _FORTIFY_SOURCE
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-coroutine-int.h"
|
||||
|
||||
enum {
|
||||
/* Maximum free pool size prevents holding too many freed coroutines */
|
||||
POOL_MAX_SIZE = 64,
|
||||
};
|
||||
|
||||
/** Free list to speed up creation */
|
||||
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
|
||||
static unsigned int pool_size;
|
||||
|
||||
typedef struct {
|
||||
Coroutine base;
|
||||
void *stack;
|
||||
jmp_buf env;
|
||||
} CoroutineUContext;
|
||||
|
||||
/**
|
||||
* Per-thread coroutine bookkeeping
|
||||
*/
|
||||
typedef struct {
|
||||
/** Currently executing coroutine */
|
||||
Coroutine *current;
|
||||
|
||||
/** The default coroutine */
|
||||
CoroutineUContext leader;
|
||||
|
||||
/** Information for the signal handler (trampoline) */
|
||||
jmp_buf tr_reenter;
|
||||
volatile sig_atomic_t tr_called;
|
||||
void *tr_handler;
|
||||
} CoroutineThreadState;
|
||||
|
||||
static pthread_key_t thread_state_key;
|
||||
|
||||
static CoroutineThreadState *coroutine_get_thread_state(void)
|
||||
{
|
||||
CoroutineThreadState *s = pthread_getspecific(thread_state_key);
|
||||
|
||||
if (!s) {
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->current = &s->leader.base;
|
||||
pthread_setspecific(thread_state_key, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static void qemu_coroutine_thread_cleanup(void *opaque)
|
||||
{
|
||||
CoroutineThreadState *s = opaque;
|
||||
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void __attribute__((destructor)) coroutine_cleanup(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
Coroutine *tmp;
|
||||
|
||||
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
|
||||
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
|
||||
g_free(co);
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) coroutine_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* "boot" function
|
||||
* This is what starts the coroutine, is called from the trampoline
|
||||
* (from the signal handler when it is not signal handling, read ahead
|
||||
* for more information).
|
||||
*/
|
||||
static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co)
|
||||
{
|
||||
/* Initialize longjmp environment and switch back the caller */
|
||||
if (!setjmp(self->env)) {
|
||||
longjmp(*(jmp_buf *)co->entry_arg, 1);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
co->entry(co->entry_arg);
|
||||
qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is used as the signal handler. This is called with the brand new stack
|
||||
* (thanks to sigaltstack). We have to return, given that this is a signal
|
||||
* handler and the sigmask and some other things are changed.
|
||||
*/
|
||||
static void coroutine_trampoline(int signal)
|
||||
{
|
||||
CoroutineUContext *self;
|
||||
Coroutine *co;
|
||||
CoroutineThreadState *coTS;
|
||||
|
||||
/* Get the thread specific information */
|
||||
coTS = coroutine_get_thread_state();
|
||||
self = coTS->tr_handler;
|
||||
coTS->tr_called = 1;
|
||||
co = &self->base;
|
||||
|
||||
/*
|
||||
* Here we have to do a bit of a ping pong between the caller, given that
|
||||
* this is a signal handler and we have to do a return "soon". Then the
|
||||
* caller can reestablish everything and do a longjmp here again.
|
||||
*/
|
||||
if (!setjmp(coTS->tr_reenter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, the caller has longjmp'ed back to us, so now prepare
|
||||
* us for the real machine state switching. We have to jump
|
||||
* into another function here to get a new stack context for
|
||||
* the auto variables (which have to be auto-variables
|
||||
* because the start of the thread happens later). Else with
|
||||
* PIC (i.e. Position Independent Code which is used when PTH
|
||||
* is built as a shared library) most platforms would
|
||||
* horrible core dump as experience showed.
|
||||
*/
|
||||
coroutine_bootstrap(self, co);
|
||||
}
|
||||
|
||||
static Coroutine *coroutine_new(void)
|
||||
{
|
||||
const size_t stack_size = 1 << 20;
|
||||
CoroutineUContext *co;
|
||||
CoroutineThreadState *coTS;
|
||||
struct sigaction sa;
|
||||
struct sigaction osa;
|
||||
struct sigaltstack ss;
|
||||
struct sigaltstack oss;
|
||||
sigset_t sigs;
|
||||
sigset_t osigs;
|
||||
jmp_buf old_env;
|
||||
|
||||
/* The way to manipulate stack is with the sigaltstack function. We
|
||||
* prepare a stack, with it delivering a signal to ourselves and then
|
||||
* put setjmp/longjmp where needed.
|
||||
* This has been done keeping coroutine-ucontext as a model and with the
|
||||
* pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics
|
||||
* of the coroutines and see pth_mctx.c (from the pth project) for the
|
||||
* sigaltstack way of manipulating stacks.
|
||||
*/
|
||||
|
||||
co = g_malloc0(sizeof(*co));
|
||||
co->stack = g_malloc(stack_size);
|
||||
co->base.entry_arg = &old_env; /* stash away our jmp_buf */
|
||||
|
||||
coTS = coroutine_get_thread_state();
|
||||
coTS->tr_handler = co;
|
||||
|
||||
/*
|
||||
* Preserve the SIGUSR2 signal state, block SIGUSR2,
|
||||
* and establish our signal handler. The signal will
|
||||
* later transfer control onto the signal stack.
|
||||
*/
|
||||
sigemptyset(&sigs);
|
||||
sigaddset(&sigs, SIGUSR2);
|
||||
pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
|
||||
sa.sa_handler = coroutine_trampoline;
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_ONSTACK;
|
||||
if (sigaction(SIGUSR2, &sa, &osa) != 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the new stack.
|
||||
*/
|
||||
ss.ss_sp = co->stack;
|
||||
ss.ss_size = stack_size;
|
||||
ss.ss_flags = 0;
|
||||
if (sigaltstack(&ss, &oss) < 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Now transfer control onto the signal stack and set it up.
|
||||
* It will return immediately via "return" after the setjmp()
|
||||
* was performed. Be careful here with race conditions. The
|
||||
* signal can be delivered the first time sigsuspend() is
|
||||
* called.
|
||||
*/
|
||||
coTS->tr_called = 0;
|
||||
pthread_kill(pthread_self(), SIGUSR2);
|
||||
sigfillset(&sigs);
|
||||
sigdelset(&sigs, SIGUSR2);
|
||||
while (!coTS->tr_called) {
|
||||
sigsuspend(&sigs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inform the system that we are back off the signal stack by
|
||||
* removing the alternative signal stack. Be careful here: It
|
||||
* first has to be disabled, before it can be removed.
|
||||
*/
|
||||
sigaltstack(NULL, &ss);
|
||||
ss.ss_flags = SS_DISABLE;
|
||||
if (sigaltstack(&ss, NULL) < 0) {
|
||||
abort();
|
||||
}
|
||||
sigaltstack(NULL, &ss);
|
||||
if (!(oss.ss_flags & SS_DISABLE)) {
|
||||
sigaltstack(&oss, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the old SIGUSR2 signal handler and mask
|
||||
*/
|
||||
sigaction(SIGUSR2, &osa, NULL);
|
||||
pthread_sigmask(SIG_SETMASK, &osigs, NULL);
|
||||
|
||||
/*
|
||||
* Now enter the trampoline again, but this time not as a signal
|
||||
* handler. Instead we jump into it directly. The functionally
|
||||
* redundant ping-pong pointer arithmetic is necessary to avoid
|
||||
* type-conversion warnings related to the `volatile' qualifier and
|
||||
* the fact that `jmp_buf' usually is an array type.
|
||||
*/
|
||||
if (!setjmp(old_env)) {
|
||||
longjmp(coTS->tr_reenter, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we returned again, so now we're finished
|
||||
*/
|
||||
|
||||
return &co->base;
|
||||
}
|
||||
|
||||
Coroutine *qemu_coroutine_new(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
|
||||
co = QSLIST_FIRST(&pool);
|
||||
if (co) {
|
||||
QSLIST_REMOVE_HEAD(&pool, pool_next);
|
||||
pool_size--;
|
||||
} else {
|
||||
co = coroutine_new();
|
||||
}
|
||||
return co;
|
||||
}
|
||||
|
||||
void qemu_coroutine_delete(Coroutine *co_)
|
||||
{
|
||||
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
|
||||
|
||||
if (pool_size < POOL_MAX_SIZE) {
|
||||
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
|
||||
co->base.caller = NULL;
|
||||
pool_size++;
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(co->stack);
|
||||
g_free(co);
|
||||
}
|
||||
|
||||
CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
|
||||
CoroutineAction action)
|
||||
{
|
||||
CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_);
|
||||
CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_);
|
||||
CoroutineThreadState *s = coroutine_get_thread_state();
|
||||
int ret;
|
||||
|
||||
s->current = to_;
|
||||
|
||||
ret = setjmp(from->env);
|
||||
if (ret == 0) {
|
||||
longjmp(to->env, action);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Coroutine *qemu_coroutine_self(void)
|
||||
{
|
||||
CoroutineThreadState *s = coroutine_get_thread_state();
|
||||
|
||||
return s->current;
|
||||
}
|
||||
|
||||
bool qemu_in_coroutine(void)
|
||||
{
|
||||
CoroutineThreadState *s = pthread_getspecific(thread_state_key);
|
||||
|
||||
return s && s->current->caller;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ enum {
|
||||
};
|
||||
|
||||
/** Free list to speed up creation */
|
||||
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
|
||||
static QLIST_HEAD(, Coroutine) pool = QLIST_HEAD_INITIALIZER(pool);
|
||||
static unsigned int pool_size;
|
||||
|
||||
typedef struct {
|
||||
@@ -92,7 +92,7 @@ static void __attribute__((destructor)) coroutine_cleanup(void)
|
||||
Coroutine *co;
|
||||
Coroutine *tmp;
|
||||
|
||||
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
|
||||
QLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
|
||||
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
|
||||
g_free(co);
|
||||
}
|
||||
@@ -175,9 +175,9 @@ Coroutine *qemu_coroutine_new(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
|
||||
co = QSLIST_FIRST(&pool);
|
||||
co = QLIST_FIRST(&pool);
|
||||
if (co) {
|
||||
QSLIST_REMOVE_HEAD(&pool, pool_next);
|
||||
QLIST_REMOVE(co, pool_next);
|
||||
pool_size--;
|
||||
} else {
|
||||
co = coroutine_new();
|
||||
@@ -190,7 +190,7 @@ void qemu_coroutine_delete(Coroutine *co_)
|
||||
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
|
||||
|
||||
if (pool_size < POOL_MAX_SIZE) {
|
||||
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
|
||||
QLIST_INSERT_HEAD(&pool, &co->base, pool_next);
|
||||
co->base.caller = NULL;
|
||||
pool_size++;
|
||||
return;
|
||||
|
||||
@@ -197,15 +197,14 @@ extern unsigned long reserved_va;
|
||||
#endif
|
||||
|
||||
/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
|
||||
#define g2h(x) ((void *)((unsigned long)(target_ulong)(x) + GUEST_BASE))
|
||||
#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
|
||||
|
||||
#if HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS
|
||||
#define h2g_valid(x) 1
|
||||
#else
|
||||
#define h2g_valid(x) ({ \
|
||||
unsigned long __guest = (unsigned long)(x) - GUEST_BASE; \
|
||||
(__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS)) && \
|
||||
(!RESERVED_VA || (__guest < RESERVED_VA)); \
|
||||
__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS); \
|
||||
})
|
||||
#endif
|
||||
|
||||
@@ -222,8 +221,8 @@ extern unsigned long reserved_va;
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
/* NOTE: we use double casts if pointers and target_ulong have
|
||||
different sizes */
|
||||
#define saddr(x) (uint8_t *)(intptr_t)(x)
|
||||
#define laddr(x) (uint8_t *)(intptr_t)(x)
|
||||
#define saddr(x) (uint8_t *)(long)(x)
|
||||
#define laddr(x) (uint8_t *)(long)(x)
|
||||
#endif
|
||||
|
||||
#define ldub_raw(p) ldub_p(laddr((p)))
|
||||
@@ -260,21 +259,12 @@ extern unsigned long reserved_va;
|
||||
#define stfl(p, v) stfl_raw(p, v)
|
||||
#define stfq(p, v) stfq_raw(p, v)
|
||||
|
||||
#ifndef CONFIG_TCG_PASS_AREG0
|
||||
#define ldub_code(p) ldub_raw(p)
|
||||
#define ldsb_code(p) ldsb_raw(p)
|
||||
#define lduw_code(p) lduw_raw(p)
|
||||
#define ldsw_code(p) ldsw_raw(p)
|
||||
#define ldl_code(p) ldl_raw(p)
|
||||
#define ldq_code(p) ldq_raw(p)
|
||||
#else
|
||||
#define cpu_ldub_code(env1, p) ldub_raw(p)
|
||||
#define cpu_ldsb_code(env1, p) ldsb_raw(p)
|
||||
#define cpu_lduw_code(env1, p) lduw_raw(p)
|
||||
#define cpu_ldsw_code(env1, p) ldsw_raw(p)
|
||||
#define cpu_ldl_code(env1, p) ldl_raw(p)
|
||||
#define cpu_ldq_code(env1, p) ldq_raw(p)
|
||||
#endif
|
||||
|
||||
#define ldub_kernel(p) ldub_raw(p)
|
||||
#define ldsb_kernel(p) ldsb_raw(p)
|
||||
@@ -299,10 +289,10 @@ extern unsigned long reserved_va;
|
||||
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
||||
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
||||
|
||||
/* ??? These should be the larger of uintptr_t and target_ulong. */
|
||||
extern uintptr_t qemu_real_host_page_size;
|
||||
extern uintptr_t qemu_host_page_size;
|
||||
extern uintptr_t qemu_host_page_mask;
|
||||
/* ??? These should be the larger of unsigned long and target_ulong. */
|
||||
extern unsigned long qemu_real_host_page_size;
|
||||
extern unsigned long qemu_host_page_size;
|
||||
extern unsigned long qemu_host_page_mask;
|
||||
|
||||
#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask)
|
||||
|
||||
@@ -332,21 +322,21 @@ void page_set_flags(target_ulong start, target_ulong end, int flags);
|
||||
int page_check_range(target_ulong start, target_ulong len, int flags);
|
||||
#endif
|
||||
|
||||
CPUArchState *cpu_copy(CPUArchState *env);
|
||||
CPUArchState *qemu_get_cpu(int cpu);
|
||||
CPUState *cpu_copy(CPUState *env);
|
||||
CPUState *qemu_get_cpu(int cpu);
|
||||
|
||||
#define CPU_DUMP_CODE 0x00010000
|
||||
|
||||
void cpu_dump_state(CPUArchState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
int flags);
|
||||
void cpu_dump_statistics(CPUArchState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
void cpu_dump_statistics(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
int flags);
|
||||
|
||||
void QEMU_NORETURN cpu_abort(CPUArchState *env, const char *fmt, ...)
|
||||
void QEMU_NORETURN cpu_abort(CPUState *env, const char *fmt, ...)
|
||||
GCC_FMT_ATTR(2, 3);
|
||||
extern CPUArchState *first_cpu;
|
||||
DECLARE_TLS(CPUArchState *,cpu_single_env);
|
||||
#define cpu_single_env tls_var(cpu_single_env)
|
||||
extern CPUState *first_cpu;
|
||||
DECLARE_TLS(CPUState *,cpu_single_env);
|
||||
#define cpu_single_env get_tls(cpu_single_env)
|
||||
|
||||
/* Flags for use in ENV->INTERRUPT_PENDING.
|
||||
|
||||
@@ -378,16 +368,15 @@ DECLARE_TLS(CPUArchState *,cpu_single_env);
|
||||
#define CPU_INTERRUPT_TGT_EXT_4 0x1000
|
||||
|
||||
/* Several target-specific internal interrupts. These differ from the
|
||||
preceding target-specific interrupts in that they are intended to
|
||||
preceeding target-specific interrupts in that they are intended to
|
||||
originate from within the cpu itself, typically in response to some
|
||||
instruction being executed. These, therefore, are not masked while
|
||||
single-stepping within the debugger. */
|
||||
#define CPU_INTERRUPT_TGT_INT_0 0x0100
|
||||
#define CPU_INTERRUPT_TGT_INT_1 0x0400
|
||||
#define CPU_INTERRUPT_TGT_INT_2 0x0800
|
||||
#define CPU_INTERRUPT_TGT_INT_3 0x2000
|
||||
|
||||
/* First unused bit: 0x4000. */
|
||||
/* First unused bit: 0x2000. */
|
||||
|
||||
/* The set of all bits that should be masked when single-stepping. */
|
||||
#define CPU_INTERRUPT_SSTEP_MASK \
|
||||
@@ -399,23 +388,23 @@ DECLARE_TLS(CPUArchState *,cpu_single_env);
|
||||
| CPU_INTERRUPT_TGT_EXT_4)
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
typedef void (*CPUInterruptHandler)(CPUArchState *, int);
|
||||
typedef void (*CPUInterruptHandler)(CPUState *, int);
|
||||
|
||||
extern CPUInterruptHandler cpu_interrupt_handler;
|
||||
|
||||
static inline void cpu_interrupt(CPUArchState *s, int mask)
|
||||
static inline void cpu_interrupt(CPUState *s, int mask)
|
||||
{
|
||||
cpu_interrupt_handler(s, mask);
|
||||
}
|
||||
#else /* USER_ONLY */
|
||||
void cpu_interrupt(CPUArchState *env, int mask);
|
||||
void cpu_interrupt(CPUState *env, int mask);
|
||||
#endif /* USER_ONLY */
|
||||
|
||||
void cpu_reset_interrupt(CPUArchState *env, int mask);
|
||||
void cpu_reset_interrupt(CPUState *env, int mask);
|
||||
|
||||
void cpu_exit(CPUArchState *s);
|
||||
void cpu_exit(CPUState *s);
|
||||
|
||||
bool qemu_cpu_has_work(CPUArchState *env);
|
||||
bool qemu_cpu_has_work(CPUState *env);
|
||||
|
||||
/* Breakpoint/watchpoint flags */
|
||||
#define BP_MEM_READ 0x01
|
||||
@@ -426,26 +415,26 @@ bool qemu_cpu_has_work(CPUArchState *env);
|
||||
#define BP_GDB 0x10
|
||||
#define BP_CPU 0x20
|
||||
|
||||
int cpu_breakpoint_insert(CPUArchState *env, target_ulong pc, int flags,
|
||||
int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags,
|
||||
CPUBreakpoint **breakpoint);
|
||||
int cpu_breakpoint_remove(CPUArchState *env, target_ulong pc, int flags);
|
||||
void cpu_breakpoint_remove_by_ref(CPUArchState *env, CPUBreakpoint *breakpoint);
|
||||
void cpu_breakpoint_remove_all(CPUArchState *env, int mask);
|
||||
int cpu_watchpoint_insert(CPUArchState *env, target_ulong addr, target_ulong len,
|
||||
int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags);
|
||||
void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint);
|
||||
void cpu_breakpoint_remove_all(CPUState *env, int mask);
|
||||
int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
|
||||
int flags, CPUWatchpoint **watchpoint);
|
||||
int cpu_watchpoint_remove(CPUArchState *env, target_ulong addr,
|
||||
int cpu_watchpoint_remove(CPUState *env, target_ulong addr,
|
||||
target_ulong len, int flags);
|
||||
void cpu_watchpoint_remove_by_ref(CPUArchState *env, CPUWatchpoint *watchpoint);
|
||||
void cpu_watchpoint_remove_all(CPUArchState *env, int mask);
|
||||
void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint);
|
||||
void cpu_watchpoint_remove_all(CPUState *env, int mask);
|
||||
|
||||
#define SSTEP_ENABLE 0x1 /* Enable simulated HW single stepping */
|
||||
#define SSTEP_NOIRQ 0x2 /* Do not use IRQ while single stepping */
|
||||
#define SSTEP_NOTIMER 0x4 /* Do not Timers while single stepping */
|
||||
|
||||
void cpu_single_step(CPUArchState *env, int enabled);
|
||||
void cpu_state_reset(CPUArchState *s);
|
||||
int cpu_is_stopped(CPUArchState *env);
|
||||
void run_on_cpu(CPUArchState *env, void (*func)(void *data), void *data);
|
||||
void cpu_single_step(CPUState *env, int enabled);
|
||||
void cpu_reset(CPUState *s);
|
||||
int cpu_is_stopped(CPUState *env);
|
||||
void run_on_cpu(CPUState *env, void (*func)(void *data), void *data);
|
||||
|
||||
#define CPU_LOG_TB_OUT_ASM (1 << 0)
|
||||
#define CPU_LOG_TB_IN_ASM (1 << 1)
|
||||
@@ -476,7 +465,7 @@ int cpu_str_to_log_mask(const char *str);
|
||||
/* Return the physical page corresponding to a virtual one. Use it
|
||||
only for debugging because no protection checks are done. Return -1
|
||||
if no page found. */
|
||||
target_phys_addr_t cpu_get_phys_page_debug(CPUArchState *env, target_ulong addr);
|
||||
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
|
||||
|
||||
/* memory API */
|
||||
|
||||
@@ -487,7 +476,6 @@ extern ram_addr_t ram_size;
|
||||
#define RAM_PREALLOC_MASK (1 << 0)
|
||||
|
||||
typedef struct RAMBlock {
|
||||
struct MemoryRegion *mr;
|
||||
uint8_t *host;
|
||||
ram_addr_t offset;
|
||||
ram_addr_t length;
|
||||
@@ -508,6 +496,14 @@ extern RAMList ram_list;
|
||||
extern const char *mem_path;
|
||||
extern int mem_prealloc;
|
||||
|
||||
/* physical memory access */
|
||||
|
||||
/* MMIO pages are identified by a combination of an IO device index and
|
||||
3 flags. The ROMD code stores the page ram offset in iotlb entry,
|
||||
so only a limited number of ids are avaiable. */
|
||||
|
||||
#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT))
|
||||
|
||||
/* Flags stored in the low bits of the TLB virtual address. These are
|
||||
defined so that fast path ram access is all zeros. */
|
||||
/* Zero if TLB entry is valid. */
|
||||
@@ -518,10 +514,74 @@ extern int mem_prealloc;
|
||||
/* Set if TLB entry is an IO callback. */
|
||||
#define TLB_MMIO (1 << 5)
|
||||
|
||||
#define VGA_DIRTY_FLAG 0x01
|
||||
#define CODE_DIRTY_FLAG 0x02
|
||||
#define MIGRATION_DIRTY_FLAG 0x08
|
||||
|
||||
/* read dirty bit (return 0 or 1) */
|
||||
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
|
||||
{
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
|
||||
{
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
|
||||
{
|
||||
ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
|
||||
int length,
|
||||
int dirty_flags)
|
||||
{
|
||||
int i, mask, len;
|
||||
uint8_t *p;
|
||||
|
||||
len = length >> TARGET_PAGE_BITS;
|
||||
mask = ~dirty_flags;
|
||||
p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS);
|
||||
for (i = 0; i < len; i++) {
|
||||
p[i] &= mask;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
|
||||
int dirty_flags);
|
||||
void cpu_tlb_update_dirty(CPUState *env);
|
||||
|
||||
int cpu_physical_memory_set_dirty_tracking(int enable);
|
||||
|
||||
int cpu_physical_memory_get_dirty_tracking(void);
|
||||
|
||||
int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr);
|
||||
|
||||
int cpu_physical_log_start(target_phys_addr_t start_addr,
|
||||
ram_addr_t size);
|
||||
|
||||
int cpu_physical_log_stop(target_phys_addr_t start_addr,
|
||||
ram_addr_t size);
|
||||
|
||||
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
||||
int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
|
||||
uint8_t *buf, int len, int is_write);
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
+75
-9
@@ -1,7 +1,7 @@
|
||||
#ifndef CPU_COMMON_H
|
||||
#define CPU_COMMON_H 1
|
||||
|
||||
/* CPU interfaces that are target independent. */
|
||||
/* CPU interfaces that are target indpendent. */
|
||||
|
||||
#ifdef TARGET_PHYS_ADDR_BITS
|
||||
#include "targphys.h"
|
||||
@@ -28,9 +28,9 @@ typedef uint64_t ram_addr_t;
|
||||
# define RAM_ADDR_MAX UINT64_MAX
|
||||
# define RAM_ADDR_FMT "%" PRIx64
|
||||
#else
|
||||
typedef uintptr_t ram_addr_t;
|
||||
# define RAM_ADDR_MAX UINTPTR_MAX
|
||||
# define RAM_ADDR_FMT "%" PRIxPTR
|
||||
typedef unsigned long ram_addr_t;
|
||||
# define RAM_ADDR_MAX ULONG_MAX
|
||||
# define RAM_ADDR_FMT "%lx"
|
||||
#endif
|
||||
|
||||
/* memory API */
|
||||
@@ -38,6 +38,34 @@ typedef uintptr_t ram_addr_t;
|
||||
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
|
||||
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
|
||||
|
||||
void cpu_register_physical_memory_log(target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
ram_addr_t region_offset,
|
||||
bool log_dirty);
|
||||
|
||||
static inline void cpu_register_physical_memory_offset(target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
ram_addr_t region_offset)
|
||||
{
|
||||
cpu_register_physical_memory_log(start_addr, size, phys_offset,
|
||||
region_offset, false);
|
||||
}
|
||||
|
||||
static inline void cpu_register_physical_memory(target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset)
|
||||
{
|
||||
cpu_register_physical_memory_offset(start_addr, size, phys_offset, 0);
|
||||
}
|
||||
|
||||
ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
|
||||
ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
|
||||
ram_addr_t size, void *host);
|
||||
ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size);
|
||||
void qemu_ram_free(ram_addr_t addr);
|
||||
void qemu_ram_free_from_ptr(ram_addr_t addr);
|
||||
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
|
||||
/* This should only be used for ram local to a device. */
|
||||
void *qemu_get_ram_ptr(ram_addr_t addr);
|
||||
@@ -49,7 +77,11 @@ void qemu_put_ram_ptr(void *addr);
|
||||
/* This should not be used by devices. */
|
||||
int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr);
|
||||
ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr);
|
||||
void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev);
|
||||
|
||||
int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
|
||||
CPUWriteMemoryFunc * const *mem_write,
|
||||
void *opaque, enum device_endian endian);
|
||||
void cpu_unregister_io_memory(int table_address);
|
||||
|
||||
void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
|
||||
int len, int is_write);
|
||||
@@ -71,11 +103,38 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
|
||||
void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
|
||||
void cpu_unregister_map_client(void *cookie);
|
||||
|
||||
struct CPUPhysMemoryClient;
|
||||
typedef struct CPUPhysMemoryClient CPUPhysMemoryClient;
|
||||
struct CPUPhysMemoryClient {
|
||||
void (*set_memory)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
bool log_dirty);
|
||||
int (*sync_dirty_bitmap)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr);
|
||||
int (*migration_log)(struct CPUPhysMemoryClient *client,
|
||||
int enable);
|
||||
int (*log_start)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t phys_addr, ram_addr_t size);
|
||||
int (*log_stop)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t phys_addr, ram_addr_t size);
|
||||
QLIST_ENTRY(CPUPhysMemoryClient) list;
|
||||
};
|
||||
|
||||
void cpu_register_phys_memory_client(CPUPhysMemoryClient *);
|
||||
void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *);
|
||||
|
||||
/* Coalesced MMIO regions are areas where write operations can be reordered.
|
||||
* This usually implies that write operations are side-effect free. This allows
|
||||
* batching which can make a major impact on performance when using
|
||||
* virtualization.
|
||||
*/
|
||||
void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
|
||||
|
||||
void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
|
||||
|
||||
void qemu_flush_coalesced_mmio_buffer(void);
|
||||
|
||||
uint32_t ldub_phys(target_phys_addr_t addr);
|
||||
@@ -107,10 +166,17 @@ void stq_phys(target_phys_addr_t addr, uint64_t val);
|
||||
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
|
||||
const uint8_t *buf, int len);
|
||||
|
||||
extern struct MemoryRegion io_mem_ram;
|
||||
extern struct MemoryRegion io_mem_rom;
|
||||
extern struct MemoryRegion io_mem_unassigned;
|
||||
extern struct MemoryRegion io_mem_notdirty;
|
||||
#define IO_MEM_SHIFT 3
|
||||
|
||||
#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */
|
||||
#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */
|
||||
#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT)
|
||||
#define IO_MEM_NOTDIRTY (3 << IO_MEM_SHIFT)
|
||||
#define IO_MEM_SUBPAGE_RAM (4 << IO_MEM_SHIFT)
|
||||
|
||||
/* Acts like a ROM when read and like a device when written. */
|
||||
#define IO_MEM_ROMD (1)
|
||||
#define IO_MEM_SUBPAGE (2)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+10
-17
@@ -60,6 +60,8 @@ typedef uint64_t target_ulong __attribute__((aligned(TARGET_LONG_ALIGNMENT)));
|
||||
#error TARGET_LONG_SIZE undefined
|
||||
#endif
|
||||
|
||||
#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
|
||||
|
||||
#define EXCP_INTERRUPT 0x10000 /* async interruption */
|
||||
#define EXCP_HLT 0x10001 /* hlt instruction reached */
|
||||
#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
|
||||
@@ -98,12 +100,12 @@ typedef struct CPUTLBEntry {
|
||||
target_ulong addr_code;
|
||||
/* Addend to virtual address to get host address. IO accesses
|
||||
use the corresponding iotlb value. */
|
||||
uintptr_t addend;
|
||||
unsigned long addend;
|
||||
/* padding to get a power of two size */
|
||||
uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) -
|
||||
(sizeof(target_ulong) * 3 +
|
||||
((-sizeof(target_ulong) * 3) & (sizeof(uintptr_t) - 1)) +
|
||||
sizeof(uintptr_t))];
|
||||
uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) -
|
||||
(sizeof(target_ulong) * 3 +
|
||||
((-sizeof(target_ulong) * 3) & (sizeof(unsigned long) - 1)) +
|
||||
sizeof(unsigned long))];
|
||||
} CPUTLBEntry;
|
||||
|
||||
extern int CPUTLBEntry_wrong_size[sizeof(CPUTLBEntry) == (1 << CPU_TLB_ENTRY_BITS) ? 1 : -1];
|
||||
@@ -151,14 +153,6 @@ typedef struct CPUWatchpoint {
|
||||
QTAILQ_ENTRY(CPUWatchpoint) entry;
|
||||
} CPUWatchpoint;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define CPU_COMMON_THREAD \
|
||||
void *hThread;
|
||||
|
||||
#else
|
||||
#define CPU_COMMON_THREAD
|
||||
#endif
|
||||
|
||||
#define CPU_TEMP_BUF_NLONGS 128
|
||||
#define CPU_COMMON \
|
||||
struct TranslationBlock *current_tb; /* currently executing TB */ \
|
||||
@@ -166,8 +160,8 @@ typedef struct CPUWatchpoint {
|
||||
/* in order to avoid passing too many arguments to the MMIO \
|
||||
helpers, we store some rarely used information in the CPU \
|
||||
context) */ \
|
||||
uintptr_t mem_io_pc; /* host pc at which the memory was \
|
||||
accessed */ \
|
||||
unsigned long mem_io_pc; /* host pc at which the memory was \
|
||||
accessed */ \
|
||||
target_ulong mem_io_vaddr; /* target virtual addr at which the \
|
||||
memory was accessed */ \
|
||||
uint32_t halted; /* Nonzero if the CPU is in suspend state */ \
|
||||
@@ -202,7 +196,7 @@ typedef struct CPUWatchpoint {
|
||||
jmp_buf jmp_env; \
|
||||
int exception_index; \
|
||||
\
|
||||
CPUArchState *next_cpu; /* next CPU sharing TB cache */ \
|
||||
CPUState *next_cpu; /* next CPU sharing TB cache */ \
|
||||
int cpu_index; /* CPU index (informative) */ \
|
||||
uint32_t host_tid; /* host thread ID */ \
|
||||
int numa_node; /* NUMA node this cpu is belonging to */ \
|
||||
@@ -217,7 +211,6 @@ typedef struct CPUWatchpoint {
|
||||
uint32_t stop; /* Stop request */ \
|
||||
uint32_t stopped; /* Artificially stopped */ \
|
||||
struct QemuThread *thread; \
|
||||
CPU_COMMON_THREAD \
|
||||
struct QemuCond *halt_cond; \
|
||||
int thread_kicked; \
|
||||
struct qemu_work_item *queued_work_first, *queued_work_last; \
|
||||
|
||||
Loaded 100 of 1486 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user