Compare commits
109
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ce5bc2dd1 | ||
|
|
113f4cd9e9 | ||
|
|
f965d237b5 | ||
|
|
5a16dd9bc8 | ||
|
|
c7580c1034 | ||
|
|
a8cd6f7ddf | ||
|
|
ce4fc986e5 | ||
|
|
219a7482ab | ||
|
|
28846ad3b5 | ||
|
|
cccb5446a6 | ||
|
|
25c0807e3c | ||
|
|
57fa9fb4ef | ||
|
|
450ead742a | ||
|
|
69c67eca97 | ||
|
|
256c899432 | ||
|
|
849c865155 | ||
|
|
57708c532f | ||
|
|
8d45ae8352 | ||
|
|
2f0f684cce | ||
|
|
bc4321e754 | ||
|
|
9a32fb2824 | ||
|
|
50462f2ca8 | ||
|
|
ec16f35e4e | ||
|
|
0ba3d50242 | ||
|
|
0214b6b4de | ||
|
|
63f7166a80 | ||
|
|
1d34dff02f | ||
|
|
5330a894ed | ||
|
|
cdcf2aa41c | ||
|
|
5ea33884f6 | ||
|
|
50d0184cb7 | ||
|
|
c068d37020 | ||
|
|
cc5caf7df4 | ||
|
|
b68e45c686 | ||
|
|
df60f451b3 | ||
|
|
1bc6332461 | ||
|
|
07548727b3 | ||
|
|
c7e6d6b115 | ||
|
|
886c62a3ff | ||
|
|
b598cb2214 | ||
|
|
799c27e124 | ||
|
|
b90d717b64 | ||
|
|
a410be59b5 | ||
|
|
f946f010f5 | ||
|
|
d438650fa5 | ||
|
|
07ab4fc1ef | ||
|
|
fd21cc14f5 | ||
|
|
cfeb9958c8 | ||
|
|
ffc7565c81 | ||
|
|
393d4c9214 | ||
|
|
5ae1c7ca0a | ||
|
|
c094d3d818 | ||
|
|
a473439574 | ||
|
|
2a93a89400 | ||
|
|
c337db8cc9 | ||
|
|
44a42fd2b6 | ||
|
|
1a933e4801 | ||
|
|
55e64b6ddd | ||
|
|
b83883e2db | ||
|
|
9c7e3b98aa | ||
|
|
8a869baa08 | ||
|
|
dbeb6c22d7 | ||
|
|
b00201d402 | ||
|
|
a1a17b1d5a | ||
|
|
f49853a4bd | ||
|
|
b10daa61f9 | ||
|
|
34b41ed15d | ||
|
|
3b38972743 | ||
|
|
36ed337845 | ||
|
|
5a1800cf1c | ||
|
|
97ac3b1af5 | ||
|
|
08e642c00e | ||
|
|
7940c766d4 | ||
|
|
f408f49165 | ||
|
|
785adb09b9 | ||
|
|
f52d0d639e | ||
|
|
4082e889ee | ||
|
|
b7093f294c | ||
|
|
cd63a77e99 | ||
|
|
8456852657 | ||
|
|
7d440f20bd | ||
|
|
feba8ae20b | ||
|
|
c9c2479289 | ||
|
|
4c45bf61d3 | ||
|
|
70d582074f | ||
|
|
0da4c07322 | ||
|
|
ee7735fa63 | ||
|
|
02fe741375 | ||
|
|
dbe4ac16bb | ||
|
|
f63e60327b | ||
|
|
0ec3907571 | ||
|
|
1658e3cd89 | ||
|
|
065436479b | ||
|
|
f6db26e4f8 | ||
|
|
c49dd1bf64 | ||
|
|
b4fcb4b499 | ||
|
|
7672b714b2 | ||
|
|
ca09717e8e | ||
|
|
0cc21de484 | ||
|
|
08375616a0 | ||
|
|
b993b863e7 | ||
|
|
07ff37597b | ||
|
|
e77326d99c | ||
|
|
8b3ac66120 | ||
|
|
2eb4d314ce | ||
|
|
adda59173c | ||
|
|
b696aeab6a | ||
|
|
6514fe5047 | ||
|
|
c63c453889 |
No files matched your search
@@ -1,7 +0,0 @@
|
||||
"VIM settings to match QEMU coding style. They are activated by adding the
|
||||
"following settings (without the " symbol) as last two lines in $HOME/.vimrc:
|
||||
"set secure
|
||||
"set exrc
|
||||
set expandtab
|
||||
set shiftwidth=4
|
||||
set smarttab
|
||||
+2
-6
@@ -12,6 +12,8 @@ trace-dtrace.dtrace
|
||||
*-linux-user
|
||||
*-bsd-user
|
||||
libdis*
|
||||
libhw32
|
||||
libhw64
|
||||
libuser
|
||||
linux-headers/asm
|
||||
qapi-generated
|
||||
@@ -39,14 +41,12 @@ qemu-io
|
||||
qemu-ga
|
||||
qemu-bridge-helper
|
||||
qemu-monitor.texi
|
||||
vscclient
|
||||
QMP/qmp-commands.txt
|
||||
test-coroutine
|
||||
test-qmp-input-visitor
|
||||
test-qmp-output-visitor
|
||||
test-string-input-visitor
|
||||
test-string-output-visitor
|
||||
test-visitor-serialization
|
||||
fsdev/virtfs-proxy-helper.1
|
||||
fsdev/virtfs-proxy-helper.pod
|
||||
.gdbinit
|
||||
@@ -69,10 +69,6 @@ fsdev/virtfs-proxy-helper.pod
|
||||
*.vr
|
||||
*.d
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.pc
|
||||
.libs
|
||||
*.swp
|
||||
*.orig
|
||||
.pc
|
||||
|
||||
@@ -19,6 +19,3 @@
|
||||
[submodule "roms/sgabios"]
|
||||
path = roms/sgabios
|
||||
url = git://git.qemu.org/sgabios.git
|
||||
[submodule "pixman"]
|
||||
path = pixman
|
||||
url = git://anongit.freedesktop.org/pixman
|
||||
@@ -32,7 +32,7 @@ mandatory for VMState fields.
|
||||
|
||||
Don't use Linux kernel internal types like u32, __u32 or __le32.
|
||||
|
||||
Use hwaddr for guest physical addresses except pcibus_t
|
||||
Use target_phys_addr_t for guest physical addresses except pcibus_t
|
||||
for PCI addresses. In addition, ram_addr_t is a QEMU internal address
|
||||
space that maps guest RAM physical addresses into an intermediate
|
||||
address space that can map to host virtual address spaces. Generally
|
||||
@@ -91,11 +91,10 @@ emulators.
|
||||
|
||||
4. String manipulation
|
||||
|
||||
Do not use the strncpy function. As mentioned in the man page, it does *not*
|
||||
guarantee a NULL-terminated buffer, which makes it extremely dangerous to use.
|
||||
It also zeros trailing destination bytes out to the specified length. Instead,
|
||||
use this similar function when possible, but note its different signature:
|
||||
void pstrcpy(char *dest, int dest_buf_size, const char *src)
|
||||
Do not use the strncpy function. According to the man page, it does
|
||||
*not* guarantee a NULL-terminated buffer, which makes it extremely dangerous
|
||||
to use. Instead, use functionally equivalent function:
|
||||
void pstrcpy(char *buf, int buf_size, const char *str)
|
||||
|
||||
Don't use strcat because it can't check for buffer overflows, but:
|
||||
char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
|
||||
+3
-115
@@ -207,12 +207,6 @@ M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/gumstix.c
|
||||
|
||||
i.MX31
|
||||
M: Peter Chubb <peter.chubb@nicta.com.au>
|
||||
S: Odd fixes
|
||||
F: hw/imx*
|
||||
F: hw/kzm.c
|
||||
|
||||
Integrator CP
|
||||
M: Paul Brook <paul@codesourcery.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -268,7 +262,6 @@ S: Maintained
|
||||
F: hw/xilinx_zynq.c
|
||||
F: hw/zynq_slcr.c
|
||||
F: hw/cadence_*
|
||||
F: hw/xilinx_spips.c
|
||||
|
||||
CRIS Machines
|
||||
-------------
|
||||
@@ -318,11 +311,6 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/petalogix_s3adsp1800.c
|
||||
|
||||
petalogix_ml605
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
S: Maintained
|
||||
F: hw/petalogix_ml605_mmu.c
|
||||
|
||||
MIPS Machines
|
||||
-------------
|
||||
Jazz
|
||||
@@ -350,31 +338,9 @@ PowerPC Machines
|
||||
405
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: hw/ppc405_boards.c
|
||||
|
||||
Bamboo
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc440_bamboo.c
|
||||
|
||||
e500
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
M: Scott Wood <scottwood@freescale.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Supported
|
||||
F: hw/ppc/e500.[hc]
|
||||
F: hw/ppc/e500plat.c
|
||||
|
||||
mpc8544ds
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
M: Scott Wood <scottwood@freescale.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Supported
|
||||
F: hw/ppc/mpc8544ds.c
|
||||
F: hw/mpc8544_guts.c
|
||||
|
||||
New World
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
@@ -397,19 +363,6 @@ S: Odd Fixes
|
||||
F: hw/ppc_prep.c
|
||||
F: hw/prep_pci.[hc]
|
||||
|
||||
sPAPR
|
||||
M: David Gibson <david@gibson.dropbear.id.au>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Supported
|
||||
F: hw/spapr*
|
||||
|
||||
virtex_ml507
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/virtex_ml507.c
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
R2D
|
||||
@@ -434,12 +387,6 @@ M: Blue Swirl <blauwirbel@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/sun4u.c
|
||||
|
||||
Leon3
|
||||
M: Fabien Chouteau <chouteau@adacore.com>
|
||||
S: Maintained
|
||||
F: hw/leon3.c
|
||||
F: hw/grlib*
|
||||
|
||||
S390 Machines
|
||||
-------------
|
||||
S390 Virtio
|
||||
@@ -447,14 +394,6 @@ M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: hw/s390-*.c
|
||||
|
||||
UniCore32 Machines
|
||||
-------------
|
||||
PKUnity-3 SoC initramfs-with-busybox
|
||||
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
|
||||
S: Maintained
|
||||
F: hw/puv3*
|
||||
F: hw/unicore32/
|
||||
|
||||
X86 Machines
|
||||
------------
|
||||
PC
|
||||
@@ -493,19 +432,6 @@ S: Supported
|
||||
F: hw/pci*
|
||||
F: hw/piix*
|
||||
|
||||
ppc4xx
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc4xx*.[hc]
|
||||
|
||||
ppce500
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
M: Scott Wood <scottwood@freescale.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Supported
|
||||
F: hw/ppce500_*
|
||||
|
||||
SCSI
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Supported
|
||||
@@ -518,22 +444,11 @@ M: Paul Brook <paul@codesourcery.com>
|
||||
S: Odd Fixes
|
||||
F: hw/lsi53c895a.c
|
||||
|
||||
SSI
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
S: Maintained
|
||||
F: hw/ssi.*
|
||||
F: hw/m25p80.c
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb*
|
||||
|
||||
VFIO
|
||||
M: Alex Williamson <alex.williamson@redhat.com>
|
||||
S: Supported
|
||||
F: hw/vfio*
|
||||
|
||||
vhost
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
@@ -553,7 +468,6 @@ T: git git://github.com/kvaneesh/QEMU.git
|
||||
|
||||
virtio-blk
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Supported
|
||||
F: hw/virtio-blk*
|
||||
|
||||
@@ -563,18 +477,6 @@ S: Supported
|
||||
F: hw/virtio-serial*
|
||||
F: hw/virtio-console*
|
||||
|
||||
Xilinx EDK
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/xilinx_axi*
|
||||
F: hw/xilinx_uartlite.c
|
||||
F: hw/xilinx_intc.c
|
||||
F: hw/xilinx_ethlite.c
|
||||
F: hw/xilinx_timer.c
|
||||
F: hw/xilinx.h
|
||||
F: hw/xilinx_spi.c
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
@@ -584,7 +486,6 @@ F: audio/
|
||||
|
||||
Block
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Supported
|
||||
F: block*
|
||||
F: block/
|
||||
@@ -594,18 +495,6 @@ M: Anthony Liguori <aliguori@us.ibm.com>
|
||||
S: Maintained
|
||||
F: qemu-char.c
|
||||
|
||||
CPU
|
||||
M: Andreas Färber <afaerber@suse.de>
|
||||
S: Supported
|
||||
F: qom/cpu.c
|
||||
F: include/qemu/cpu.h
|
||||
|
||||
Device Tree
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: device-tree.[ch]
|
||||
|
||||
GDB stub
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Odd Fixes
|
||||
@@ -643,10 +532,9 @@ F: monitor.c
|
||||
|
||||
Network device layer
|
||||
M: Anthony Liguori <aliguori@us.ibm.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Mark McLoughlin <markmc@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
T: git git://github.com/stefanha/qemu.git net
|
||||
|
||||
Network Block Device (NBD)
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
@@ -663,7 +551,7 @@ F: slirp/
|
||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Tracing
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
F: trace/
|
||||
F: scripts/tracetool.py
|
||||
|
||||
@@ -6,30 +6,17 @@ BUILD_DIR=$(CURDIR)
|
||||
# All following code might depend on configuration variables
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
# Put the all: rule here so that config-host.mak can contain dependencies.
|
||||
all:
|
||||
all: build-all
|
||||
include config-host.mak
|
||||
|
||||
# Check that we're not trying to do an out-of-tree build from
|
||||
# a tree that's been used for an in-tree build.
|
||||
ifneq ($(realpath $(SRC_PATH)),$(realpath .))
|
||||
ifneq ($(wildcard $(SRC_PATH)/config-host.mak),)
|
||||
$(error This is an out of tree build but your source tree ($(SRC_PATH)) \
|
||||
seems to have been used for an in-tree build. You can fix this by running \
|
||||
"make distclean && rm -rf *-linux-user *-softmmu" in your source tree)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(SRC_PATH)/rules.mak
|
||||
config-host.mak: $(SRC_PATH)/configure
|
||||
@echo $@ is out-of-date, running configure
|
||||
@sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh
|
||||
else
|
||||
config-host.mak:
|
||||
ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||
@echo "Please call configure before running make!"
|
||||
@exit 1
|
||||
endif
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
@@ -44,9 +31,9 @@ Makefile: ;
|
||||
configure: ;
|
||||
|
||||
.PHONY: all clean cscope distclean dvi html info install install-doc \
|
||||
pdf recurse-all speed test dist
|
||||
pdf recurse-all speed tar tarbin test build-all
|
||||
|
||||
$(call set-vpath, $(SRC_PATH))
|
||||
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
|
||||
|
||||
LIBS+=-lz $(LIBS_TOOLS)
|
||||
|
||||
@@ -65,13 +52,8 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) BUILD_DIR=$(BUILD_DIR)
|
||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
|
||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS))
|
||||
|
||||
ifeq ($(SUBDIR_DEVICES_MAK),)
|
||||
config-all-devices.mak:
|
||||
$(call quiet-command,echo '# no devices' > $@," GEN $@")
|
||||
else
|
||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
|
||||
$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@")
|
||||
endif
|
||||
|
||||
-include $(SUBDIR_DEVICES_MAK_DEP)
|
||||
|
||||
@@ -100,7 +82,7 @@ defconfig:
|
||||
|
||||
-include config-all-devices.mak
|
||||
|
||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
|
||||
build-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
|
||||
|
||||
config-host.h: config-host.h-timestamp
|
||||
config-host.h-timestamp: config-host.mak
|
||||
@@ -109,29 +91,19 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx
|
||||
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
|
||||
subdir-%:
|
||||
subdir-%: $(GENERATED_HEADERS)
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
endif
|
||||
|
||||
$(universal-obj-y) $(common-obj-y): $(GENERATED_HEADERS)
|
||||
subdir-libcacard: $(oslib-obj-y) $(trace-obj-y) qemu-timer-common.o
|
||||
|
||||
subdir-pixman: pixman/Makefile
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,)
|
||||
$(filter %-softmmu,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(common-obj-y) subdir-libdis
|
||||
|
||||
pixman/Makefile: $(SRC_PATH)/pixman/configure
|
||||
(cd pixman; CFLAGS="$(CFLAGS) -fPIC" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static)
|
||||
|
||||
$(SRC_PATH)/pixman/configure:
|
||||
(cd $(SRC_PATH)/pixman; autoreconf -v --install)
|
||||
|
||||
$(SUBDIR_RULES): libqemustub.a
|
||||
|
||||
$(filter %-softmmu,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(common-obj-y) $(extra-obj-y) subdir-libdis
|
||||
|
||||
$(filter %-user,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) subdir-libdis-user subdir-libuser
|
||||
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(universal-obj-y) $(trace-obj-y) subdir-libdis-user subdir-libuser
|
||||
|
||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
romsubdir-%:
|
||||
@@ -149,7 +121,7 @@ QEMU_CFLAGS += -I$(SRC_PATH)/include
|
||||
|
||||
ui/cocoa.o: ui/cocoa.m
|
||||
|
||||
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o hw/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
|
||||
ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
|
||||
|
||||
@@ -159,12 +131,6 @@ version.o: $(SRC_PATH)/version.rc config-host.h
|
||||
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
|
||||
|
||||
version-obj-$(CONFIG_WIN32) += version.o
|
||||
|
||||
######################################################################
|
||||
# Build library with stubs
|
||||
|
||||
libqemustub.a: $(stub-obj-y)
|
||||
|
||||
######################################################################
|
||||
# Support building shared library libcacard
|
||||
|
||||
@@ -176,29 +142,27 @@ libcacard.la:
|
||||
install-libcacard:
|
||||
@echo "libtool is missing, please install and rerun configure"; exit 1
|
||||
else
|
||||
libcacard.la: $(oslib-obj-y) qemu-timer-common.o $(addsuffix .lo, $(basename $(trace-obj-y)))
|
||||
libcacard.la: $(GENERATED_HEADERS) $(oslib-obj-y) qemu-timer-common.o $(addsuffix .lo, $(basename $(trace-obj-y)))
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" libcacard.la,)
|
||||
|
||||
install-libcacard: libcacard.la
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" install-libcacard,)
|
||||
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 \
|
||||
main-loop.o iohandler.o error.o
|
||||
qemu-timer-common.o main-loop.o notify.o iohandler.o cutils.o async.o
|
||||
tools-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
|
||||
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
|
||||
|
||||
vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) libcacard/vscclient.o libqemustub.a
|
||||
$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $^ $(libcacard_libs) $(LIBS)," LINK $@")
|
||||
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
|
||||
@@ -206,8 +170,10 @@ 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 $@")
|
||||
|
||||
$(qapi-obj-y): $(GENERATED_HEADERS)
|
||||
qapi-dir := $(BUILD_DIR)/qapi-generated
|
||||
qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
|
||||
qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
|
||||
qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
|
||||
|
||||
gen-out-type = $(subst .,-,$(suffix $@))
|
||||
|
||||
@@ -215,63 +181,57 @@ ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/tests/Makefile
|
||||
endif
|
||||
|
||||
qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
|
||||
|
||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
|
||||
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@")
|
||||
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\
|
||||
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@")
|
||||
qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
|
||||
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " 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 $@")
|
||||
$(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 $@")
|
||||
$(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 $@")
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
$(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 $@")
|
||||
qapi-visit.c qapi-visit.h :\
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
|
||||
$(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 $@")
|
||||
qmp-commands.h qmp-marshal.c :\
|
||||
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
$(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 $@")
|
||||
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
|
||||
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
|
||||
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) $(oslib-obj-y) $(trace-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) libqemustub.a
|
||||
qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(tools-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) $(QGALIB_OBJ)
|
||||
|
||||
QEMULIBS=libuser libdis libdis-user
|
||||
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
|
||||
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
find . -name '*.[od]' -exec rm -f {} +
|
||||
rm -f *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -f *.o *.d *.a *.lo $(TOOLS) $(HELPERS-y) 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-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 -rf qapi-generated
|
||||
rm -rf qga/qapi-generated
|
||||
rm -rf $(qapi-dir)
|
||||
$(MAKE) -C tests/tcg 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; \
|
||||
done
|
||||
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
|
||||
dist: qemu-$(VERSION).tar.bz2
|
||||
|
||||
qemu-%.tar.bz2:
|
||||
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
|
||||
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
|
||||
rm -f config-all-devices.mak
|
||||
@@ -286,12 +246,10 @@ distclean: clean
|
||||
for d in $(TARGET_DIRS) $(QEMULIBS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
if test -f pixman/config.log; then make -C pixman distclean; fi
|
||||
|
||||
KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
|
||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr \
|
||||
bepo
|
||||
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
|
||||
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
@@ -301,6 +259,7 @@ 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 \
|
||||
s390-zipl.rom \
|
||||
spapr-rtas.bin slof.bin \
|
||||
@@ -332,6 +291,7 @@ install-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_DIR) "$(DESTDIR)$(bindir)"
|
||||
@@ -430,12 +390,15 @@ qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi \
|
||||
qemu-monitor.texi qemu-img-cmds.texi
|
||||
|
||||
# Add a dependency on the generated files, so that they are always
|
||||
# rebuilt before other object files
|
||||
ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||
Makefile: $(GENERATED_HEADERS)
|
||||
endif
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
FILE = qemu-$(VERSION)
|
||||
|
||||
# tar release (use 'make -k tar' on a checkouted tree)
|
||||
tar:
|
||||
rm -rf /tmp/$(FILE)
|
||||
cp -r . /tmp/$(FILE)
|
||||
cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn
|
||||
rm -rf /tmp/$(FILE)
|
||||
|
||||
# Include automatically generated dependency files
|
||||
# Dependencies in Makefile.objs files come from our recursive subdir rules
|
||||
-include $(wildcard *.d tests/*.d)
|
||||
-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
|
||||
@@ -18,3 +18,6 @@ all: $(libdis-y)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.a *~
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Makefile for qemu target independent devices.
|
||||
|
||||
include ../config-host.mak
|
||||
include ../config-all-devices.mak
|
||||
include config.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
|
||||
.PHONY: all
|
||||
|
||||
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
|
||||
|
||||
QEMU_CFLAGS+=-I..
|
||||
QEMU_CFLAGS += -I$(SRC_PATH)/include
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
all: $(hw-obj-y)
|
||||
# Dummy command so that make thinks it has done something
|
||||
@true
|
||||
|
||||
clean:
|
||||
rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
+291
-70
@@ -1,11 +1,6 @@
|
||||
#######################################################################
|
||||
# Stub library, linked in tools
|
||||
stub-obj-y = stubs/
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
universal-obj-y =
|
||||
universal-obj-y += qemu-log.o
|
||||
|
||||
#######################################################################
|
||||
# QObject
|
||||
@@ -17,13 +12,15 @@ universal-obj-y += $(qobject-obj-y)
|
||||
|
||||
#######################################################################
|
||||
# QOM
|
||||
qom-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 cutils.o qemu-timer-common.o
|
||||
oslib-obj-y = osdep.o
|
||||
oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
|
||||
oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
|
||||
|
||||
@@ -45,72 +42,155 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y = iov.o cache-utils.o qemu-option.o module.o async.o
|
||||
block-obj-y += nbd.o block.o blockjob.o aes.o qemu-config.o
|
||||
block-obj-y += thread-pool.o qemu-progress.o qemu-sockets.o uri.o notify.o
|
||||
block-obj-y = cutils.o cache-utils.o qemu-option.o module.o async.o
|
||||
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o
|
||||
block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y)
|
||||
block-obj-$(CONFIG_POSIX) += event_notifier-posix.o aio-posix.o
|
||||
block-obj-$(CONFIG_WIN32) += event_notifier-win32.o aio-win32.o
|
||||
block-obj-y += block/
|
||||
block-obj-y += $(qapi-obj-y) qapi-types.o qapi-visit.o
|
||||
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
|
||||
block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||
block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
||||
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
|
||||
block-nested-$(CONFIG_CURL) += curl.o
|
||||
block-nested-$(CONFIG_RBD) += rbd.o
|
||||
|
||||
block-obj-y += $(addprefix block/, $(block-nested-y))
|
||||
|
||||
net-obj-y = net.o
|
||||
net-nested-y = queue.o checksum.o util.o
|
||||
net-nested-y += socket.o
|
||||
net-nested-y += dump.o
|
||||
net-nested-$(CONFIG_POSIX) += tap.o
|
||||
net-nested-$(CONFIG_LINUX) += tap-linux.o
|
||||
net-nested-$(CONFIG_WIN32) += tap-win32.o
|
||||
net-nested-$(CONFIG_BSD) += tap-bsd.o
|
||||
net-nested-$(CONFIG_SOLARIS) += tap-solaris.o
|
||||
net-nested-$(CONFIG_AIX) += tap-aix.o
|
||||
net-nested-$(CONFIG_HAIKU) += tap-haiku.o
|
||||
net-nested-$(CONFIG_SLIRP) += slirp.o
|
||||
net-nested-$(CONFIG_VDE) += vde.o
|
||||
net-obj-y += $(addprefix net/, $(net-nested-y))
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += net.o net/
|
||||
common-obj-y += qom/
|
||||
common-obj-y = $(block-obj-y) blockdev.o
|
||||
common-obj-y += $(net-obj-y)
|
||||
common-obj-y += $(qom-obj-twice-y)
|
||||
common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
|
||||
common-obj-y += readline.o console.o cursor.o
|
||||
common-obj-y += qemu-pixman.o
|
||||
common-obj-y += $(oslib-obj-y)
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
extra-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += tcg-runtime.o host-utils.o main-loop.o
|
||||
common-obj-y += input.o
|
||||
common-obj-y += irq.o input.o
|
||||
common-obj-$(CONFIG_PTIMER) += ptimer.o
|
||||
common-obj-$(CONFIG_MAX7310) += max7310.o
|
||||
common-obj-$(CONFIG_WM8750) += wm8750.o
|
||||
common-obj-$(CONFIG_TWL92230) += twl92230.o
|
||||
common-obj-$(CONFIG_TSC2005) += tsc2005.o
|
||||
common-obj-$(CONFIG_LM832X) += lm832x.o
|
||||
common-obj-$(CONFIG_TMP105) += tmp105.o
|
||||
common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
|
||||
common-obj-$(CONFIG_SSD0303) += ssd0303.o
|
||||
common-obj-$(CONFIG_SSD0323) += ssd0323.o
|
||||
common-obj-$(CONFIG_ADS7846) += ads7846.o
|
||||
common-obj-$(CONFIG_MAX111X) += max111x.o
|
||||
common-obj-$(CONFIG_DS1338) += ds1338.o
|
||||
common-obj-y += i2c.o smbus.o smbus_eeprom.o
|
||||
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-$(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 += buffered_file.o migration.o migration-tcp.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += msmouse.o ps2.o
|
||||
common-obj-y += qdev.o qdev-properties.o qdev-monitor.o
|
||||
common-obj-y += block-migration.o iohandler.o
|
||||
common-obj-y += pflib.o
|
||||
common-obj-y += bitmap.o bitops.o
|
||||
common-obj-y += page_cache.o
|
||||
|
||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
||||
common-obj-$(CONFIG_WIN32) += version.o
|
||||
|
||||
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
||||
common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o
|
||||
|
||||
common-obj-y += audio/
|
||||
common-obj-y += hw/
|
||||
common-obj-y += ui/
|
||||
common-obj-y += bt-host.o bt-vhci.o
|
||||
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
audio-obj-$(CONFIG_SDL) += sdlaudio.o
|
||||
audio-obj-$(CONFIG_OSS) += ossaudio.o
|
||||
audio-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
|
||||
audio-obj-$(CONFIG_ALSA) += alsaaudio.o
|
||||
audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
|
||||
audio-obj-$(CONFIG_FMOD) += fmodaudio.o
|
||||
audio-obj-$(CONFIG_ESD) += esdaudio.o
|
||||
audio-obj-$(CONFIG_PA) += paaudio.o
|
||||
audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
|
||||
audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
|
||||
audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
|
||||
audio-obj-y += wavcapture.o
|
||||
common-obj-y += $(addprefix audio/, $(audio-obj-y))
|
||||
|
||||
common-obj-y += dma-helpers.o
|
||||
common-obj-y += acl.o
|
||||
common-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
common-obj-y += qemu-timer.o qemu-timer-common.o
|
||||
common-obj-y += qtest.o
|
||||
common-obj-y += vl.o
|
||||
|
||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
||||
|
||||
common-obj-y += backends/
|
||||
|
||||
######################################################################
|
||||
# libseccomp
|
||||
ifeq ($(CONFIG_SECCOMP),y)
|
||||
common-obj-y += qemu-seccomp.o
|
||||
ui-obj-y += keymaps.o
|
||||
ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
|
||||
ui-obj-$(CONFIG_COCOA) += cocoa.o
|
||||
ui-obj-$(CONFIG_CURSES) += curses.o
|
||||
vnc-obj-y += vnc.o d3des.o
|
||||
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
|
||||
vnc-obj-y += vnc-enc-tight.o vnc-palette.o
|
||||
vnc-obj-y += vnc-enc-zrle.o
|
||||
vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
|
||||
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
|
||||
ifdef CONFIG_VNC_THREAD
|
||||
vnc-obj-y += vnc-jobs-async.o
|
||||
else
|
||||
vnc-obj-y += vnc-jobs-sync.o
|
||||
endif
|
||||
common-obj-y += $(addprefix ui/, $(ui-obj-y))
|
||||
common-obj-$(CONFIG_VNC) += $(addprefix ui/, $(vnc-obj-y))
|
||||
|
||||
common-obj-y += iov.o acl.o
|
||||
common-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
common-obj-y += notify.o event_notifier.o
|
||||
common-obj-y += qemu-timer.o qemu-timer-common.o
|
||||
|
||||
slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o
|
||||
slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
|
||||
slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
|
||||
common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y))
|
||||
|
||||
# xen backend driver support
|
||||
common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
|
||||
common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o
|
||||
|
||||
######################################################################
|
||||
# libuser
|
||||
@@ -118,11 +198,156 @@ endif
|
||||
user-obj-y =
|
||||
user-obj-y += envlist.o path.o
|
||||
user-obj-y += tcg-runtime.o host-utils.o
|
||||
user-obj-y += cache-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/
|
||||
user-obj-y += $(qom-obj-twice-y)
|
||||
|
||||
######################################################################
|
||||
# libhw
|
||||
|
||||
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-$(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) += 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
|
||||
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
|
||||
hw-obj-$(CONFIG_ECC) += ecc.o
|
||||
hw-obj-$(CONFIG_NAND) += nand.o
|
||||
hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
|
||||
hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
|
||||
|
||||
hw-obj-$(CONFIG_M48T59) += m48t59.o
|
||||
hw-obj-$(CONFIG_ESCC) += escc.o
|
||||
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_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_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_NSS) += ccid-card-emulated.o
|
||||
hw-obj-$(CONFIG_USB_REDIR) += usb/redirect.o
|
||||
hw-obj-$(CONFIG_I8259) += i8259_common.o 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
|
||||
hw-obj-$(CONFIG_ADB) += adb.o
|
||||
hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
|
||||
hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
|
||||
# OldWorld PowerMac
|
||||
hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
|
||||
hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o
|
||||
# NewWorld PowerMac
|
||||
hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o
|
||||
hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o
|
||||
# PowerPC E500 boards
|
||||
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
|
||||
|
||||
hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
|
||||
|
||||
# PCI network cards
|
||||
hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
|
||||
hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
|
||||
hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
|
||||
hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
|
||||
hw-obj-$(CONFIG_E1000_PCI) += e1000.o
|
||||
hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
|
||||
|
||||
hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
|
||||
hw-obj-$(CONFIG_LAN9118) += lan9118.o
|
||||
hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
|
||||
hw-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
|
||||
|
||||
# IDE
|
||||
hw-obj-$(CONFIG_IDE_CORE) += ide/core.o ide/atapi.o
|
||||
hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o
|
||||
hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o
|
||||
hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o
|
||||
hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o
|
||||
hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o
|
||||
hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o
|
||||
hw-obj-$(CONFIG_IDE_VIA) += ide/via.o
|
||||
hw-obj-$(CONFIG_AHCI) += ide/ahci.o
|
||||
hw-obj-$(CONFIG_AHCI) += ide/ich.o
|
||||
|
||||
# SCSI layer
|
||||
hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
|
||||
hw-obj-$(CONFIG_ESP) += esp.o
|
||||
|
||||
hw-obj-y += dma-helpers.o sysbus.o isa-bus.o
|
||||
hw-obj-y += qdev-addr.o
|
||||
|
||||
# VGA
|
||||
hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o
|
||||
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
|
||||
sound-obj-$(CONFIG_ES1370) += es1370.o
|
||||
sound-obj-$(CONFIG_AC97) += ac97.o
|
||||
sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
|
||||
sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
|
||||
sound-obj-$(CONFIG_CS4231A) += cs4231a.o
|
||||
sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
|
||||
|
||||
adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
|
||||
hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
|
||||
|
||||
9pfs-nested-$(CONFIG_VIRTFS) = virtio-9p.o
|
||||
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o
|
||||
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
|
||||
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))
|
||||
|
||||
######################################################################
|
||||
# libdis
|
||||
@@ -200,30 +425,31 @@ ifneq ($(TRACE_BACKEND),dtrace)
|
||||
trace-obj-y = trace.o
|
||||
endif
|
||||
|
||||
trace-obj-$(CONFIG_TRACE_DEFAULT) += trace/default.o
|
||||
trace-obj-$(CONFIG_TRACE_SIMPLE) += trace/simple.o
|
||||
trace-nested-$(CONFIG_TRACE_DEFAULT) += default.o
|
||||
|
||||
trace-nested-$(CONFIG_TRACE_SIMPLE) += simple.o
|
||||
trace-obj-$(CONFIG_TRACE_SIMPLE) += qemu-timer-common.o
|
||||
trace-obj-$(CONFIG_TRACE_STDERR) += trace/stderr.o
|
||||
trace-obj-y += trace/control.o
|
||||
|
||||
trace-nested-$(CONFIG_TRACE_STDERR) += stderr.o
|
||||
|
||||
trace-nested-y += control.o
|
||||
|
||||
trace-obj-y += $(addprefix trace/, $(trace-nested-y))
|
||||
|
||||
$(trace-obj-y): $(GENERATED_HEADERS)
|
||||
|
||||
######################################################################
|
||||
# smartcard
|
||||
|
||||
libcacard-y += libcacard/cac.o libcacard/event.o
|
||||
libcacard-y += libcacard/vcard.o libcacard/vreader.o
|
||||
libcacard-y += libcacard/vcard_emul_nss.o
|
||||
libcacard-y += libcacard/vcard_emul_type.o
|
||||
libcacard-y += libcacard/card_7816.o
|
||||
|
||||
common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
|
||||
libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
|
||||
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
qapi-obj-y = qapi/
|
||||
qapi-obj-y += qapi-types.o qapi-visit.o
|
||||
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-obj-y = $(addprefix qapi/, $(qapi-nested-y))
|
||||
|
||||
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o
|
||||
common-obj-y += qmp.o hmp.o
|
||||
@@ -233,8 +459,13 @@ universal-obj-y += $(qapi-obj-y)
|
||||
######################################################################
|
||||
# guest agent
|
||||
|
||||
qga-obj-y = qga/ qemu-ga.o module.o qemu-tool.o
|
||||
qga-obj-$(CONFIG_POSIX) += qemu-sockets.o qemu-option.o
|
||||
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-obj-y = $(addprefix qga/, $(qga-nested-y))
|
||||
qga-obj-y += qemu-ga.o module.o
|
||||
qga-obj-$(CONFIG_WIN32) += oslib-win32.o
|
||||
qga-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-sockets.o qemu-option.o
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
@@ -242,13 +473,3 @@ vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||
|
||||
QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
||||
|
||||
nested-vars += \
|
||||
stub-obj-y \
|
||||
qga-obj-y \
|
||||
qom-obj-y \
|
||||
qapi-obj-y \
|
||||
block-obj-y \
|
||||
user-obj-y \
|
||||
common-obj-y \
|
||||
extra-obj-y
|
||||
dummy := $(call unnest-vars)
|
||||
+307
-58
@@ -1,15 +1,26 @@
|
||||
# -*- Mode: makefile -*-
|
||||
|
||||
GENERATED_HEADERS = config-target.h
|
||||
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
|
||||
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
|
||||
CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
|
||||
|
||||
include ../config-host.mak
|
||||
include config-devices.mak
|
||||
include config-target.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
ifneq ($(HWDIR),)
|
||||
include $(HWDIR)/config.mak
|
||||
endif
|
||||
|
||||
$(call set-vpath, $(SRC_PATH))
|
||||
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
|
||||
$(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw)
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
endif
|
||||
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target-$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
QEMU_CFLAGS += -I.. -I$(TARGET_PATH) -DNEED_CPU_H
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/include
|
||||
|
||||
@@ -66,26 +77,78 @@ all: $(PROGS) stap
|
||||
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
obj-y = exec.o translate-all.o cpu-exec.o
|
||||
obj-y += tcg/tcg.o tcg/optimize.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += tci.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += disas.o
|
||||
obj-$(CONFIG_TCI_DIS) += tci-dis.o
|
||||
obj-y += target-$(TARGET_BASE_ARCH)/
|
||||
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
# variables
|
||||
ifneq ($(TARGET_BASE_ARCH), sparc)
|
||||
op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
|
||||
endif
|
||||
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.
|
||||
signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
|
||||
|
||||
#########################################################
|
||||
# Linux user emulator target
|
||||
|
||||
ifdef CONFIG_LINUX_USER
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
|
||||
$(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR))
|
||||
|
||||
obj-y += linux-user/
|
||||
obj-y += gdbstub.o thunk.o user-exec.o $(oslib-obj-y)
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
|
||||
obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \
|
||||
elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \
|
||||
user-exec.o $(oslib-obj-y)
|
||||
|
||||
obj-$(TARGET_HAS_BFLT) += flatload.o
|
||||
|
||||
obj-$(TARGET_I386) += vm86.o
|
||||
|
||||
obj-i386-y += ioport-user.o
|
||||
|
||||
nwfpe-obj-y = fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o fpopcode.o
|
||||
nwfpe-obj-y += single_cpdo.o double_cpdo.o extended_cpdo.o
|
||||
obj-arm-y += $(addprefix nwfpe/, $(nwfpe-obj-y))
|
||||
obj-arm-y += arm-semi.o
|
||||
|
||||
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
|
||||
|
||||
@@ -94,83 +157,269 @@ endif #CONFIG_LINUX_USER
|
||||
|
||||
ifdef CONFIG_BSD_USER
|
||||
|
||||
$(call set-vpath, $(SRC_PATH)/bsd-user)
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
|
||||
|
||||
obj-y += bsd-user/
|
||||
obj-y += gdbstub.o user-exec.o $(oslib-obj-y)
|
||||
obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
|
||||
gdbstub.o uaccess.o user-exec.o
|
||||
|
||||
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)
|
||||
|
||||
endif #CONFIG_BSD_USER
|
||||
|
||||
#########################################################
|
||||
# System emulator target
|
||||
ifdef CONFIG_SOFTMMU
|
||||
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
|
||||
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
|
||||
CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
|
||||
CONFIG_NO_GET_MEMORY_MAPPING = $(if $(subst n,,$(CONFIG_HAVE_GET_MEMORY_MAPPING)),n,y)
|
||||
CONFIG_NO_CORE_DUMP = $(if $(subst n,,$(CONFIG_HAVE_CORE_DUMP)),n,y)
|
||||
|
||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o
|
||||
obj-y += hw/
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
|
||||
# virtio has to be here due to weird dependency between PCI and virtio-net.
|
||||
# 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-$(CONFIG_HAVE_GET_MEMORY_MAPPING) += memory_mapping.o
|
||||
obj-$(CONFIG_HAVE_CORE_DUMP) += dump.o
|
||||
obj-$(CONFIG_NO_GET_MEMORY_MAPPING) += memory_mapping-stub.o
|
||||
obj-$(CONFIG_NO_CORE_DUMP) += dump-stub.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)
|
||||
|
||||
# xen support
|
||||
obj-$(CONFIG_XEN) += xen-all.o xen-mapcache.o
|
||||
obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o
|
||||
obj-$(CONFIG_NO_XEN) += xen-stub.o
|
||||
|
||||
# Hardware support
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
obj-y += hw/sparc64/
|
||||
else
|
||||
obj-y += hw/$(TARGET_BASE_ARCH)/
|
||||
obj-i386-$(CONFIG_XEN) += xen_platform.o xen_apic.o
|
||||
|
||||
# Inter-VM PCI shared memory
|
||||
CONFIG_IVSHMEM =
|
||||
ifeq ($(CONFIG_KVM), y)
|
||||
ifeq ($(CONFIG_PCI), y)
|
||||
CONFIG_IVSHMEM = y
|
||||
endif
|
||||
endif
|
||||
obj-$(CONFIG_IVSHMEM) += ivshmem.o
|
||||
|
||||
# Generic hotplugging
|
||||
obj-y += device-hotplug.o
|
||||
|
||||
# Hardware support
|
||||
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 += vmport.o
|
||||
obj-i386-y += 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_SPICE) += qxl.o qxl-logger.o qxl-render.o
|
||||
|
||||
# shared objects
|
||||
obj-ppc-y = ppc.o ppc_booke.o
|
||||
# PREP target
|
||||
obj-ppc-y += mc146818rtc.o
|
||||
obj-ppc-y += ppc_prep.o
|
||||
# OldWorld PowerMac
|
||||
obj-ppc-y += ppc_oldworld.o
|
||||
# NewWorld PowerMac
|
||||
obj-ppc-y += ppc_newworld.o
|
||||
# IBM pSeries (sPAPR)
|
||||
obj-ppc-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
|
||||
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
|
||||
# PowerPC E500 boards
|
||||
obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o
|
||||
# PowerPC 440 Xilinx ML507 reference board.
|
||||
obj-ppc-y += virtex_ml507.o
|
||||
obj-ppc-$(CONFIG_KVM) += kvm_ppc.o
|
||||
obj-ppc-$(CONFIG_FDT) += device_tree.o
|
||||
# PowerPC OpenPIC
|
||||
obj-ppc-y += openpic.o
|
||||
|
||||
# Xilinx PPC peripherals
|
||||
obj-ppc-y += xilinx_intc.o
|
||||
obj-ppc-y += xilinx_timer.o
|
||||
obj-ppc-y += xilinx_uartlite.o
|
||||
obj-ppc-y += xilinx_ethlite.o
|
||||
|
||||
# LM32 boards
|
||||
obj-lm32-y += lm32_boards.o
|
||||
obj-lm32-y += milkymist.o
|
||||
|
||||
# LM32 peripherals
|
||||
obj-lm32-y += lm32_pic.o
|
||||
obj-lm32-y += lm32_juart.o
|
||||
obj-lm32-y += lm32_timer.o
|
||||
obj-lm32-y += lm32_uart.o
|
||||
obj-lm32-y += lm32_sys.o
|
||||
obj-lm32-y += milkymist-ac97.o
|
||||
obj-lm32-y += milkymist-hpdmc.o
|
||||
obj-lm32-y += milkymist-memcard.o
|
||||
obj-lm32-y += milkymist-minimac2.o
|
||||
obj-lm32-y += milkymist-pfpu.o
|
||||
obj-lm32-y += milkymist-softusb.o
|
||||
obj-lm32-y += milkymist-sysctl.o
|
||||
obj-lm32-$(CONFIG_OPENGL) += milkymist-tmu2.o
|
||||
obj-lm32-y += milkymist-uart.o
|
||||
obj-lm32-y += milkymist-vgafb.o
|
||||
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 += gt64xxx.o mc146818rtc.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
|
||||
obj-microblaze-y += xilinx_timer.o
|
||||
obj-microblaze-y += xilinx_uartlite.o
|
||||
obj-microblaze-y += xilinx_ethlite.o
|
||||
obj-microblaze-y += xilinx_axidma.o
|
||||
obj-microblaze-y += xilinx_axienet.o
|
||||
|
||||
obj-microblaze-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
# Boards
|
||||
obj-cris-y = cris_pic_cpu.o
|
||||
obj-cris-y += cris-boot.o
|
||||
obj-cris-y += axis_dev88.o
|
||||
|
||||
# IO blocks
|
||||
obj-cris-y += etraxfs_dma.o
|
||||
obj-cris-y += etraxfs_pic.o
|
||||
obj-cris-y += etraxfs_eth.o
|
||||
obj-cris-y += etraxfs_timer.o
|
||||
obj-cris-y += etraxfs_ser.o
|
||||
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
obj-sparc-y = sun4u.o apb_pci.o
|
||||
obj-sparc-y += mc146818rtc.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
|
||||
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
|
||||
|
||||
# GRLIB
|
||||
obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
|
||||
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
|
||||
obj-arm-y += gumstix.o
|
||||
obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o
|
||||
obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
|
||||
omap_gpio.o omap_intc.o omap_uart.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 += 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 += 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
|
||||
obj-sh4-y += ide/mmio.o
|
||||
|
||||
obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
|
||||
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 += alpha_pci.o alpha_dp264.o alpha_typhoon.o
|
||||
|
||||
obj-xtensa-y += xtensa_pic.o
|
||||
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)
|
||||
|
||||
GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
|
||||
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)
|
||||
obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
|
||||
obj-y += $(addprefix ../, $(trace-obj-y))
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
|
||||
# Workaround for http://gcc.gnu.org/PR55489, see configure.
|
||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
||||
ifndef CONFIG_LINUX_USER
|
||||
ifndef CONFIG_BSD_USER
|
||||
# libcacard needs qemu-thread support, and besides is only needed by devices
|
||||
# so not requires with linux-user / bsd-user targets
|
||||
obj-$(CONFIG_SMARTCARD_NSS) += $(addprefix ../libcacard/, $(libcacard-y))
|
||||
endif # CONFIG_BSD_USER
|
||||
endif # CONFIG_LINUX_USER
|
||||
|
||||
nested-vars += obj-y
|
||||
|
||||
# This resolves all nested paths, so it must come last
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
all-obj-y = $(obj-y)
|
||||
all-obj-y += $(addprefix ../, $(universal-obj-y))
|
||||
|
||||
ifdef CONFIG_SOFTMMU
|
||||
all-obj-y += $(addprefix ../, $(common-obj-y))
|
||||
all-obj-y += $(addprefix ../libdis/, $(libdis-y))
|
||||
all-obj-y += $(addprefix ../, $(trace-obj-y))
|
||||
else
|
||||
all-obj-y += $(addprefix ../libuser/, $(user-obj-y))
|
||||
all-obj-y += $(addprefix ../libdis-user/, $(libdis-y))
|
||||
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): $(all-obj-y) ../libqemustub.a
|
||||
$(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): $(all-obj-y) ../libqemustub.a
|
||||
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
|
||||
$(call LINK,$^)
|
||||
endif
|
||||
|
||||
@@ -184,8 +433,8 @@ qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
|
||||
clean:
|
||||
rm -f *.a *~ $(PROGS)
|
||||
rm -f $(shell find . -name '*.[od]')
|
||||
rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
|
||||
rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o kvm/*.o
|
||||
rm -f hmp-commands.h qmp-commands-old.h gdbstub-xml.c
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
rm -f *.stp
|
||||
@@ -203,5 +452,5 @@ ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS += config-target.h
|
||||
Makefile: $(GENERATED_HEADERS)
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
+3
-1
@@ -10,7 +10,6 @@ $(call set-vpath, $(SRC_PATH))
|
||||
|
||||
QEMU_CFLAGS+=-I..
|
||||
QEMU_CFLAGS += -I$(SRC_PATH)/include
|
||||
QEMU_CFLAGS += -DCONFIG_USER_ONLY
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
|
||||
@@ -22,3 +21,6 @@ clean:
|
||||
for d in . trace; do \
|
||||
rm -f $$d/*.o $$d/*.d $$d/*.a $$d/*~; \
|
||||
done
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
@@ -1,299 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# QEMU Guest Agent Client
|
||||
#
|
||||
# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
# the COPYING file in the top-level directory.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# Start QEMU with:
|
||||
#
|
||||
# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
|
||||
# -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
|
||||
#
|
||||
# Run the script:
|
||||
#
|
||||
# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
|
||||
#
|
||||
# or
|
||||
#
|
||||
# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
|
||||
# $ qemu-ga-client <command> [args...]
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# $ qemu-ga-client cat /etc/resolv.conf
|
||||
# # Generated by NetworkManager
|
||||
# nameserver 10.0.2.3
|
||||
# $ qemu-ga-client fsfreeze status
|
||||
# thawed
|
||||
# $ qemu-ga-client fsfreeze freeze
|
||||
# 2 filesystems frozen
|
||||
#
|
||||
# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
|
||||
#
|
||||
|
||||
import base64
|
||||
import random
|
||||
|
||||
import qmp
|
||||
|
||||
|
||||
class QemuGuestAgent(qmp.QEMUMonitorProtocol):
|
||||
def __getattr__(self, name):
|
||||
def wrapper(**kwds):
|
||||
return self.command('guest-' + name.replace('_', '-'), **kwds)
|
||||
return wrapper
|
||||
|
||||
|
||||
class QemuGuestAgentClient:
|
||||
error = QemuGuestAgent.error
|
||||
|
||||
def __init__(self, address):
|
||||
self.qga = QemuGuestAgent(address)
|
||||
self.qga.connect(negotiate=False)
|
||||
|
||||
def sync(self, timeout=3):
|
||||
# Avoid being blocked forever
|
||||
if not self.ping(timeout):
|
||||
raise EnvironmentError('Agent seems not alive')
|
||||
uid = random.randint(0, (1 << 32) - 1)
|
||||
while True:
|
||||
ret = self.qga.sync(id=uid)
|
||||
if isinstance(ret, int) and int(ret) == uid:
|
||||
break
|
||||
|
||||
def __file_read_all(self, handle):
|
||||
eof = False
|
||||
data = ''
|
||||
while not eof:
|
||||
ret = self.qga.file_read(handle=handle, count=1024)
|
||||
_data = base64.b64decode(ret['buf-b64'])
|
||||
data += _data
|
||||
eof = ret['eof']
|
||||
return data
|
||||
|
||||
def read(self, path):
|
||||
handle = self.qga.file_open(path=path)
|
||||
try:
|
||||
data = self.__file_read_all(handle)
|
||||
finally:
|
||||
self.qga.file_close(handle=handle)
|
||||
return data
|
||||
|
||||
def info(self):
|
||||
info = self.qga.info()
|
||||
|
||||
msgs = []
|
||||
msgs.append('version: ' + info['version'])
|
||||
msgs.append('supported_commands:')
|
||||
enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
|
||||
msgs.append('\tenabled: ' + ', '.join(enabled))
|
||||
disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
|
||||
msgs.append('\tdisabled: ' + ', '.join(disabled))
|
||||
|
||||
return '\n'.join(msgs)
|
||||
|
||||
def __gen_ipv4_netmask(self, prefixlen):
|
||||
mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
|
||||
return '.'.join([str(mask >> 24),
|
||||
str((mask >> 16) & 0xff),
|
||||
str((mask >> 8) & 0xff),
|
||||
str(mask & 0xff)])
|
||||
|
||||
def ifconfig(self):
|
||||
nifs = self.qga.network_get_interfaces()
|
||||
|
||||
msgs = []
|
||||
for nif in nifs:
|
||||
msgs.append(nif['name'] + ':')
|
||||
if 'ip-addresses' in nif:
|
||||
for ipaddr in nif['ip-addresses']:
|
||||
if ipaddr['ip-address-type'] == 'ipv4':
|
||||
addr = ipaddr['ip-address']
|
||||
mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
|
||||
msgs.append("\tinet %s netmask %s" % (addr, mask))
|
||||
elif ipaddr['ip-address-type'] == 'ipv6':
|
||||
addr = ipaddr['ip-address']
|
||||
prefix = ipaddr['prefix']
|
||||
msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix))
|
||||
if nif['hardware-address'] != '00:00:00:00:00:00':
|
||||
msgs.append("\tether " + nif['hardware-address'])
|
||||
|
||||
return '\n'.join(msgs)
|
||||
|
||||
def ping(self, timeout):
|
||||
self.qga.settimeout(timeout)
|
||||
try:
|
||||
self.qga.ping()
|
||||
except self.qga.timeout:
|
||||
return False
|
||||
return True
|
||||
|
||||
def fsfreeze(self, cmd):
|
||||
if cmd not in ['status', 'freeze', 'thaw']:
|
||||
raise StandardError('Invalid command: ' + cmd)
|
||||
|
||||
return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
|
||||
|
||||
def fstrim(self, minimum=0):
|
||||
return getattr(self.qga, 'fstrim')(minimum=minimum)
|
||||
|
||||
def suspend(self, mode):
|
||||
if mode not in ['disk', 'ram', 'hybrid']:
|
||||
raise StandardError('Invalid mode: ' + mode)
|
||||
|
||||
try:
|
||||
getattr(self.qga, 'suspend' + '_' + mode)()
|
||||
# On error exception will raise
|
||||
except self.qga.timeout:
|
||||
# On success command will timed out
|
||||
return
|
||||
|
||||
def shutdown(self, mode='powerdown'):
|
||||
if mode not in ['powerdown', 'halt', 'reboot']:
|
||||
raise StandardError('Invalid mode: ' + mode)
|
||||
|
||||
try:
|
||||
self.qga.shutdown(mode=mode)
|
||||
except self.qga.timeout:
|
||||
return
|
||||
|
||||
|
||||
def _cmd_cat(client, args):
|
||||
if len(args) != 1:
|
||||
print('Invalid argument')
|
||||
print('Usage: cat <file>')
|
||||
sys.exit(1)
|
||||
print(client.read(args[0]))
|
||||
|
||||
|
||||
def _cmd_fsfreeze(client, args):
|
||||
usage = 'Usage: fsfreeze status|freeze|thaw'
|
||||
if len(args) != 1:
|
||||
print('Invalid argument')
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
if args[0] not in ['status', 'freeze', 'thaw']:
|
||||
print('Invalid command: ' + args[0])
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
cmd = args[0]
|
||||
ret = client.fsfreeze(cmd)
|
||||
if cmd == 'status':
|
||||
print(ret)
|
||||
elif cmd == 'freeze':
|
||||
print("%d filesystems frozen" % ret)
|
||||
else:
|
||||
print("%d filesystems thawed" % ret)
|
||||
|
||||
|
||||
def _cmd_fstrim(client, args):
|
||||
if len(args) == 0:
|
||||
minimum = 0
|
||||
else:
|
||||
minimum = int(args[0])
|
||||
print(client.fstrim(minimum))
|
||||
|
||||
|
||||
def _cmd_ifconfig(client, args):
|
||||
print(client.ifconfig())
|
||||
|
||||
|
||||
def _cmd_info(client, args):
|
||||
print(client.info())
|
||||
|
||||
|
||||
def _cmd_ping(client, args):
|
||||
if len(args) == 0:
|
||||
timeout = 3
|
||||
else:
|
||||
timeout = float(args[0])
|
||||
alive = client.ping(timeout)
|
||||
if not alive:
|
||||
print("Not responded in %s sec" % args[0])
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _cmd_suspend(client, args):
|
||||
usage = 'Usage: suspend disk|ram|hybrid'
|
||||
if len(args) != 1:
|
||||
print('Less argument')
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
if args[0] not in ['disk', 'ram', 'hybrid']:
|
||||
print('Invalid command: ' + args[0])
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
client.suspend(args[0])
|
||||
|
||||
|
||||
def _cmd_shutdown(client, args):
|
||||
client.shutdown()
|
||||
_cmd_powerdown = _cmd_shutdown
|
||||
|
||||
|
||||
def _cmd_halt(client, args):
|
||||
client.shutdown('halt')
|
||||
|
||||
|
||||
def _cmd_reboot(client, args):
|
||||
client.shutdown('reboot')
|
||||
|
||||
|
||||
commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
|
||||
|
||||
|
||||
def main(address, cmd, args):
|
||||
if not os.path.exists(address):
|
||||
print('%s not found' % address)
|
||||
sys.exit(1)
|
||||
|
||||
if cmd not in commands:
|
||||
print('Invalid command: ' + cmd)
|
||||
print('Available commands: ' + ', '.join(commands))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
client = QemuGuestAgentClient(address)
|
||||
except QemuGuestAgent.error, e:
|
||||
import errno
|
||||
|
||||
print(e)
|
||||
if e.errno == errno.ECONNREFUSED:
|
||||
print('Hint: qemu is not running?')
|
||||
sys.exit(1)
|
||||
|
||||
if cmd != 'ping':
|
||||
client.sync()
|
||||
|
||||
globals()['_cmd_' + cmd](client, args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import os
|
||||
import optparse
|
||||
|
||||
address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
|
||||
|
||||
usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
|
||||
usage += '<command>: ' + ', '.join(commands)
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('--address', action='store', type='string',
|
||||
default=address, help='Specify a ip:port pair or a unix socket path')
|
||||
options, args = parser.parse_args()
|
||||
|
||||
address = options.address
|
||||
if address is None:
|
||||
parser.error('address is not specified')
|
||||
sys.exit(1)
|
||||
|
||||
if len(args) == 0:
|
||||
parser.error('Less argument')
|
||||
sys.exit(1)
|
||||
|
||||
main(address, args[0], args[1:])
|
||||
+111
-204
@@ -1,23 +1,6 @@
|
||||
QEMU Monitor Protocol Events
|
||||
============================
|
||||
|
||||
BALLOON_CHANGE
|
||||
--------------
|
||||
|
||||
Emitted when the guest changes the actual BALLOON level. This
|
||||
value is equivalent to the 'actual' field return by the
|
||||
'query-balloon' command
|
||||
|
||||
Data:
|
||||
|
||||
- "actual": actual level of the guest memory balloon in bytes (json-number)
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "BALLOON_CHANGE",
|
||||
"data": { "actual": 944766976 },
|
||||
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
|
||||
|
||||
BLOCK_IO_ERROR
|
||||
--------------
|
||||
|
||||
@@ -43,99 +26,6 @@ Example:
|
||||
Note: If action is "stop", a STOP event will eventually follow the
|
||||
BLOCK_IO_ERROR event.
|
||||
|
||||
BLOCK_JOB_CANCELLED
|
||||
-------------------
|
||||
|
||||
Emitted when a block job has been cancelled.
|
||||
|
||||
Data:
|
||||
|
||||
- "type": Job type (json-string; "stream" for image streaming
|
||||
"commit" for block commit)
|
||||
- "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 } }
|
||||
|
||||
BLOCK_JOB_COMPLETED
|
||||
-------------------
|
||||
|
||||
Emitted when a block job has completed.
|
||||
|
||||
Data:
|
||||
|
||||
- "type": Job type (json-string; "stream" for image streaming
|
||||
"commit" for block commit)
|
||||
- "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_ERROR
|
||||
---------------
|
||||
|
||||
Emitted when a block job encounters an error.
|
||||
|
||||
Data:
|
||||
|
||||
- "device": device name (json-string)
|
||||
- "operation": I/O operation (json-string, "read" or "write")
|
||||
- "action": action that has been taken, it's one of the following (json-string):
|
||||
"ignore": error has been ignored, the job may fail later
|
||||
"report": error will be reported and the job canceled
|
||||
"stop": error caused job to be paused
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "BLOCK_JOB_ERROR",
|
||||
"data": { "device": "ide0-hd1",
|
||||
"operation": "write",
|
||||
"action": "stop" },
|
||||
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
|
||||
|
||||
BLOCK_JOB_READY
|
||||
---------------
|
||||
|
||||
Emitted when a block job is ready to complete.
|
||||
|
||||
Data:
|
||||
|
||||
- "device": device name (json-string)
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "BLOCK_JOB_READY",
|
||||
"data": { "device": "ide0-hd1" },
|
||||
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
|
||||
|
||||
Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR
|
||||
event.
|
||||
|
||||
DEVICE_TRAY_MOVED
|
||||
-----------------
|
||||
|
||||
@@ -208,68 +98,6 @@ Example:
|
||||
Note: If the command-line option "-no-shutdown" has been specified, a STOP
|
||||
event will eventually follow the SHUTDOWN event.
|
||||
|
||||
SPICE_CONNECTED, SPICE_DISCONNECTED
|
||||
-----------------------------------
|
||||
|
||||
Emitted when a SPICE client connects or disconnects.
|
||||
|
||||
Data:
|
||||
|
||||
- "server": Server information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
- "client": Client information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
|
||||
Example:
|
||||
|
||||
{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
|
||||
"event": "SPICE_CONNECTED",
|
||||
"data": {
|
||||
"server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
|
||||
"client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
|
||||
}}
|
||||
|
||||
SPICE_INITIALIZED
|
||||
-----------------
|
||||
|
||||
Emitted after initial handshake and authentication takes place (if any)
|
||||
and the SPICE channel is up'n'running
|
||||
|
||||
Data:
|
||||
|
||||
- "server": Server information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
- "auth": authentication method (json-string, optional)
|
||||
- "client": Client information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
- "connection-id": spice connection id. All channels with the same id
|
||||
belong to the same spice session (json-int)
|
||||
- "channel-type": channel type. "1" is the main control channel, filter for
|
||||
this one if you want track spice sessions only (json-int)
|
||||
- "channel-id": channel id. Usually "0", might be different needed when
|
||||
multiple channels of the same type exist, such as multiple
|
||||
display channels in a multihead setup (json-int)
|
||||
- "tls": whevener the channel is encrypted (json-bool)
|
||||
|
||||
Example:
|
||||
|
||||
{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
|
||||
"event": "SPICE_INITIALIZED",
|
||||
"data": {"server": {"auth": "spice", "port": "5921",
|
||||
"family": "ipv4", "host": "127.0.0.1"},
|
||||
"client": {"port": "49004", "family": "ipv4", "channel-type": 3,
|
||||
"connection-id": 1804289383, "host": "127.0.0.1",
|
||||
"channel-id": 0, "tls": true}
|
||||
}}
|
||||
|
||||
STOP
|
||||
----
|
||||
|
||||
@@ -282,32 +110,6 @@ Example:
|
||||
{ "event": "STOP",
|
||||
"timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
|
||||
|
||||
SUSPEND
|
||||
-------
|
||||
|
||||
Emitted when guest enters S3 state.
|
||||
|
||||
Data: None.
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "SUSPEND",
|
||||
"timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
|
||||
|
||||
SUSPEND_DISK
|
||||
------------
|
||||
|
||||
Emitted when the guest makes a request to enter S4 state.
|
||||
|
||||
Data: None.
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "SUSPEND_DISK",
|
||||
"timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
|
||||
|
||||
Note: QEMU shuts down when entering S4 state.
|
||||
|
||||
VNC_CONNECTED
|
||||
-------------
|
||||
|
||||
@@ -398,17 +200,69 @@ Example:
|
||||
"host": "127.0.0.1", "sasl_username": "luiz" } },
|
||||
"timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
|
||||
|
||||
WAKEUP
|
||||
------
|
||||
SPICE_CONNECTED, SPICE_DISCONNECTED
|
||||
-----------------------------------
|
||||
|
||||
Emitted when the guest has woken up from S3 and is running.
|
||||
Emitted when a SPICE client connects or disconnects.
|
||||
|
||||
Data: None.
|
||||
Data:
|
||||
|
||||
- "server": Server information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
- "client": Client information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
|
||||
Example:
|
||||
|
||||
{ "event": "WATCHDOG",
|
||||
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
|
||||
{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
|
||||
"event": "SPICE_CONNECTED",
|
||||
"data": {
|
||||
"server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
|
||||
"client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
|
||||
}}
|
||||
|
||||
|
||||
SPICE_INITIALIZED
|
||||
-----------------
|
||||
|
||||
Emitted after initial handshake and authentication takes place (if any)
|
||||
and the SPICE channel is up'n'running
|
||||
|
||||
Data:
|
||||
|
||||
- "server": Server information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
- "auth": authentication method (json-string, optional)
|
||||
- "client": Client information (json-object)
|
||||
- "host": IP address (json-string)
|
||||
- "port": port number (json-string)
|
||||
- "family": address family (json-string, "ipv4" or "ipv6")
|
||||
- "connection-id": spice connection id. All channels with the same id
|
||||
belong to the same spice session (json-int)
|
||||
- "channel-type": channel type. "1" is the main control channel, filter for
|
||||
this one if you want track spice sessions only (json-int)
|
||||
- "channel-id": channel id. Usually "0", might be different needed when
|
||||
multiple channels of the same type exist, such as multiple
|
||||
display channels in a multihead setup (json-int)
|
||||
- "tls": whevener the channel is encrypted (json-bool)
|
||||
|
||||
Example:
|
||||
|
||||
{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
|
||||
"event": "SPICE_INITIALIZED",
|
||||
"data": {"server": {"auth": "spice", "port": "5921",
|
||||
"family": "ipv4", "host": "127.0.0.1"},
|
||||
"client": {"port": "49004", "family": "ipv4", "channel-type": 3,
|
||||
"connection-id": 1804289383, "host": "127.0.0.1",
|
||||
"channel-id": 0, "tls": true}
|
||||
}}
|
||||
|
||||
|
||||
WATCHDOG
|
||||
--------
|
||||
@@ -428,3 +282,56 @@ 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 } }
|
||||
+13
-33
@@ -33,7 +33,6 @@
|
||||
import qmp
|
||||
import readline
|
||||
import sys
|
||||
import pprint
|
||||
|
||||
class QMPCompleter(list):
|
||||
def complete(self, text, state):
|
||||
@@ -53,11 +52,10 @@ class QMPShellBadPort(QMPShellError):
|
||||
# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
|
||||
# _execute_cmd()). Let's design a better one.
|
||||
class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
def __init__(self, address, pp=None):
|
||||
def __init__(self, address):
|
||||
qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
|
||||
self._greeting = None
|
||||
self._completer = None
|
||||
self._pp = pp
|
||||
|
||||
def __get_address(self, arg):
|
||||
"""
|
||||
@@ -116,11 +114,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
if resp is None:
|
||||
print 'Disconnected'
|
||||
return False
|
||||
|
||||
if self._pp is not None:
|
||||
self._pp.pprint(resp)
|
||||
else:
|
||||
print resp
|
||||
print resp
|
||||
return True
|
||||
|
||||
def connect(self):
|
||||
@@ -228,36 +222,22 @@ def die(msg):
|
||||
def fail_cmdline(option=None):
|
||||
if option:
|
||||
sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
|
||||
sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
|
||||
sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
addr = ''
|
||||
qemu = None
|
||||
hmp = False
|
||||
pp = None
|
||||
|
||||
try:
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == "-H":
|
||||
if qemu is not None:
|
||||
fail_cmdline(arg)
|
||||
hmp = True
|
||||
elif arg == "-p":
|
||||
if pp is not None:
|
||||
fail_cmdline(arg)
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
else:
|
||||
if qemu is not None:
|
||||
fail_cmdline(arg)
|
||||
if hmp:
|
||||
qemu = HMPShell(arg)
|
||||
else:
|
||||
qemu = QMPShell(arg, pp)
|
||||
addr = arg
|
||||
|
||||
if qemu is None:
|
||||
fail_cmdline()
|
||||
if len(sys.argv) == 2:
|
||||
qemu = QMPShell(sys.argv[1])
|
||||
addr = sys.argv[1]
|
||||
elif len(sys.argv) == 3:
|
||||
if sys.argv[1] != '-H':
|
||||
fail_cmdline(sys.argv[1])
|
||||
qemu = HMPShell(sys.argv[2])
|
||||
addr = sys.argv[2]
|
||||
else:
|
||||
fail_cmdline()
|
||||
except QMPShellBadPort:
|
||||
die('bad port number in command-line')
|
||||
|
||||
|
||||
+7
-3
@@ -106,11 +106,14 @@ completed because of an error condition.
|
||||
|
||||
The format is:
|
||||
|
||||
{ "error": { "class": json-string, "desc": json-string }, "id": json-value }
|
||||
{ "error": { "class": json-string, "data": json-object, "desc": json-string },
|
||||
"id": json-value }
|
||||
|
||||
Where,
|
||||
|
||||
- The "class" member contains the error class name (eg. "GenericError")
|
||||
- The "class" member contains the error class name (eg. "ServiceUnavailable")
|
||||
- The "data" member contains specific error data and is defined in a
|
||||
per-command basis, it will be an empty json-object if the error has no data
|
||||
- The "desc" member is a human-readable error message. Clients should
|
||||
not attempt to parse this message.
|
||||
- The "id" member contains the transaction identification associated with
|
||||
@@ -170,7 +173,8 @@ S: {"return": {"enabled": true, "present": true}, "id": "example"}
|
||||
------------------
|
||||
|
||||
C: { "execute": }
|
||||
S: {"error": {"class": "GenericError", "desc": "Invalid JSON syntax" } }
|
||||
S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data":
|
||||
{}}}
|
||||
|
||||
3.5 Powerdown event
|
||||
-------------------
|
||||
|
||||
+3
-30
@@ -49,6 +49,7 @@ class QEMUMonitorProtocol:
|
||||
return socket.socket(family, socket.SOCK_STREAM)
|
||||
|
||||
def __negotiate_capabilities(self):
|
||||
self.__sockfile = self.__sock.makefile()
|
||||
greeting = self.__json_read()
|
||||
if greeting is None or not greeting.has_key('QMP'):
|
||||
raise QMPConnectError
|
||||
@@ -72,7 +73,7 @@ class QEMUMonitorProtocol:
|
||||
|
||||
error = socket.error
|
||||
|
||||
def connect(self, negotiate=True):
|
||||
def connect(self):
|
||||
"""
|
||||
Connect to the QMP Monitor and perform capabilities negotiation.
|
||||
|
||||
@@ -82,9 +83,7 @@ class QEMUMonitorProtocol:
|
||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||
"""
|
||||
self.__sock.connect(self.__address)
|
||||
self.__sockfile = self.__sock.makefile()
|
||||
if negotiate:
|
||||
return self.__negotiate_capabilities()
|
||||
return self.__negotiate_capabilities()
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
@@ -96,7 +95,6 @@ class QEMUMonitorProtocol:
|
||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||
"""
|
||||
self.__sock, _ = self.__sock.accept()
|
||||
self.__sockfile = self.__sock.makefile()
|
||||
return self.__negotiate_capabilities()
|
||||
|
||||
def cmd_obj(self, qmp_cmd):
|
||||
@@ -136,26 +134,6 @@ class QEMUMonitorProtocol:
|
||||
raise Exception(ret['error']['desc'])
|
||||
return ret['return']
|
||||
|
||||
def pull_event(self, wait=False):
|
||||
"""
|
||||
Get and delete the first available QMP event.
|
||||
|
||||
@param wait: block until an event is available (bool)
|
||||
"""
|
||||
self.__sock.setblocking(0)
|
||||
try:
|
||||
self.__json_read()
|
||||
except socket.error, err:
|
||||
if err[0] == errno.EAGAIN:
|
||||
# No data available
|
||||
pass
|
||||
self.__sock.setblocking(1)
|
||||
if not self.__events and wait:
|
||||
self.__json_read(only_event=True)
|
||||
event = self.__events[0]
|
||||
del self.__events[0]
|
||||
return event
|
||||
|
||||
def get_events(self, wait=False):
|
||||
"""
|
||||
Get a list of available QMP events.
|
||||
@@ -183,8 +161,3 @@ class QEMUMonitorProtocol:
|
||||
def close(self):
|
||||
self.__sock.close()
|
||||
self.__sockfile.close()
|
||||
|
||||
timeout = socket.timeout
|
||||
|
||||
def settimeout(self, timeout):
|
||||
self.__sock.settimeout(timeout)
|
||||
-269
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* QEMU aio implementation
|
||||
*
|
||||
* Copyright IBM, Corp. 2008
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "qemu_socket.h"
|
||||
|
||||
struct AioHandler
|
||||
{
|
||||
GPollFD pfd;
|
||||
IOHandler *io_read;
|
||||
IOHandler *io_write;
|
||||
AioFlushHandler *io_flush;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
static AioHandler *find_aio_handler(AioContext *ctx, int fd)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->pfd.fd == fd)
|
||||
if (!node->deleted)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void aio_set_fd_handler(AioContext *ctx,
|
||||
int fd,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
AioFlushHandler *io_flush,
|
||||
void *opaque)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
node = find_aio_handler(ctx, fd);
|
||||
|
||||
/* Are we deleting the fd handler? */
|
||||
if (!io_read && !io_write) {
|
||||
if (node) {
|
||||
g_source_remove_poll(&ctx->source, &node->pfd);
|
||||
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (ctx->walking_handlers) {
|
||||
node->deleted = 1;
|
||||
node->pfd.revents = 0;
|
||||
} else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (node == NULL) {
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = g_malloc0(sizeof(AioHandler));
|
||||
node->pfd.fd = fd;
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
|
||||
g_source_add_poll(&ctx->source, &node->pfd);
|
||||
}
|
||||
/* Update handler with latest information */
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
node->io_flush = io_flush;
|
||||
node->opaque = opaque;
|
||||
|
||||
node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0);
|
||||
node->pfd.events |= (io_write ? G_IO_OUT : 0);
|
||||
}
|
||||
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *notifier,
|
||||
EventNotifierHandler *io_read,
|
||||
AioFlushEventNotifierHandler *io_flush)
|
||||
{
|
||||
aio_set_fd_handler(ctx, event_notifier_get_fd(notifier),
|
||||
(IOHandler *)io_read, NULL,
|
||||
(AioFlushHandler *)io_flush, notifier);
|
||||
}
|
||||
|
||||
bool aio_pending(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
int revents;
|
||||
|
||||
/*
|
||||
* FIXME: right now we cannot get G_IO_HUP and G_IO_ERR because
|
||||
* main-loop.c is still select based (due to the slirp legacy).
|
||||
* If main-loop.c ever switches to poll, G_IO_ERR should be
|
||||
* tested too. Dispatching G_IO_ERR to both handlers should be
|
||||
* okay, since handlers need to be ready for spurious wakeups.
|
||||
*/
|
||||
revents = node->pfd.revents & node->pfd.events;
|
||||
if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
|
||||
return true;
|
||||
}
|
||||
if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
static struct timeval tv0;
|
||||
AioHandler *node;
|
||||
fd_set rdfds, wrfds;
|
||||
int max_fd = -1;
|
||||
int ret;
|
||||
bool busy, progress;
|
||||
|
||||
progress = false;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call then.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Then dispatch any pending callbacks from the GSource.
|
||||
*
|
||||
* We have to walk very carefully in case qemu_aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
int revents;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
revents = node->pfd.revents & node->pfd.events;
|
||||
node->pfd.revents = 0;
|
||||
|
||||
/* See comment in aio_pending. */
|
||||
if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
|
||||
node->io_read(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
|
||||
node->io_write(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (progress && !blocking) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
FD_ZERO(&rdfds);
|
||||
FD_ZERO(&wrfds);
|
||||
|
||||
/* fill fd sets */
|
||||
busy = false;
|
||||
QLIST_FOREACH(node, &ctx->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->deleted && node->io_flush) {
|
||||
if (node->io_flush(node->opaque) == 0) {
|
||||
continue;
|
||||
}
|
||||
busy = true;
|
||||
}
|
||||
if (!node->deleted && node->io_read) {
|
||||
FD_SET(node->pfd.fd, &rdfds);
|
||||
max_fd = MAX(max_fd, node->pfd.fd + 1);
|
||||
}
|
||||
if (!node->deleted && node->io_write) {
|
||||
FD_SET(node->pfd.fd, &wrfds);
|
||||
max_fd = MAX(max_fd, node->pfd.fd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* No AIO operations? Get us out of here */
|
||||
if (!busy) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
ret = select(max_fd, &rdfds, &wrfds, NULL, blocking ? NULL : &tv0);
|
||||
|
||||
/* if we have any readable fds, dispatch event */
|
||||
if (ret > 0) {
|
||||
/* we have to walk very carefully in case
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (!node->deleted &&
|
||||
FD_ISSET(node->pfd.fd, &rdfds) &&
|
||||
node->io_read) {
|
||||
node->io_read(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
if (!node->deleted &&
|
||||
FD_ISSET(node->pfd.fd, &wrfds) &&
|
||||
node->io_write) {
|
||||
node->io_write(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(progress || busy);
|
||||
return true;
|
||||
}
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* QEMU aio implementation
|
||||
*
|
||||
* Copyright IBM Corp., 2008
|
||||
* Copyright Red Hat Inc., 2012
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "qemu_socket.h"
|
||||
|
||||
struct AioHandler {
|
||||
EventNotifier *e;
|
||||
EventNotifierHandler *io_notify;
|
||||
AioFlushEventNotifierHandler *io_flush;
|
||||
GPollFD pfd;
|
||||
int deleted;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *e,
|
||||
EventNotifierHandler *io_notify,
|
||||
AioFlushEventNotifierHandler *io_flush)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->e == e && !node->deleted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Are we deleting the fd handler? */
|
||||
if (!io_notify) {
|
||||
if (node) {
|
||||
g_source_remove_poll(&ctx->source, &node->pfd);
|
||||
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (ctx->walking_handlers) {
|
||||
node->deleted = 1;
|
||||
node->pfd.revents = 0;
|
||||
} else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (node == NULL) {
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = g_malloc0(sizeof(AioHandler));
|
||||
node->e = e;
|
||||
node->pfd.fd = (uintptr_t)event_notifier_get_handle(e);
|
||||
node->pfd.events = G_IO_IN;
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
|
||||
g_source_add_poll(&ctx->source, &node->pfd);
|
||||
}
|
||||
/* Update handler with latest information */
|
||||
node->io_notify = io_notify;
|
||||
node->io_flush = io_flush;
|
||||
}
|
||||
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
bool aio_pending(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->pfd.revents && node->io_notify) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
AioHandler *node;
|
||||
HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
|
||||
bool busy, progress;
|
||||
int count;
|
||||
|
||||
progress = false;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call then.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Then dispatch any pending callbacks from the GSource.
|
||||
*
|
||||
* We have to walk very carefully in case qemu_aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (node->pfd.revents && node->io_notify) {
|
||||
node->pfd.revents = 0;
|
||||
node->io_notify(node->e);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (progress && !blocking) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
/* fill fd sets */
|
||||
busy = false;
|
||||
count = 0;
|
||||
QLIST_FOREACH(node, &ctx->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->deleted && node->io_flush) {
|
||||
if (node->io_flush(node->e) == 0) {
|
||||
continue;
|
||||
}
|
||||
busy = true;
|
||||
}
|
||||
if (!node->deleted && node->io_notify) {
|
||||
events[count++] = event_notifier_get_handle(node->e);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* No AIO operations? Get us out of here */
|
||||
if (!busy) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
while (count > 0) {
|
||||
int timeout = blocking ? INFINITE : 0;
|
||||
int ret = WaitForMultipleObjects(count, events, FALSE, timeout);
|
||||
|
||||
/* if we have any signaled events, dispatch event */
|
||||
if ((DWORD) (ret - WAIT_OBJECT_0) >= count) {
|
||||
break;
|
||||
}
|
||||
|
||||
blocking = false;
|
||||
|
||||
/* we have to walk very carefully in case
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (!node->deleted &&
|
||||
event_notifier_get_handle(node->e) == events[ret - WAIT_OBJECT_0] &&
|
||||
node->io_notify) {
|
||||
node->io_notify(node->e);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try again, but only call each handler once. */
|
||||
events[ret - WAIT_OBJECT_0] = events[--count];
|
||||
}
|
||||
|
||||
assert(progress || busy);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* QEMU aio implementation
|
||||
*
|
||||
* Copyright IBM, Corp. 2008
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "qemu_socket.h"
|
||||
|
||||
typedef struct AioHandler AioHandler;
|
||||
|
||||
/* The list of registered AIO handlers */
|
||||
static QLIST_HEAD(, AioHandler) aio_handlers;
|
||||
|
||||
/* This is a simple lock used to protect the aio_handlers list. Specifically,
|
||||
* it's used to ensure that no callbacks are removed while we're walking and
|
||||
* dispatching callbacks.
|
||||
*/
|
||||
static int walking_handlers;
|
||||
|
||||
struct AioHandler
|
||||
{
|
||||
int fd;
|
||||
IOHandler *io_read;
|
||||
IOHandler *io_write;
|
||||
AioFlushHandler *io_flush;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
static AioHandler *find_aio_handler(int fd)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &aio_handlers, node) {
|
||||
if (node->fd == fd)
|
||||
if (!node->deleted)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qemu_aio_set_fd_handler(int fd,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
AioFlushHandler *io_flush,
|
||||
void *opaque)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
node = find_aio_handler(fd);
|
||||
|
||||
/* Are we deleting the fd handler? */
|
||||
if (!io_read && !io_write) {
|
||||
if (node) {
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (walking_handlers)
|
||||
node->deleted = 1;
|
||||
else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (node == NULL) {
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = g_malloc0(sizeof(AioHandler));
|
||||
node->fd = fd;
|
||||
QLIST_INSERT_HEAD(&aio_handlers, node, node);
|
||||
}
|
||||
/* Update handler with latest information */
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
node->io_flush = io_flush;
|
||||
node->opaque = opaque;
|
||||
}
|
||||
|
||||
qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qemu_aio_flush(void)
|
||||
{
|
||||
while (qemu_aio_wait());
|
||||
}
|
||||
|
||||
bool qemu_aio_wait(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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
+95
-505
@@ -31,8 +31,6 @@
|
||||
#include "config.h"
|
||||
#include "monitor.h"
|
||||
#include "sysemu.h"
|
||||
#include "bitops.h"
|
||||
#include "bitmap.h"
|
||||
#include "arch_init.h"
|
||||
#include "audio/audio.h"
|
||||
#include "hw/pc.h"
|
||||
@@ -45,17 +43,6 @@
|
||||
#include "hw/smbios.h"
|
||||
#include "exec-memory.h"
|
||||
#include "hw/pcspk.h"
|
||||
#include "qemu/page_cache.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
|
||||
#ifdef DEBUG_ARCH_INIT
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stdout, "arch_init: " fmt, ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_SPARC
|
||||
int graphic_width = 1024;
|
||||
@@ -84,8 +71,6 @@ int graphic_depth = 15;
|
||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
|
||||
#elif defined(TARGET_MIPS)
|
||||
#define QEMU_ARCH QEMU_ARCH_MIPS
|
||||
#elif defined(TARGET_OPENRISC)
|
||||
#define QEMU_ARCH QEMU_ARCH_OPENRISC
|
||||
#elif defined(TARGET_PPC)
|
||||
#define QEMU_ARCH QEMU_ARCH_PPC
|
||||
#elif defined(TARGET_S390X)
|
||||
@@ -96,8 +81,6 @@ int graphic_depth = 15;
|
||||
#define QEMU_ARCH QEMU_ARCH_SPARC
|
||||
#elif defined(TARGET_XTENSA)
|
||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#endif
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
@@ -111,7 +94,6 @@ const uint32_t arch_type = QEMU_ARCH;
|
||||
#define RAM_SAVE_FLAG_PAGE 0x08
|
||||
#define RAM_SAVE_FLAG_EOS 0x10
|
||||
#define RAM_SAVE_FLAG_CONTINUE 0x20
|
||||
#define RAM_SAVE_FLAG_XBZRLE 0x40
|
||||
|
||||
#ifdef __ALTIVEC__
|
||||
#include <altivec.h>
|
||||
@@ -139,6 +121,7 @@ static struct defconfig_file {
|
||||
/* 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 */
|
||||
@@ -178,287 +161,52 @@ static int is_dup_page(uint8_t *page)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* struct contains XBZRLE cache and a static page
|
||||
used by the compression */
|
||||
static struct {
|
||||
/* buffer used for XBZRLE encoding */
|
||||
uint8_t *encoded_buf;
|
||||
/* buffer for storing page content */
|
||||
uint8_t *current_buf;
|
||||
/* buffer used for XBZRLE decoding */
|
||||
uint8_t *decoded_buf;
|
||||
/* Cache for XBZRLE */
|
||||
PageCache *cache;
|
||||
} XBZRLE = {
|
||||
.encoded_buf = NULL,
|
||||
.current_buf = NULL,
|
||||
.decoded_buf = NULL,
|
||||
.cache = NULL,
|
||||
};
|
||||
|
||||
|
||||
int64_t xbzrle_cache_resize(int64_t new_size)
|
||||
{
|
||||
if (XBZRLE.cache != NULL) {
|
||||
return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) *
|
||||
TARGET_PAGE_SIZE;
|
||||
}
|
||||
return pow2floor(new_size);
|
||||
}
|
||||
|
||||
/* accounting for migration statistics */
|
||||
typedef struct AccountingInfo {
|
||||
uint64_t dup_pages;
|
||||
uint64_t norm_pages;
|
||||
uint64_t iterations;
|
||||
uint64_t xbzrle_bytes;
|
||||
uint64_t xbzrle_pages;
|
||||
uint64_t xbzrle_cache_miss;
|
||||
uint64_t xbzrle_overflows;
|
||||
} AccountingInfo;
|
||||
|
||||
static AccountingInfo acct_info;
|
||||
|
||||
static void acct_clear(void)
|
||||
{
|
||||
memset(&acct_info, 0, sizeof(acct_info));
|
||||
}
|
||||
|
||||
uint64_t dup_mig_bytes_transferred(void)
|
||||
{
|
||||
return acct_info.dup_pages * TARGET_PAGE_SIZE;
|
||||
}
|
||||
|
||||
uint64_t dup_mig_pages_transferred(void)
|
||||
{
|
||||
return acct_info.dup_pages;
|
||||
}
|
||||
|
||||
uint64_t norm_mig_bytes_transferred(void)
|
||||
{
|
||||
return acct_info.norm_pages * TARGET_PAGE_SIZE;
|
||||
}
|
||||
|
||||
uint64_t norm_mig_pages_transferred(void)
|
||||
{
|
||||
return acct_info.norm_pages;
|
||||
}
|
||||
|
||||
uint64_t xbzrle_mig_bytes_transferred(void)
|
||||
{
|
||||
return acct_info.xbzrle_bytes;
|
||||
}
|
||||
|
||||
uint64_t xbzrle_mig_pages_transferred(void)
|
||||
{
|
||||
return acct_info.xbzrle_pages;
|
||||
}
|
||||
|
||||
uint64_t xbzrle_mig_pages_cache_miss(void)
|
||||
{
|
||||
return acct_info.xbzrle_cache_miss;
|
||||
}
|
||||
|
||||
uint64_t xbzrle_mig_pages_overflow(void)
|
||||
{
|
||||
return acct_info.xbzrle_overflows;
|
||||
}
|
||||
|
||||
static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
|
||||
int cont, int flag)
|
||||
{
|
||||
qemu_put_be64(f, offset | cont | flag);
|
||||
if (!cont) {
|
||||
qemu_put_byte(f, strlen(block->idstr));
|
||||
qemu_put_buffer(f, (uint8_t *)block->idstr,
|
||||
strlen(block->idstr));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define ENCODING_FLAG_XBZRLE 0x1
|
||||
|
||||
static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
||||
ram_addr_t current_addr, RAMBlock *block,
|
||||
ram_addr_t offset, int cont, bool last_stage)
|
||||
{
|
||||
int encoded_len = 0, bytes_sent = -1;
|
||||
uint8_t *prev_cached_page;
|
||||
|
||||
if (!cache_is_cached(XBZRLE.cache, current_addr)) {
|
||||
if (!last_stage) {
|
||||
cache_insert(XBZRLE.cache, current_addr,
|
||||
g_memdup(current_data, TARGET_PAGE_SIZE));
|
||||
}
|
||||
acct_info.xbzrle_cache_miss++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);
|
||||
|
||||
/* save current buffer into memory */
|
||||
memcpy(XBZRLE.current_buf, current_data, TARGET_PAGE_SIZE);
|
||||
|
||||
/* XBZRLE encoding (if there is no overflow) */
|
||||
encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf,
|
||||
TARGET_PAGE_SIZE, XBZRLE.encoded_buf,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (encoded_len == 0) {
|
||||
DPRINTF("Skipping unmodified page\n");
|
||||
return 0;
|
||||
} else if (encoded_len == -1) {
|
||||
DPRINTF("Overflow\n");
|
||||
acct_info.xbzrle_overflows++;
|
||||
/* update data in the cache */
|
||||
memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we need to update the data in the cache, in order to get the same data */
|
||||
if (!last_stage) {
|
||||
memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);
|
||||
}
|
||||
|
||||
/* Send XBZRLE based compressed page */
|
||||
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE);
|
||||
qemu_put_byte(f, ENCODING_FLAG_XBZRLE);
|
||||
qemu_put_be16(f, encoded_len);
|
||||
qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
|
||||
bytes_sent = encoded_len + 1 + 2;
|
||||
acct_info.xbzrle_pages++;
|
||||
acct_info.xbzrle_bytes += bytes_sent;
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
static RAMBlock *last_block;
|
||||
static ram_addr_t last_offset;
|
||||
static unsigned long *migration_bitmap;
|
||||
static uint64_t migration_dirty_pages;
|
||||
|
||||
static inline bool migration_bitmap_test_and_reset_dirty(MemoryRegion *mr,
|
||||
ram_addr_t offset)
|
||||
{
|
||||
bool ret;
|
||||
int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
|
||||
|
||||
ret = test_and_clear_bit(nr, migration_bitmap);
|
||||
|
||||
if (ret) {
|
||||
migration_dirty_pages--;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
|
||||
ram_addr_t offset)
|
||||
{
|
||||
bool ret;
|
||||
int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
|
||||
|
||||
ret = test_and_set_bit(nr, migration_bitmap);
|
||||
|
||||
if (!ret) {
|
||||
migration_dirty_pages++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void migration_bitmap_sync(void)
|
||||
{
|
||||
RAMBlock *block;
|
||||
ram_addr_t addr;
|
||||
uint64_t num_dirty_pages_init = migration_dirty_pages;
|
||||
MigrationState *s = migrate_get_current();
|
||||
static int64_t start_time;
|
||||
static int64_t num_dirty_pages_period;
|
||||
int64_t end_time;
|
||||
|
||||
if (!start_time) {
|
||||
start_time = qemu_get_clock_ms(rt_clock);
|
||||
}
|
||||
|
||||
trace_migration_bitmap_sync_start();
|
||||
memory_global_sync_dirty_bitmap(get_system_memory());
|
||||
|
||||
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)) {
|
||||
migration_bitmap_set_dirty(block->mr, addr);
|
||||
}
|
||||
}
|
||||
memory_region_reset_dirty(block->mr, 0, block->length,
|
||||
DIRTY_MEMORY_MIGRATION);
|
||||
}
|
||||
trace_migration_bitmap_sync_end(migration_dirty_pages
|
||||
- num_dirty_pages_init);
|
||||
num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init;
|
||||
end_time = qemu_get_clock_ms(rt_clock);
|
||||
|
||||
/* more than 1 second = 1000 millisecons */
|
||||
if (end_time > start_time + 1000) {
|
||||
s->dirty_pages_rate = num_dirty_pages_period * 1000
|
||||
/ (end_time - start_time);
|
||||
start_time = end_time;
|
||||
num_dirty_pages_period = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ram_save_block: Writes a page of memory to the stream f
|
||||
*
|
||||
* Returns: 0: if the page hasn't changed
|
||||
* -1: if there are no more dirty pages
|
||||
* n: the amount of bytes written in other case
|
||||
*/
|
||||
|
||||
static int ram_save_block(QEMUFile *f, bool last_stage)
|
||||
static int ram_save_block(QEMUFile *f)
|
||||
{
|
||||
RAMBlock *block = last_block;
|
||||
ram_addr_t offset = last_offset;
|
||||
int bytes_sent = -1;
|
||||
int bytes_sent = 0;
|
||||
MemoryRegion *mr;
|
||||
ram_addr_t current_addr;
|
||||
|
||||
if (!block)
|
||||
block = QLIST_FIRST(&ram_list.blocks);
|
||||
|
||||
do {
|
||||
mr = block->mr;
|
||||
if (migration_bitmap_test_and_reset_dirty(mr, offset)) {
|
||||
if (memory_region_get_dirty(mr, offset, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
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);
|
||||
|
||||
p = memory_region_get_ram_ptr(mr) + offset;
|
||||
|
||||
if (is_dup_page(p)) {
|
||||
acct_info.dup_pages++;
|
||||
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
|
||||
qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
|
||||
if (!cont) {
|
||||
qemu_put_byte(f, strlen(block->idstr));
|
||||
qemu_put_buffer(f, (uint8_t *)block->idstr,
|
||||
strlen(block->idstr));
|
||||
}
|
||||
qemu_put_byte(f, *p);
|
||||
bytes_sent = 1;
|
||||
} else if (migrate_use_xbzrle()) {
|
||||
current_addr = block->offset + offset;
|
||||
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
|
||||
offset, cont, last_stage);
|
||||
if (!last_stage) {
|
||||
p = get_cached_data(XBZRLE.cache, current_addr);
|
||||
} else {
|
||||
qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE);
|
||||
if (!cont) {
|
||||
qemu_put_byte(f, strlen(block->idstr));
|
||||
qemu_put_buffer(f, (uint8_t *)block->idstr,
|
||||
strlen(block->idstr));
|
||||
}
|
||||
}
|
||||
|
||||
/* either we didn't send yet (we may have had XBZRLE overflow) */
|
||||
if (bytes_sent == -1) {
|
||||
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
|
||||
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
|
||||
bytes_sent = TARGET_PAGE_SIZE;
|
||||
acct_info.norm_pages++;
|
||||
}
|
||||
|
||||
/* if page is unmodified, continue to the next */
|
||||
if (bytes_sent != 0) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
offset += TARGET_PAGE_SIZE;
|
||||
@@ -480,7 +228,20 @@ static uint64_t bytes_transferred;
|
||||
|
||||
static ram_addr_t ram_save_remaining(void)
|
||||
{
|
||||
return migration_dirty_pages;
|
||||
RAMBlock *block;
|
||||
ram_addr_t count = 0;
|
||||
|
||||
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)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t ram_bytes_remaining(void)
|
||||
@@ -533,117 +294,60 @@ static void sort_ram_list(void)
|
||||
g_free(blocks);
|
||||
}
|
||||
|
||||
static void migration_end(void)
|
||||
{
|
||||
if (migration_bitmap) {
|
||||
memory_global_dirty_log_stop();
|
||||
g_free(migration_bitmap);
|
||||
migration_bitmap = NULL;
|
||||
}
|
||||
|
||||
if (XBZRLE.cache) {
|
||||
cache_fini(XBZRLE.cache);
|
||||
g_free(XBZRLE.cache);
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
g_free(XBZRLE.current_buf);
|
||||
g_free(XBZRLE.decoded_buf);
|
||||
XBZRLE.cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ram_migration_cancel(void *opaque)
|
||||
{
|
||||
migration_end();
|
||||
}
|
||||
|
||||
|
||||
static void reset_ram_globals(void)
|
||||
{
|
||||
last_block = NULL;
|
||||
last_offset = 0;
|
||||
sort_ram_list();
|
||||
}
|
||||
|
||||
#define MAX_WAIT 50 /* ms, half buffered_file limit */
|
||||
|
||||
static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||
{
|
||||
RAMBlock *block;
|
||||
int64_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||
|
||||
migration_bitmap = bitmap_new(ram_pages);
|
||||
bitmap_set(migration_bitmap, 0, ram_pages);
|
||||
migration_dirty_pages = ram_pages;
|
||||
|
||||
bytes_transferred = 0;
|
||||
reset_ram_globals();
|
||||
|
||||
if (migrate_use_xbzrle()) {
|
||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
||||
TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.cache) {
|
||||
DPRINTF("Error creating cache\n");
|
||||
return -1;
|
||||
}
|
||||
XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
|
||||
XBZRLE.current_buf = g_malloc(TARGET_PAGE_SIZE);
|
||||
acct_clear();
|
||||
}
|
||||
|
||||
memory_global_dirty_log_start();
|
||||
migration_bitmap_sync();
|
||||
|
||||
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
||||
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
qemu_put_byte(f, strlen(block->idstr));
|
||||
qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
|
||||
qemu_put_be64(f, block->length);
|
||||
}
|
||||
|
||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||
int ram_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
uint64_t bytes_transferred_last;
|
||||
double bwidth = 0;
|
||||
uint64_t expected_time = 0;
|
||||
int ret;
|
||||
int i;
|
||||
uint64_t expected_downtime;
|
||||
MigrationState *s = migrate_get_current();
|
||||
|
||||
if (stage < 0) {
|
||||
memory_global_dirty_log_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
memory_global_sync_dirty_bitmap(get_system_memory());
|
||||
|
||||
if (stage == 1) {
|
||||
RAMBlock *block;
|
||||
bytes_transferred = 0;
|
||||
last_block = NULL;
|
||||
last_offset = 0;
|
||||
sort_ram_list();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory_global_dirty_log_start();
|
||||
|
||||
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
||||
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
qemu_put_byte(f, strlen(block->idstr));
|
||||
qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
|
||||
qemu_put_be64(f, block->length);
|
||||
}
|
||||
}
|
||||
|
||||
bytes_transferred_last = bytes_transferred;
|
||||
bwidth = qemu_get_clock_ns(rt_clock);
|
||||
|
||||
i = 0;
|
||||
while ((ret = qemu_file_rate_limit(f)) == 0) {
|
||||
int bytes_sent;
|
||||
|
||||
bytes_sent = ram_save_block(f, false);
|
||||
/* no more blocks to sent */
|
||||
if (bytes_sent < 0) {
|
||||
bytes_sent = ram_save_block(f);
|
||||
bytes_transferred += bytes_sent;
|
||||
if (bytes_sent == 0) { /* no more blocks */
|
||||
break;
|
||||
}
|
||||
bytes_transferred += bytes_sent;
|
||||
acct_info.iterations++;
|
||||
/* we want to check in the 1st loop, just in case it was the 1st time
|
||||
and we had to sync the dirty bitmap.
|
||||
qemu_get_clock_ns() is a bit expensive, so we only check each some
|
||||
iterations
|
||||
*/
|
||||
if ((i & 63) == 0) {
|
||||
uint64_t t1 = (qemu_get_clock_ns(rt_clock) - bwidth) / 1000000;
|
||||
if (t1 > MAX_WAIT) {
|
||||
DPRINTF("big wait: %" PRIu64 " milliseconds, %d iterations\n",
|
||||
t1, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -653,92 +357,28 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||
bwidth = qemu_get_clock_ns(rt_clock) - bwidth;
|
||||
bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
|
||||
|
||||
/* if we haven't transferred anything this round, force
|
||||
* expected_downtime to a very high value, but without
|
||||
* crashing */
|
||||
/* if we haven't transferred anything this round, force expected_time to a
|
||||
* a very high value, but without crashing */
|
||||
if (bwidth == 0) {
|
||||
bwidth = 0.000001;
|
||||
}
|
||||
|
||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||
|
||||
expected_downtime = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
||||
DPRINTF("ram_save_live: expected(%" PRIu64 ") <= max(" PRIu64 ")?\n",
|
||||
expected_downtime, migrate_max_downtime());
|
||||
|
||||
if (expected_downtime <= migrate_max_downtime()) {
|
||||
migration_bitmap_sync();
|
||||
expected_downtime = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
||||
s->expected_downtime = expected_downtime / 1000000; /* ns -> ms */
|
||||
|
||||
return expected_downtime <= migrate_max_downtime();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||
{
|
||||
migration_bitmap_sync();
|
||||
|
||||
/* try transferring iterative blocks of memory */
|
||||
|
||||
/* flush all remaining blocks regardless of rate limiting */
|
||||
while (true) {
|
||||
if (stage == 3) {
|
||||
int bytes_sent;
|
||||
|
||||
bytes_sent = ram_save_block(f, true);
|
||||
/* no more blocks to sent */
|
||||
if (bytes_sent < 0) {
|
||||
break;
|
||||
/* flush all remaining blocks regardless of rate limiting */
|
||||
while ((bytes_sent = ram_save_block(f)) != 0) {
|
||||
bytes_transferred += bytes_sent;
|
||||
}
|
||||
bytes_transferred += bytes_sent;
|
||||
memory_global_dirty_log_stop();
|
||||
}
|
||||
migration_end();
|
||||
|
||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
||||
|
||||
static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
|
||||
{
|
||||
int ret, rc = 0;
|
||||
unsigned int xh_len;
|
||||
int xh_flags;
|
||||
|
||||
if (!XBZRLE.decoded_buf) {
|
||||
XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE);
|
||||
}
|
||||
|
||||
/* extract RLE header */
|
||||
xh_flags = qemu_get_byte(f);
|
||||
xh_len = qemu_get_be16(f);
|
||||
|
||||
if (xh_flags != ENCODING_FLAG_XBZRLE) {
|
||||
fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xh_len > TARGET_PAGE_SIZE) {
|
||||
fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n");
|
||||
return -1;
|
||||
}
|
||||
/* load data and decode */
|
||||
qemu_get_buffer(f, XBZRLE.decoded_buf, xh_len);
|
||||
|
||||
/* decode RLE */
|
||||
ret = xbzrle_decode_buffer(XBZRLE.decoded_buf, xh_len, host,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
|
||||
rc = -1;
|
||||
} else if (ret > TARGET_PAGE_SIZE) {
|
||||
fprintf(stderr, "Failed to load XBZRLE page - size %d exceeds %d!\n",
|
||||
ret, TARGET_PAGE_SIZE);
|
||||
abort();
|
||||
}
|
||||
|
||||
return rc;
|
||||
return (stage == 2) && (expected_time <= migrate_max_downtime());
|
||||
}
|
||||
|
||||
static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
@@ -771,14 +411,11 @@ static inline void *host_from_stream_offset(QEMUFile *f,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
int flags, ret = 0;
|
||||
int flags;
|
||||
int error;
|
||||
static uint64_t seq_iter;
|
||||
|
||||
seq_iter++;
|
||||
|
||||
if (version_id < 4 || version_id > 4) {
|
||||
return -EINVAL;
|
||||
@@ -808,10 +445,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
if (!strncmp(id, block->idstr, sizeof(id))) {
|
||||
if (block->length != length) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (block->length != length)
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -819,8 +454,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
if (!block) {
|
||||
fprintf(stderr, "Unknown ramblock \"%s\", cannot "
|
||||
"accept migration\n", id);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
total_ram_bytes -= length;
|
||||
@@ -841,8 +475,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
memset(host, ch, TARGET_PAGE_SIZE);
|
||||
#ifndef _WIN32
|
||||
if (ch == 0 &&
|
||||
(!kvm_enabled() || kvm_has_sync_mmu()) &&
|
||||
getpagesize() <= TARGET_PAGE_SIZE) {
|
||||
(!kvm_enabled() || kvm_has_sync_mmu())) {
|
||||
qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED);
|
||||
}
|
||||
#endif
|
||||
@@ -850,46 +483,18 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
void *host;
|
||||
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
|
||||
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
|
||||
if (!migrate_use_xbzrle()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
void *host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (load_xbzrle(f, addr, host) < 0) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
error = qemu_file_get_error(f);
|
||||
if (error) {
|
||||
ret = error;
|
||||
goto done;
|
||||
return error;
|
||||
}
|
||||
} while (!(flags & RAM_SAVE_FLAG_EOS));
|
||||
|
||||
done:
|
||||
DPRINTF("Completed load of VM with exit code %d seq iteration "
|
||||
"%" PRIu64 "\n", ret, seq_iter);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SaveVMHandlers savevm_ram_handlers = {
|
||||
.save_live_setup = ram_save_setup,
|
||||
.save_live_iterate = ram_save_iterate,
|
||||
.save_live_complete = ram_save_complete,
|
||||
.load_state = ram_load,
|
||||
.cancel = ram_migration_cancel,
|
||||
};
|
||||
|
||||
#ifdef HAS_AUDIO
|
||||
struct soundhw {
|
||||
const char *name;
|
||||
@@ -997,20 +602,15 @@ void select_soundhw(const char *optarg)
|
||||
{
|
||||
struct soundhw *c;
|
||||
|
||||
if (is_help_option(optarg)) {
|
||||
if (*optarg == '?') {
|
||||
show_valid_cards:
|
||||
|
||||
#ifdef HAS_AUDIO_CHOICE
|
||||
printf("Valid sound card names (comma separated):\n");
|
||||
for (c = soundhw; c->name; ++c) {
|
||||
printf ("%-11s %s\n", c->name, c->descr);
|
||||
}
|
||||
printf("\n-soundhw all will enable all of the above\n");
|
||||
#else
|
||||
printf("Machine has no user-selectable audio hardware "
|
||||
"(it may or may not have always-present audio hardware).\n");
|
||||
#endif
|
||||
exit(!is_help_option(optarg));
|
||||
exit(*optarg != '?');
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
@@ -1164,13 +764,3 @@ int xen_available(void)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TargetInfo *qmp_query_target(Error **errp)
|
||||
{
|
||||
TargetInfo *info = g_malloc0(sizeof(*info));
|
||||
|
||||
info->arch = TARGET_TYPE;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef QEMU_ARCH_INIT_H
|
||||
#define QEMU_ARCH_INIT_H
|
||||
|
||||
#include "qmp-commands.h"
|
||||
|
||||
enum {
|
||||
QEMU_ARCH_ALL = -1,
|
||||
QEMU_ARCH_ALPHA = 1,
|
||||
@@ -18,8 +16,6 @@ enum {
|
||||
QEMU_ARCH_SH4 = 1024,
|
||||
QEMU_ARCH_SPARC = 2048,
|
||||
QEMU_ARCH_XTENSA = 4096,
|
||||
QEMU_ARCH_OPENRISC = 8192,
|
||||
QEMU_ARCH_UNICORE32 = 0x4000,
|
||||
};
|
||||
|
||||
extern const uint32_t arch_type;
|
||||
@@ -34,6 +30,4 @@ int tcg_available(void);
|
||||
int kvm_available(void);
|
||||
int xen_available(void);
|
||||
|
||||
CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
|
||||
|
||||
#endif
|
||||
@@ -166,20 +166,17 @@ static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong er
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Read the input value from the argument block; fail the semihosting
|
||||
* call if the memory read fails.
|
||||
*/
|
||||
#define GET_ARG(n) do { \
|
||||
if (get_user_ual(arg ## n, args + (n) * 4)) { \
|
||||
return (uint32_t)-1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ARG(n) \
|
||||
({ \
|
||||
target_ulong __arg; \
|
||||
/* FIXME - handle get_user() failure */ \
|
||||
get_user_ual(__arg, args + (n) * 4); \
|
||||
__arg; \
|
||||
})
|
||||
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
|
||||
uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
{
|
||||
target_ulong args;
|
||||
target_ulong arg0, arg1, arg2, arg3;
|
||||
char * s;
|
||||
int nr;
|
||||
uint32_t ret;
|
||||
@@ -194,39 +191,33 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
args = env->regs[1];
|
||||
switch (nr) {
|
||||
case TARGET_SYS_OPEN:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
GET_ARG(2);
|
||||
s = lock_user_string(arg0);
|
||||
if (!s) {
|
||||
if (!(s = lock_user_string(ARG(0))))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
if (arg1 >= 12) {
|
||||
unlock_user(s, arg0, 0);
|
||||
if (ARG(1) >= 12) {
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
if (strcmp(s, ":tt") == 0) {
|
||||
int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
|
||||
unlock_user(s, arg0, 0);
|
||||
int result_fileno = ARG(1) < 4 ? STDIN_FILENO : STDOUT_FILENO;
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return result_fileno;
|
||||
}
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
|
||||
(int)arg2+1, gdb_open_modeflags[arg1]);
|
||||
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];
|
||||
} else {
|
||||
ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
|
||||
ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
|
||||
}
|
||||
unlock_user(s, arg0, 0);
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
case TARGET_SYS_CLOSE:
|
||||
GET_ARG(0);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
|
||||
gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return set_swi_errno(ts, close(arg0));
|
||||
return set_swi_errno(ts, close(ARG(0)));
|
||||
}
|
||||
case TARGET_SYS_WRITEC:
|
||||
{
|
||||
@@ -257,81 +248,67 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
unlock_user(s, args, 0);
|
||||
return ret;
|
||||
case TARGET_SYS_WRITE:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
GET_ARG(2);
|
||||
len = arg2;
|
||||
len = ARG(2);
|
||||
if (use_gdb_syscalls()) {
|
||||
arm_semi_syscall_len = len;
|
||||
gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
|
||||
gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
s = lock_user(VERIFY_READ, arg1, len, 1);
|
||||
if (!s) {
|
||||
if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
ret = set_swi_errno(ts, write(arg0, s, len));
|
||||
unlock_user(s, arg1, 0);
|
||||
ret = set_swi_errno(ts, write(ARG(0), s, len));
|
||||
unlock_user(s, ARG(1), 0);
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return len - ret;
|
||||
}
|
||||
case TARGET_SYS_READ:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
GET_ARG(2);
|
||||
len = arg2;
|
||||
len = ARG(2);
|
||||
if (use_gdb_syscalls()) {
|
||||
arm_semi_syscall_len = len;
|
||||
gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
|
||||
gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
s = lock_user(VERIFY_WRITE, arg1, len, 0);
|
||||
if (!s) {
|
||||
if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
do {
|
||||
ret = set_swi_errno(ts, read(arg0, s, len));
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
unlock_user(s, arg1, len);
|
||||
do
|
||||
ret = set_swi_errno(ts, read(ARG(0), s, len));
|
||||
while (ret == -1 && errno == EINTR);
|
||||
unlock_user(s, ARG(1), len);
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return len - ret;
|
||||
}
|
||||
case TARGET_SYS_READC:
|
||||
/* XXX: Read from debug console. Not implemented. */
|
||||
/* XXX: Read from debug cosole. Not implemented. */
|
||||
return 0;
|
||||
case TARGET_SYS_ISTTY:
|
||||
GET_ARG(0);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
|
||||
gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return isatty(arg0);
|
||||
return isatty(ARG(0));
|
||||
}
|
||||
case TARGET_SYS_SEEK:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
|
||||
gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
|
||||
ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
case TARGET_SYS_FLEN:
|
||||
GET_ARG(0);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
|
||||
arg0, env->regs[13]-64);
|
||||
ARG(0), env->regs[13]-64);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
struct stat buf;
|
||||
ret = set_swi_errno(ts, fstat(arg0, &buf));
|
||||
ret = set_swi_errno(ts, fstat(ARG(0), &buf));
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return buf.st_size;
|
||||
@@ -340,43 +317,35 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
/* XXX: Not implemented. */
|
||||
return -1;
|
||||
case TARGET_SYS_REMOVE:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
|
||||
gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
|
||||
ret = env->regs[0];
|
||||
} else {
|
||||
s = lock_user_string(arg0);
|
||||
if (!s) {
|
||||
if (!(s = lock_user_string(ARG(0))))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
ret = set_swi_errno(ts, remove(s));
|
||||
unlock_user(s, arg0, 0);
|
||||
unlock_user(s, ARG(0), 0);
|
||||
}
|
||||
return ret;
|
||||
case TARGET_SYS_RENAME:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
GET_ARG(2);
|
||||
GET_ARG(3);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
|
||||
arg0, (int)arg1+1, arg2, (int)arg3+1);
|
||||
ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
char *s2;
|
||||
s = lock_user_string(arg0);
|
||||
s2 = lock_user_string(arg2);
|
||||
s = lock_user_string(ARG(0));
|
||||
s2 = lock_user_string(ARG(2));
|
||||
if (!s || !s2)
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
ret = (uint32_t)-1;
|
||||
else
|
||||
ret = set_swi_errno(ts, rename(s, s2));
|
||||
if (s2)
|
||||
unlock_user(s2, arg2, 0);
|
||||
unlock_user(s2, ARG(2), 0);
|
||||
if (s)
|
||||
unlock_user(s, arg0, 0);
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
}
|
||||
case TARGET_SYS_CLOCK:
|
||||
@@ -384,19 +353,15 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
case TARGET_SYS_TIME:
|
||||
return set_swi_errno(ts, time(NULL));
|
||||
case TARGET_SYS_SYSTEM:
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
|
||||
gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
s = lock_user_string(arg0);
|
||||
if (!s) {
|
||||
if (!(s = lock_user_string(ARG(0))))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
ret = set_swi_errno(ts, system(s));
|
||||
unlock_user(s, arg0, 0);
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
}
|
||||
case TARGET_SYS_ERRNO:
|
||||
@@ -410,24 +375,22 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
/* Build a command-line from the original argv.
|
||||
*
|
||||
* The inputs are:
|
||||
* * arg0, pointer to a buffer of at least the size
|
||||
* specified in arg1.
|
||||
* * arg1, size of the buffer pointed to by arg0 in
|
||||
* * ARG(0), pointer to a buffer of at least the size
|
||||
* specified in ARG(1).
|
||||
* * ARG(1), size of the buffer pointed to by ARG(0) in
|
||||
* bytes.
|
||||
*
|
||||
* The outputs are:
|
||||
* * arg0, pointer to null-terminated string of the
|
||||
* * ARG(0), pointer to null-terminated string of the
|
||||
* command line.
|
||||
* * arg1, length of the string pointed to by arg0.
|
||||
* * ARG(1), length of the string pointed to by ARG(0).
|
||||
*/
|
||||
|
||||
char *output_buffer;
|
||||
size_t input_size;
|
||||
size_t input_size = ARG(1);
|
||||
size_t output_size;
|
||||
int status = 0;
|
||||
GET_ARG(0);
|
||||
GET_ARG(1);
|
||||
input_size = arg1;
|
||||
|
||||
/* Compute the size of the output string. */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
output_size = strlen(ts->boot_info->kernel_filename)
|
||||
@@ -451,13 +414,10 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
}
|
||||
|
||||
/* Adjust the command-line length. */
|
||||
if (SET_ARG(1, output_size - 1)) {
|
||||
/* Couldn't write back to argument block */
|
||||
return -1;
|
||||
}
|
||||
SET_ARG(1, output_size - 1);
|
||||
|
||||
/* Lock the buffer on the ARM side. */
|
||||
output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
|
||||
output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0);
|
||||
if (!output_buffer) {
|
||||
return -1;
|
||||
}
|
||||
@@ -489,7 +449,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
out:
|
||||
#endif
|
||||
/* Unlock the buffer on the ARM side. */
|
||||
unlock_user(output_buffer, arg0, output_size);
|
||||
unlock_user(output_buffer, ARG(0), output_size);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -497,7 +457,6 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
{
|
||||
uint32_t *ptr;
|
||||
uint32_t limit;
|
||||
GET_ARG(0);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Some C libraries assume the heap immediately follows .bss, so
|
||||
@@ -518,29 +477,25 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
ts->heap_limit = limit;
|
||||
}
|
||||
|
||||
ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
|
||||
if (!ptr) {
|
||||
if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
ptr[0] = tswap32(ts->heap_base);
|
||||
ptr[1] = tswap32(ts->heap_limit);
|
||||
ptr[2] = tswap32(ts->stack_base);
|
||||
ptr[3] = tswap32(0); /* Stack limit. */
|
||||
unlock_user(ptr, arg0, 16);
|
||||
unlock_user(ptr, ARG(0), 16);
|
||||
#else
|
||||
limit = ram_size;
|
||||
ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
|
||||
if (!ptr) {
|
||||
if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
|
||||
/* FIXME - should this error code be -TARGET_EFAULT ? */
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
/* TODO: Make this use the limit of the loaded application. */
|
||||
ptr[0] = tswap32(limit / 2);
|
||||
ptr[1] = tswap32(limit);
|
||||
ptr[2] = tswap32(limit); /* Stack base */
|
||||
ptr[3] = tswap32(0); /* Stack limit. */
|
||||
unlock_user(ptr, arg0, 16);
|
||||
unlock_user(ptr, ARG(0), 16);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -26,11 +26,13 @@
|
||||
#include "qemu-aio.h"
|
||||
#include "main-loop.h"
|
||||
|
||||
/* Anchor of the list of Bottom Halves belonging to the context */
|
||||
static struct QEMUBH *first_bh;
|
||||
|
||||
/***********************************************************/
|
||||
/* bottom halves (can be seen as timers which expire ASAP) */
|
||||
|
||||
struct QEMUBH {
|
||||
AioContext *ctx;
|
||||
QEMUBHFunc *cb;
|
||||
void *opaque;
|
||||
QEMUBH *next;
|
||||
@@ -39,27 +41,27 @@ struct QEMUBH {
|
||||
bool deleted;
|
||||
};
|
||||
|
||||
QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
|
||||
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
|
||||
{
|
||||
QEMUBH *bh;
|
||||
bh = g_malloc0(sizeof(QEMUBH));
|
||||
bh->ctx = ctx;
|
||||
bh->cb = cb;
|
||||
bh->opaque = opaque;
|
||||
bh->next = ctx->first_bh;
|
||||
ctx->first_bh = bh;
|
||||
bh->next = first_bh;
|
||||
first_bh = bh;
|
||||
return bh;
|
||||
}
|
||||
|
||||
int aio_bh_poll(AioContext *ctx)
|
||||
int qemu_bh_poll(void)
|
||||
{
|
||||
QEMUBH *bh, **bhp, *next;
|
||||
int ret;
|
||||
static int nesting = 0;
|
||||
|
||||
ctx->walking_bh++;
|
||||
nesting++;
|
||||
|
||||
ret = 0;
|
||||
for (bh = ctx->first_bh; bh; bh = next) {
|
||||
for (bh = first_bh; bh; bh = next) {
|
||||
next = bh->next;
|
||||
if (!bh->deleted && bh->scheduled) {
|
||||
bh->scheduled = 0;
|
||||
@@ -70,11 +72,11 @@ int aio_bh_poll(AioContext *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
ctx->walking_bh--;
|
||||
nesting--;
|
||||
|
||||
/* remove deleted bhs */
|
||||
if (!ctx->walking_bh) {
|
||||
bhp = &ctx->first_bh;
|
||||
if (!nesting) {
|
||||
bhp = &first_bh;
|
||||
while (*bhp) {
|
||||
bh = *bhp;
|
||||
if (bh->deleted) {
|
||||
@@ -103,7 +105,8 @@ void qemu_bh_schedule(QEMUBH *bh)
|
||||
return;
|
||||
bh->scheduled = 1;
|
||||
bh->idle = 0;
|
||||
aio_notify(bh->ctx);
|
||||
/* stop the currently executing CPU to execute the BH ASAP */
|
||||
qemu_notify_event();
|
||||
}
|
||||
|
||||
void qemu_bh_cancel(QEMUBH *bh)
|
||||
@@ -117,106 +120,23 @@ void qemu_bh_delete(QEMUBH *bh)
|
||||
bh->deleted = 1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
aio_ctx_prepare(GSource *source, gint *timeout)
|
||||
void qemu_bh_update_timeout(uint32_t *timeout)
|
||||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
QEMUBH *bh;
|
||||
|
||||
for (bh = ctx->first_bh; bh; bh = bh->next) {
|
||||
for (bh = first_bh; bh; bh = bh->next) {
|
||||
if (!bh->deleted && bh->scheduled) {
|
||||
if (bh->idle) {
|
||||
/* idle bottom halves will be polled at least
|
||||
* every 10ms */
|
||||
*timeout = 10;
|
||||
*timeout = MIN(10, *timeout);
|
||||
} else {
|
||||
/* non-idle bottom halves will be executed
|
||||
* immediately */
|
||||
*timeout = 0;
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
aio_ctx_check(GSource *source)
|
||||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
QEMUBH *bh;
|
||||
|
||||
for (bh = ctx->first_bh; bh; bh = bh->next) {
|
||||
if (!bh->deleted && bh->scheduled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return aio_pending(ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
aio_ctx_dispatch(GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
|
||||
assert(callback == NULL);
|
||||
aio_poll(ctx, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
aio_ctx_finalize(GSource *source)
|
||||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL);
|
||||
event_notifier_cleanup(&ctx->notifier);
|
||||
}
|
||||
|
||||
static GSourceFuncs aio_source_funcs = {
|
||||
aio_ctx_prepare,
|
||||
aio_ctx_check,
|
||||
aio_ctx_dispatch,
|
||||
aio_ctx_finalize
|
||||
};
|
||||
|
||||
GSource *aio_get_g_source(AioContext *ctx)
|
||||
{
|
||||
g_source_ref(&ctx->source);
|
||||
return &ctx->source;
|
||||
}
|
||||
|
||||
void aio_notify(AioContext *ctx)
|
||||
{
|
||||
event_notifier_set(&ctx->notifier);
|
||||
}
|
||||
|
||||
AioContext *aio_context_new(void)
|
||||
{
|
||||
AioContext *ctx;
|
||||
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
|
||||
event_notifier_init(&ctx->notifier, false);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_test_and_clear, NULL);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void aio_context_ref(AioContext *ctx)
|
||||
{
|
||||
g_source_ref(&ctx->source);
|
||||
}
|
||||
|
||||
void aio_context_unref(AioContext *ctx)
|
||||
{
|
||||
g_source_unref(&ctx->source);
|
||||
}
|
||||
|
||||
void aio_flush(AioContext *ctx)
|
||||
{
|
||||
while (aio_poll(ctx, true));
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-$(CONFIG_SDL) += sdlaudio.o
|
||||
common-obj-$(CONFIG_OSS) += ossaudio.o
|
||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
common-obj-$(CONFIG_COREAUDIO) += coreaudio.o
|
||||
common-obj-$(CONFIG_ALSA) += alsaaudio.o
|
||||
common-obj-$(CONFIG_DSOUND) += dsoundaudio.o
|
||||
common-obj-$(CONFIG_FMOD) += fmodaudio.o
|
||||
common-obj-$(CONFIG_ESD) += esdaudio.o
|
||||
common-obj-$(CONFIG_PA) += paaudio.o
|
||||
common-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
|
||||
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
|
||||
common-obj-y += wavcapture.o
|
||||
@@ -818,7 +818,6 @@ static int audio_attach_capture (HWVoiceOut *hw)
|
||||
sw->active = hw->enabled;
|
||||
sw->conv = noop_conv;
|
||||
sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
|
||||
sw->vol = nominal_volume;
|
||||
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
|
||||
if (!sw->rate) {
|
||||
dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
|
||||
|
||||
@@ -410,15 +410,15 @@ SW *glue (AUD_open_, TYPE) (
|
||||
SW *old_sw = NULL;
|
||||
#endif
|
||||
|
||||
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
||||
name, as->freq, as->nchannels, as->fmt);
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||
card, name, callback_fn, as);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
||||
name, as->freq, as->nchannels, as->fmt);
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
|
||||
audio_print_settings (as);
|
||||
goto fail;
|
||||
|
||||
@@ -72,7 +72,7 @@ static void winwave_log_mmresult (MMRESULT mr)
|
||||
break;
|
||||
|
||||
case MMSYSERR_NOMEM:
|
||||
str = "Unable to allocate or lock memory";
|
||||
str = "Unable to allocate or locl memory";
|
||||
break;
|
||||
|
||||
case WAVERR_SYNC:
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
common-obj-y += rng.o rng-egd.o
|
||||
common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
* QEMU Random Number Generator Backend
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "qemu/rng.h"
|
||||
#include "qemu-char.h"
|
||||
#include "qerror.h"
|
||||
#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */
|
||||
|
||||
#define TYPE_RNG_EGD "rng-egd"
|
||||
#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
|
||||
|
||||
typedef struct RngEgd
|
||||
{
|
||||
RngBackend parent;
|
||||
|
||||
CharDriverState *chr;
|
||||
char *chr_name;
|
||||
|
||||
GSList *requests;
|
||||
} RngEgd;
|
||||
|
||||
typedef struct RngRequest
|
||||
{
|
||||
EntropyReceiveFunc *receive_entropy;
|
||||
uint8_t *data;
|
||||
void *opaque;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
} RngRequest;
|
||||
|
||||
static void rng_egd_request_entropy(RngBackend *b, size_t size,
|
||||
EntropyReceiveFunc *receive_entropy,
|
||||
void *opaque)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
RngRequest *req;
|
||||
|
||||
req = g_malloc(sizeof(*req));
|
||||
|
||||
req->offset = 0;
|
||||
req->size = size;
|
||||
req->receive_entropy = receive_entropy;
|
||||
req->opaque = opaque;
|
||||
req->data = g_malloc(req->size);
|
||||
|
||||
while (size > 0) {
|
||||
uint8_t header[2];
|
||||
uint8_t len = MIN(size, 255);
|
||||
|
||||
/* synchronous entropy request */
|
||||
header[0] = 0x02;
|
||||
header[1] = len;
|
||||
|
||||
qemu_chr_fe_write(s->chr, header, sizeof(header));
|
||||
|
||||
size -= len;
|
||||
}
|
||||
|
||||
s->requests = g_slist_append(s->requests, req);
|
||||
}
|
||||
|
||||
static void rng_egd_free_request(RngRequest *req)
|
||||
{
|
||||
g_free(req->data);
|
||||
g_free(req);
|
||||
}
|
||||
|
||||
static int rng_egd_chr_can_read(void *opaque)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(opaque);
|
||||
GSList *i;
|
||||
int size = 0;
|
||||
|
||||
for (i = s->requests; i; i = i->next) {
|
||||
RngRequest *req = i->data;
|
||||
size += req->size - req->offset;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(opaque);
|
||||
|
||||
while (size > 0 && s->requests) {
|
||||
RngRequest *req = s->requests->data;
|
||||
int len = MIN(size, req->size - req->offset);
|
||||
|
||||
memcpy(req->data + req->offset, buf, len);
|
||||
req->offset += len;
|
||||
size -= len;
|
||||
|
||||
if (req->offset == req->size) {
|
||||
s->requests = g_slist_remove_link(s->requests, s->requests);
|
||||
|
||||
req->receive_entropy(req->opaque, req->data, req->size);
|
||||
|
||||
rng_egd_free_request(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rng_egd_free_requests(RngEgd *s)
|
||||
{
|
||||
GSList *i;
|
||||
|
||||
for (i = s->requests; i; i = i->next) {
|
||||
rng_egd_free_request(i->data);
|
||||
}
|
||||
|
||||
g_slist_free(s->requests);
|
||||
s->requests = NULL;
|
||||
}
|
||||
|
||||
static void rng_egd_cancel_requests(RngBackend *b)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
|
||||
/* We simply delete the list of pending requests. If there is data in the
|
||||
* queue waiting to be read, this is okay, because there will always be
|
||||
* more data than we requested originally
|
||||
*/
|
||||
rng_egd_free_requests(s);
|
||||
}
|
||||
|
||||
static void rng_egd_opened(RngBackend *b, Error **errp)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
|
||||
if (s->chr_name == NULL) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"chardev", "a valid character device");
|
||||
return;
|
||||
}
|
||||
|
||||
s->chr = qemu_chr_find(s->chr_name);
|
||||
if (s->chr == NULL) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, s->chr_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME we should resubmit pending requests when the CDS reconnects. */
|
||||
qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read,
|
||||
NULL, s);
|
||||
}
|
||||
|
||||
static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
RngBackend *b = RNG_BACKEND(obj);
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
|
||||
if (b->opened) {
|
||||
error_set(errp, QERR_PERMISSION_DENIED);
|
||||
} else {
|
||||
g_free(s->chr_name);
|
||||
s->chr_name = g_strdup(value);
|
||||
}
|
||||
}
|
||||
|
||||
static char *rng_egd_get_chardev(Object *obj, Error **errp)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(obj);
|
||||
|
||||
if (s->chr && s->chr->label) {
|
||||
return g_strdup(s->chr->label);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rng_egd_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "chardev",
|
||||
rng_egd_get_chardev, rng_egd_set_chardev,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void rng_egd_finalize(Object *obj)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(obj);
|
||||
|
||||
if (s->chr) {
|
||||
qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
g_free(s->chr_name);
|
||||
|
||||
rng_egd_free_requests(s);
|
||||
}
|
||||
|
||||
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
rbc->request_entropy = rng_egd_request_entropy;
|
||||
rbc->cancel_requests = rng_egd_cancel_requests;
|
||||
rbc->opened = rng_egd_opened;
|
||||
}
|
||||
|
||||
static TypeInfo rng_egd_info = {
|
||||
.name = TYPE_RNG_EGD,
|
||||
.parent = TYPE_RNG_BACKEND,
|
||||
.instance_size = sizeof(RngEgd),
|
||||
.class_init = rng_egd_class_init,
|
||||
.instance_init = rng_egd_init,
|
||||
.instance_finalize = rng_egd_finalize,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&rng_egd_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* QEMU Random Number Generator Backend
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "qemu/rng-random.h"
|
||||
#include "qemu/rng.h"
|
||||
#include "qerror.h"
|
||||
#include "main-loop.h"
|
||||
|
||||
struct RndRandom
|
||||
{
|
||||
RngBackend parent;
|
||||
|
||||
int fd;
|
||||
char *filename;
|
||||
|
||||
EntropyReceiveFunc *receive_func;
|
||||
void *opaque;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple and incomplete backend to request entropy from /dev/random.
|
||||
*
|
||||
* This backend exposes an additional "filename" property that can be used to
|
||||
* set the filename to use to open the backend.
|
||||
*/
|
||||
|
||||
static void entropy_available(void *opaque)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(opaque);
|
||||
uint8_t buffer[s->size];
|
||||
ssize_t len;
|
||||
|
||||
len = read(s->fd, buffer, s->size);
|
||||
g_assert(len != -1);
|
||||
|
||||
s->receive_func(s->opaque, buffer, len);
|
||||
s->receive_func = NULL;
|
||||
|
||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void rng_random_request_entropy(RngBackend *b, size_t size,
|
||||
EntropyReceiveFunc *receive_entropy,
|
||||
void *opaque)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(b);
|
||||
|
||||
if (s->receive_func) {
|
||||
s->receive_func(s->opaque, NULL, 0);
|
||||
}
|
||||
|
||||
s->receive_func = receive_entropy;
|
||||
s->opaque = opaque;
|
||||
s->size = size;
|
||||
|
||||
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
||||
}
|
||||
|
||||
static void rng_random_opened(RngBackend *b, Error **errp)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(b);
|
||||
|
||||
if (s->filename == NULL) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"filename", "a valid filename");
|
||||
} else {
|
||||
s->fd = open(s->filename, O_RDONLY | O_NONBLOCK);
|
||||
|
||||
if (s->fd == -1) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, s->filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *rng_random_get_filename(Object *obj, Error **errp)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(obj);
|
||||
|
||||
if (s->filename) {
|
||||
return g_strdup(s->filename);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rng_random_set_filename(Object *obj, const char *filename,
|
||||
Error **errp)
|
||||
{
|
||||
RngBackend *b = RNG_BACKEND(obj);
|
||||
RndRandom *s = RNG_RANDOM(obj);
|
||||
|
||||
if (b->opened) {
|
||||
error_set(errp, QERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->filename) {
|
||||
g_free(s->filename);
|
||||
}
|
||||
|
||||
s->filename = g_strdup(filename);
|
||||
}
|
||||
|
||||
static void rng_random_init(Object *obj)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(obj);
|
||||
|
||||
object_property_add_str(obj, "filename",
|
||||
rng_random_get_filename,
|
||||
rng_random_set_filename,
|
||||
NULL);
|
||||
|
||||
s->filename = g_strdup("/dev/random");
|
||||
}
|
||||
|
||||
static void rng_random_finalize(Object *obj)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(obj);
|
||||
|
||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
|
||||
if (s->fd != -1) {
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
g_free(s->filename);
|
||||
}
|
||||
|
||||
static void rng_random_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
rbc->request_entropy = rng_random_request_entropy;
|
||||
rbc->opened = rng_random_opened;
|
||||
}
|
||||
|
||||
static TypeInfo rng_random_info = {
|
||||
.name = TYPE_RNG_RANDOM,
|
||||
.parent = TYPE_RNG_BACKEND,
|
||||
.instance_size = sizeof(RndRandom),
|
||||
.class_init = rng_random_class_init,
|
||||
.instance_init = rng_random_init,
|
||||
.instance_finalize = rng_random_finalize,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&rng_random_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* QEMU Random Number Generator Backend
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "qemu/rng.h"
|
||||
#include "qerror.h"
|
||||
|
||||
void rng_backend_request_entropy(RngBackend *s, size_t size,
|
||||
EntropyReceiveFunc *receive_entropy,
|
||||
void *opaque)
|
||||
{
|
||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||
|
||||
if (k->request_entropy) {
|
||||
k->request_entropy(s, size, receive_entropy, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
void rng_backend_cancel_requests(RngBackend *s)
|
||||
{
|
||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||
|
||||
if (k->cancel_requests) {
|
||||
k->cancel_requests(s);
|
||||
}
|
||||
}
|
||||
|
||||
static bool rng_backend_prop_get_opened(Object *obj, Error **errp)
|
||||
{
|
||||
RngBackend *s = RNG_BACKEND(obj);
|
||||
|
||||
return s->opened;
|
||||
}
|
||||
|
||||
void rng_backend_open(RngBackend *s, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(s), true, "opened", errp);
|
||||
}
|
||||
|
||||
static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
RngBackend *s = RNG_BACKEND(obj);
|
||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||
|
||||
if (value == s->opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value && s->opened) {
|
||||
error_set(errp, QERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (k->opened) {
|
||||
k->opened(s, errp);
|
||||
}
|
||||
|
||||
if (!error_is_set(errp)) {
|
||||
s->opened = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void rng_backend_init(Object *obj)
|
||||
{
|
||||
object_property_add_bool(obj, "opened",
|
||||
rng_backend_prop_get_opened,
|
||||
rng_backend_prop_set_opened,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static TypeInfo rng_backend_info = {
|
||||
.name = TYPE_RNG_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(RngBackend),
|
||||
.instance_init = rng_backend_init,
|
||||
.class_size = sizeof(RngBackendClass),
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&rng_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "balloon.h"
|
||||
#include "trace.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qjson.h"
|
||||
|
||||
static QEMUBalloonEvent *balloon_event_fn;
|
||||
static QEMUBalloonStatus *balloon_stat_fn;
|
||||
@@ -81,19 +80,6 @@ static int qemu_balloon_status(BalloonInfo *info)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void qemu_balloon_changed(int64_t actual)
|
||||
{
|
||||
QObject *data;
|
||||
|
||||
data = qobject_from_jsonf("{ 'actual': %" PRId64 " }",
|
||||
actual);
|
||||
|
||||
monitor_protocol_event(QEVENT_BALLOON_CHANGE, data);
|
||||
|
||||
qobject_decref(data);
|
||||
}
|
||||
|
||||
|
||||
BalloonInfo *qmp_query_balloon(Error **errp)
|
||||
{
|
||||
BalloonInfo *info;
|
||||
|
||||
@@ -24,6 +24,4 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
|
||||
QEMUBalloonStatus *stat_func, void *opaque);
|
||||
void qemu_remove_balloon_handler(void *opaque);
|
||||
|
||||
void qemu_balloon_changed(int64_t actual);
|
||||
|
||||
#endif
|
||||
@@ -114,10 +114,10 @@ static inline unsigned long ffz(unsigned long word)
|
||||
* @nr: the bit to set
|
||||
* @addr: the address to start counting from
|
||||
*/
|
||||
static inline void set_bit(int nr, unsigned long *addr)
|
||||
static inline void set_bit(int nr, volatile unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
|
||||
|
||||
*p |= mask;
|
||||
}
|
||||
@@ -127,10 +127,10 @@ static inline void set_bit(int nr, unsigned long *addr)
|
||||
* @nr: Bit to clear
|
||||
* @addr: Address to start counting from
|
||||
*/
|
||||
static inline void clear_bit(int nr, unsigned long *addr)
|
||||
static inline void clear_bit(int nr, volatile unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
|
||||
|
||||
*p &= ~mask;
|
||||
}
|
||||
@@ -140,10 +140,10 @@ static inline void clear_bit(int nr, unsigned long *addr)
|
||||
* @nr: Bit to change
|
||||
* @addr: Address to start counting from
|
||||
*/
|
||||
static inline void change_bit(int nr, unsigned long *addr)
|
||||
static inline void change_bit(int nr, volatile unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
|
||||
|
||||
*p ^= mask;
|
||||
}
|
||||
@@ -153,10 +153,10 @@ static inline void change_bit(int nr, unsigned long *addr)
|
||||
* @nr: Bit to set
|
||||
* @addr: Address to count from
|
||||
*/
|
||||
static inline int test_and_set_bit(int nr, unsigned long *addr)
|
||||
static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
|
||||
unsigned long old = *p;
|
||||
|
||||
*p = old | mask;
|
||||
@@ -168,10 +168,10 @@ static inline int test_and_set_bit(int nr, unsigned long *addr)
|
||||
* @nr: Bit to clear
|
||||
* @addr: Address to count from
|
||||
*/
|
||||
static inline int test_and_clear_bit(int nr, unsigned long *addr)
|
||||
static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
|
||||
unsigned long old = *p;
|
||||
|
||||
*p = old & ~mask;
|
||||
@@ -183,10 +183,10 @@ static inline int test_and_clear_bit(int nr, unsigned long *addr)
|
||||
* @nr: Bit to change
|
||||
* @addr: Address to count from
|
||||
*/
|
||||
static inline int test_and_change_bit(int nr, unsigned long *addr)
|
||||
static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
|
||||
unsigned long old = *p;
|
||||
|
||||
*p = old ^ mask;
|
||||
@@ -198,7 +198,7 @@ static inline int test_and_change_bit(int nr, unsigned long *addr)
|
||||
* @nr: bit number to test
|
||||
* @addr: Address to start counting from
|
||||
*/
|
||||
static inline int test_bit(int nr, const unsigned long *addr)
|
||||
static inline int test_bit(int nr, const volatile unsigned long *addr)
|
||||
{
|
||||
return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
|
||||
}
|
||||
@@ -269,94 +269,4 @@ static inline unsigned long hweight_long(unsigned long w)
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* extract32:
|
||||
* @value: the value to extract the bit field from
|
||||
* @start: the lowest bit in the bit field (numbered from 0)
|
||||
* @length: the length of the bit field
|
||||
*
|
||||
* Extract from the 32 bit input @value the bit field specified by the
|
||||
* @start and @length parameters, and return it. The bit field must
|
||||
* lie entirely within the 32 bit word. It is valid to request that
|
||||
* all 32 bits are returned (ie @length 32 and @start 0).
|
||||
*
|
||||
* Returns: the value of the bit field extracted from the input value.
|
||||
*/
|
||||
static inline uint32_t extract32(uint32_t value, int start, int length)
|
||||
{
|
||||
assert(start >= 0 && length > 0 && length <= 32 - start);
|
||||
return (value >> start) & (~0U >> (32 - length));
|
||||
}
|
||||
|
||||
/**
|
||||
* extract64:
|
||||
* @value: the value to extract the bit field from
|
||||
* @start: the lowest bit in the bit field (numbered from 0)
|
||||
* @length: the length of the bit field
|
||||
*
|
||||
* Extract from the 64 bit input @value the bit field specified by the
|
||||
* @start and @length parameters, and return it. The bit field must
|
||||
* lie entirely within the 64 bit word. It is valid to request that
|
||||
* all 64 bits are returned (ie @length 64 and @start 0).
|
||||
*
|
||||
* Returns: the value of the bit field extracted from the input value.
|
||||
*/
|
||||
static inline uint64_t extract64(uint64_t value, int start, int length)
|
||||
{
|
||||
assert(start >= 0 && length > 0 && length <= 64 - start);
|
||||
return (value >> start) & (~0ULL >> (64 - length));
|
||||
}
|
||||
|
||||
/**
|
||||
* deposit32:
|
||||
* @value: initial value to insert bit field into
|
||||
* @start: the lowest bit in the bit field (numbered from 0)
|
||||
* @length: the length of the bit field
|
||||
* @fieldval: the value to insert into the bit field
|
||||
*
|
||||
* Deposit @fieldval into the 32 bit @value at the bit field specified
|
||||
* by the @start and @length parameters, and return the modified
|
||||
* @value. Bits of @value outside the bit field are not modified.
|
||||
* Bits of @fieldval above the least significant @length bits are
|
||||
* ignored. The bit field must lie entirely within the 32 bit word.
|
||||
* It is valid to request that all 32 bits are modified (ie @length
|
||||
* 32 and @start 0).
|
||||
*
|
||||
* Returns: the modified @value.
|
||||
*/
|
||||
static inline uint32_t deposit32(uint32_t value, int start, int length,
|
||||
uint32_t fieldval)
|
||||
{
|
||||
uint32_t mask;
|
||||
assert(start >= 0 && length > 0 && length <= 32 - start);
|
||||
mask = (~0U >> (32 - length)) << start;
|
||||
return (value & ~mask) | ((fieldval << start) & mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* deposit64:
|
||||
* @value: initial value to insert bit field into
|
||||
* @start: the lowest bit in the bit field (numbered from 0)
|
||||
* @length: the length of the bit field
|
||||
* @fieldval: the value to insert into the bit field
|
||||
*
|
||||
* Deposit @fieldval into the 64 bit @value at the bit field specified
|
||||
* by the @start and @length parameters, and return the modified
|
||||
* @value. Bits of @value outside the bit field are not modified.
|
||||
* Bits of @fieldval above the least significant @length bits are
|
||||
* ignored. The bit field must lie entirely within the 64 bit word.
|
||||
* It is valid to request that all 64 bits are modified (ie @length
|
||||
* 64 and @start 0).
|
||||
*
|
||||
* Returns: the modified @value.
|
||||
*/
|
||||
static inline uint64_t deposit64(uint64_t value, int start, int length,
|
||||
uint64_t fieldval)
|
||||
{
|
||||
uint64_t mask;
|
||||
assert(start >= 0 && length > 0 && length <= 64 - start);
|
||||
mask = (~0ULL >> (64 - length)) << start;
|
||||
return (value & ~mask) | ((fieldval << start) & mask);
|
||||
}
|
||||
|
||||
#endif
|
||||
+76
-127
@@ -423,23 +423,20 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
|
||||
error:
|
||||
DPRINTF("Error reading sector %" PRId64 "\n", sector);
|
||||
qemu_file_set_error(f, ret);
|
||||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return value:
|
||||
* 0: too much data for max_downtime
|
||||
* 1: few enough data for max_downtime
|
||||
*/
|
||||
static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
int ret = 1;
|
||||
int ret = 0;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
ret = mig_save_device_dirty(f, bmds, is_async);
|
||||
if (ret <= 0) {
|
||||
if (mig_save_device_dirty(f, bmds, is_async) == 0) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -447,10 +444,9 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flush_blks(QEMUFile *f)
|
||||
static void flush_blks(QEMUFile* f)
|
||||
{
|
||||
BlkMigBlock *blk;
|
||||
int ret = 0;
|
||||
|
||||
DPRINTF("%s Enter submitted %d read_done %d transferred %d\n",
|
||||
__FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
|
||||
@@ -461,7 +457,7 @@ static int flush_blks(QEMUFile *f)
|
||||
break;
|
||||
}
|
||||
if (blk->ret < 0) {
|
||||
ret = blk->ret;
|
||||
qemu_file_set_error(f, blk->ret);
|
||||
break;
|
||||
}
|
||||
blk_send(f, blk);
|
||||
@@ -478,7 +474,6 @@ static int flush_blks(QEMUFile *f)
|
||||
DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
|
||||
block_mig_state.submitted, block_mig_state.read_done,
|
||||
block_mig_state.transferred);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int64_t get_remaining_dirty(void)
|
||||
@@ -524,8 +519,6 @@ static void blk_mig_cleanup(void)
|
||||
BlkMigDevState *bmds;
|
||||
BlkMigBlock *blk;
|
||||
|
||||
bdrv_drain_all();
|
||||
|
||||
set_dirty_tracking(0);
|
||||
|
||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||
@@ -543,24 +536,34 @@ static void blk_mig_cleanup(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void block_migration_cancel(void *opaque)
|
||||
{
|
||||
blk_mig_cleanup();
|
||||
}
|
||||
|
||||
static int block_save_setup(QEMUFile *f, void *opaque)
|
||||
static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DPRINTF("Enter save live setup submitted %d transferred %d\n",
|
||||
block_mig_state.submitted, block_mig_state.transferred);
|
||||
DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
|
||||
stage, block_mig_state.submitted, block_mig_state.transferred);
|
||||
|
||||
init_blk_migration(f);
|
||||
if (stage < 0) {
|
||||
blk_mig_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* start track dirty blocks */
|
||||
set_dirty_tracking(1);
|
||||
if (block_mig_state.blk_enable != 1) {
|
||||
/* no need to migrate storage */
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = flush_blks(f);
|
||||
if (stage == 1) {
|
||||
init_blk_migration(f);
|
||||
|
||||
/* start track dirty blocks */
|
||||
set_dirty_tracking(1);
|
||||
}
|
||||
|
||||
flush_blks(f);
|
||||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
@@ -568,95 +571,56 @@ static int block_save_setup(QEMUFile *f, void *opaque)
|
||||
|
||||
blk_mig_reset_dirty_cursor();
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int block_save_iterate(QEMUFile *f, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DPRINTF("Enter save live iterate submitted %d transferred %d\n",
|
||||
block_mig_state.submitted, block_mig_state.transferred);
|
||||
|
||||
ret = flush_blks(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_mig_reset_dirty_cursor();
|
||||
|
||||
/* control the rate of transfer */
|
||||
while ((block_mig_state.submitted +
|
||||
block_mig_state.read_done) * BLOCK_SIZE <
|
||||
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) {
|
||||
/* finished saving bulk on all devices */
|
||||
block_mig_state.bulk_completed = 1;
|
||||
}
|
||||
} else {
|
||||
ret = blk_mig_save_dirty_block(f, 1);
|
||||
if (ret != 0) {
|
||||
/* no more dirty blocks */
|
||||
break;
|
||||
if (stage == 2) {
|
||||
/* control the rate of transfer */
|
||||
while ((block_mig_state.submitted +
|
||||
block_mig_state.read_done) * BLOCK_SIZE <
|
||||
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) {
|
||||
/* finished saving bulk on all devices */
|
||||
block_mig_state.bulk_completed = 1;
|
||||
}
|
||||
} else {
|
||||
if (blk_mig_save_dirty_block(f, 1) == 0) {
|
||||
/* no more dirty blocks */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
|
||||
flush_blks(f);
|
||||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = flush_blks(f);
|
||||
if (ret) {
|
||||
if (stage == 3) {
|
||||
/* we know for sure that save bulk is completed and
|
||||
all async read completed */
|
||||
assert(block_mig_state.submitted == 0);
|
||||
|
||||
while (blk_mig_save_dirty_block(f, 0) != 0);
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
|
||||
/* report completion */
|
||||
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
||||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
DPRINTF("Block migration completed\n");
|
||||
}
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
return is_stage2_completed();
|
||||
}
|
||||
|
||||
static int block_save_complete(QEMUFile *f, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DPRINTF("Enter save live complete submitted %d transferred %d\n",
|
||||
block_mig_state.submitted, block_mig_state.transferred);
|
||||
|
||||
ret = flush_blks(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_mig_reset_dirty_cursor();
|
||||
|
||||
/* we know for sure that save bulk is completed and
|
||||
all async read completed */
|
||||
assert(block_mig_state.submitted == 0);
|
||||
|
||||
do {
|
||||
ret = blk_mig_save_dirty_block(f, 0);
|
||||
} while (ret == 0);
|
||||
|
||||
blk_mig_cleanup();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
/* report completion */
|
||||
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
||||
|
||||
DPRINTF("Block migration completed\n");
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
return 0;
|
||||
return ((stage == 2) && is_stage2_completed());
|
||||
}
|
||||
|
||||
static int block_load(QEMUFile *f, void *opaque, int version_id)
|
||||
@@ -736,35 +700,20 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_set_params(const MigrationParams *params, void *opaque)
|
||||
static void block_set_params(int blk_enable, int shared_base, void *opaque)
|
||||
{
|
||||
block_mig_state.blk_enable = params->blk;
|
||||
block_mig_state.shared_base = params->shared;
|
||||
block_mig_state.blk_enable = blk_enable;
|
||||
block_mig_state.shared_base = shared_base;
|
||||
|
||||
/* shared base means that blk_enable = 1 */
|
||||
block_mig_state.blk_enable |= params->shared;
|
||||
block_mig_state.blk_enable |= shared_base;
|
||||
}
|
||||
|
||||
static bool block_is_active(void *opaque)
|
||||
{
|
||||
return block_mig_state.blk_enable == 1;
|
||||
}
|
||||
|
||||
SaveVMHandlers savevm_block_handlers = {
|
||||
.set_params = block_set_params,
|
||||
.save_live_setup = block_save_setup,
|
||||
.save_live_iterate = block_save_iterate,
|
||||
.save_live_complete = block_save_complete,
|
||||
.load_state = block_load,
|
||||
.cancel = block_migration_cancel,
|
||||
.is_active = block_is_active,
|
||||
};
|
||||
|
||||
void blk_mig_init(void)
|
||||
{
|
||||
QSIMPLEQ_INIT(&block_mig_state.bmds_list);
|
||||
QSIMPLEQ_INIT(&block_mig_state.blk_list);
|
||||
|
||||
register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers,
|
||||
&block_mig_state);
|
||||
register_savevm_live(NULL, "block", 0, 1, block_set_params,
|
||||
block_save_live, NULL, block_load, &block_mig_state);
|
||||
}
|
||||
@@ -6,11 +6,9 @@
|
||||
#include "qemu-option.h"
|
||||
#include "qemu-coroutine.h"
|
||||
#include "qobject.h"
|
||||
#include "qapi-types.h"
|
||||
|
||||
/* block.c */
|
||||
typedef struct BlockDriver BlockDriver;
|
||||
typedef struct BlockJob BlockJob;
|
||||
|
||||
typedef struct BlockDriverInfo {
|
||||
/* in bytes, 0 if irrelevant */
|
||||
@@ -81,8 +79,6 @@ typedef struct BlockDevOps {
|
||||
#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_CHECK 0x1000 /* open solely for consistency check */
|
||||
#define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */
|
||||
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
|
||||
|
||||
@@ -91,23 +87,21 @@ typedef struct BlockDevOps {
|
||||
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
|
||||
|
||||
typedef enum {
|
||||
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
|
||||
BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC,
|
||||
BLOCK_ERR_STOP_ANY
|
||||
} BlockErrorAction;
|
||||
|
||||
typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
||||
|
||||
typedef struct BDRVReopenState {
|
||||
BlockDriverState *bs;
|
||||
int flags;
|
||||
void *opaque;
|
||||
} BDRVReopenState;
|
||||
|
||||
typedef enum {
|
||||
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
|
||||
} BlockQMPEventAction;
|
||||
|
||||
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_info_print(Monitor *mon, const QObject *data);
|
||||
void bdrv_info(Monitor *mon, QObject **ret_data);
|
||||
void bdrv_stats_print(Monitor *mon, const QObject *data);
|
||||
@@ -128,24 +122,13 @@ 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_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
|
||||
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);
|
||||
int bdrv_open_backing_file(BlockDriverState *bs);
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
BlockDriver *drv);
|
||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
BlockDriverState *bs, int flags);
|
||||
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
|
||||
int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp);
|
||||
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp);
|
||||
void bdrv_reopen_commit(BDRVReopenState *reopen_state);
|
||||
void bdrv_reopen_abort(BDRVReopenState *reopen_state);
|
||||
void bdrv_close(BlockDriverState *bs);
|
||||
void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify);
|
||||
int bdrv_attach_dev(BlockDriverState *bs, void *dev);
|
||||
void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev);
|
||||
void bdrv_detach_dev(BlockDriverState *bs, void *dev);
|
||||
@@ -158,8 +141,6 @@ bool bdrv_dev_is_tray_open(BlockDriverState *bs);
|
||||
bool bdrv_dev_is_medium_locked(BlockDriverState *bs);
|
||||
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors);
|
||||
int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors);
|
||||
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
int bdrv_pread(BlockDriverState *bs, int64_t offset,
|
||||
@@ -184,44 +165,28 @@ 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);
|
||||
int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
||||
const char *backing_file);
|
||||
int bdrv_get_backing_file_depth(BlockDriverState *bs);
|
||||
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);
|
||||
int bdrv_change_backing_file(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt);
|
||||
void bdrv_register(BlockDriver *bdrv);
|
||||
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
||||
BlockDriverState *base);
|
||||
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
|
||||
BlockDriverState *bs);
|
||||
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
|
||||
|
||||
|
||||
typedef struct BdrvCheckResult {
|
||||
int corruptions;
|
||||
int leaks;
|
||||
int check_errors;
|
||||
int corruptions_fixed;
|
||||
int leaks_fixed;
|
||||
BlockFragInfo bfi;
|
||||
} BdrvCheckResult;
|
||||
|
||||
typedef enum {
|
||||
BDRV_FIX_LEAKS = 1,
|
||||
BDRV_FIX_ERRORS = 2,
|
||||
} BdrvCheckMode;
|
||||
|
||||
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
|
||||
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
|
||||
|
||||
/* async block I/O */
|
||||
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
|
||||
@@ -279,21 +244,47 @@ int bdrv_has_zero_init(BlockDriverState *bs);
|
||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
int *pnum);
|
||||
|
||||
void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error,
|
||||
BlockdevOnError on_write_error);
|
||||
BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read);
|
||||
BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error);
|
||||
void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action,
|
||||
bool is_read, int error);
|
||||
#define BIOS_ATA_TRANSLATION_AUTO 0
|
||||
#define BIOS_ATA_TRANSLATION_NONE 1
|
||||
#define BIOS_ATA_TRANSLATION_LBA 2
|
||||
#define BIOS_ATA_TRANSLATION_LARGE 3
|
||||
#define BIOS_ATA_TRANSLATION_RECHS 4
|
||||
|
||||
void bdrv_set_geometry_hint(BlockDriverState *bs,
|
||||
int cyls, int heads, int secs);
|
||||
void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
|
||||
void bdrv_get_geometry_hint(BlockDriverState *bs,
|
||||
int *pcyls, int *pheads, int *psecs);
|
||||
typedef enum FDriveType {
|
||||
FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
|
||||
FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
|
||||
FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
|
||||
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);
|
||||
int bdrv_get_translation_hint(BlockDriverState *bs);
|
||||
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
|
||||
BlockErrorAction on_write_error);
|
||||
BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read);
|
||||
int bdrv_is_read_only(BlockDriverState *bs);
|
||||
int bdrv_is_sg(BlockDriverState *bs);
|
||||
int bdrv_enable_write_cache(BlockDriverState *bs);
|
||||
void bdrv_set_enable_write_cache(BlockDriverState *bs, bool wce);
|
||||
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);
|
||||
const char *bdrv_get_format_name(BlockDriverState *bs);
|
||||
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
|
||||
BlockDriverState *bdrv_find(const char *name);
|
||||
BlockDriverState *bdrv_next(BlockDriverState *bs);
|
||||
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
|
||||
@@ -305,7 +296,6 @@ int bdrv_query_missing_keys(void);
|
||||
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
|
||||
void *opaque);
|
||||
const char *bdrv_get_device_name(BlockDriverState *bs);
|
||||
int bdrv_get_flags(BlockDriverState *bs);
|
||||
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
@@ -315,8 +305,6 @@ 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);
|
||||
BlockInfo *bdrv_query_info(BlockDriverState *s);
|
||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
||||
int bdrv_can_snapshot(BlockDriverState *bs);
|
||||
int bdrv_is_snapshot(BlockDriverState *bs);
|
||||
BlockDriverState *bdrv_snapshots(void);
|
||||
@@ -354,9 +342,8 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
||||
|
||||
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
|
||||
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector);
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
||||
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
||||
int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector);
|
||||
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);
|
||||
@@ -395,7 +382,9 @@ typedef enum {
|
||||
BLKDBG_L2_ALLOC_COW_READ,
|
||||
BLKDBG_L2_ALLOC_WRITE,
|
||||
|
||||
BLKDBG_READ,
|
||||
BLKDBG_READ_AIO,
|
||||
BLKDBG_READ_BACKING,
|
||||
BLKDBG_READ_BACKING_AIO,
|
||||
BLKDBG_READ_COMPRESSED,
|
||||
|
||||
@@ -431,4 +420,43 @@ typedef enum {
|
||||
#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt)
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event);
|
||||
|
||||
|
||||
/* Convenience for block device models */
|
||||
|
||||
typedef struct BlockConf {
|
||||
BlockDriverState *bs;
|
||||
uint16_t physical_block_size;
|
||||
uint16_t logical_block_size;
|
||||
uint16_t min_io_size;
|
||||
uint32_t opt_io_size;
|
||||
int32_t bootindex;
|
||||
uint32_t discard_granularity;
|
||||
} BlockConf;
|
||||
|
||||
static inline unsigned int get_physical_block_exp(BlockConf *conf)
|
||||
{
|
||||
unsigned int exp = 0, size;
|
||||
|
||||
for (size = conf->physical_block_size;
|
||||
size > conf->logical_block_size;
|
||||
size >>= 1) {
|
||||
exp++;
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
#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("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), \
|
||||
DEFINE_PROP_UINT32("discard_granularity", _state, \
|
||||
_conf.discard_granularity, 0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o
|
||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
|
||||
ifeq ($(CONFIG_POSIX),y)
|
||||
block-obj-y += nbd.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
block-obj-$(CONFIG_CURL) += curl.o
|
||||
block-obj-$(CONFIG_RBD) += rbd.o
|
||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||
endif
|
||||
|
||||
common-obj-y += stream.o
|
||||
common-obj-y += commit.o
|
||||
common-obj-y += mirror.o
|
||||
+48
-65
@@ -26,11 +26,24 @@
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
typedef struct BlkdebugVars {
|
||||
int state;
|
||||
int new_state;
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
|
||||
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
|
||||
|
||||
/* If inject_errno != 0, an error is injected for requests */
|
||||
int inject_errno;
|
||||
|
||||
/* Decides if all future requests fail (false) or only the next one and
|
||||
* after the next request inject_errno is reset to 0 (true) */
|
||||
bool inject_once;
|
||||
|
||||
/* Decides if aio_readv/writev fails right away (true) or returns an error
|
||||
* return value only in the callback (false) */
|
||||
bool inject_immediately;
|
||||
} BlkdebugVars;
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
BlkdebugVars vars;
|
||||
QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
|
||||
} BDRVBlkdebugState;
|
||||
|
||||
typedef struct BlkdebugAIOCB {
|
||||
@@ -41,7 +54,7 @@ typedef struct BlkdebugAIOCB {
|
||||
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
|
||||
|
||||
static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
static AIOPool blkdebug_aio_pool = {
|
||||
.aiocb_size = sizeof(BlkdebugAIOCB),
|
||||
.cancel = blkdebug_aio_cancel,
|
||||
};
|
||||
@@ -60,14 +73,12 @@ typedef struct BlkdebugRule {
|
||||
int error;
|
||||
int immediately;
|
||||
int once;
|
||||
int64_t sector;
|
||||
} inject;
|
||||
struct {
|
||||
int new_state;
|
||||
} set_state;
|
||||
} options;
|
||||
QLIST_ENTRY(BlkdebugRule) next;
|
||||
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
|
||||
} BlkdebugRule;
|
||||
|
||||
static QemuOptsList inject_error_opts = {
|
||||
@@ -86,10 +97,6 @@ static QemuOptsList inject_error_opts = {
|
||||
.name = "errno",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{
|
||||
.name = "sector",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{
|
||||
.name = "once",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
@@ -140,7 +147,9 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||
[BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read",
|
||||
[BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write",
|
||||
|
||||
[BLKDBG_READ] = "read",
|
||||
[BLKDBG_READ_AIO] = "read_aio",
|
||||
[BLKDBG_READ_BACKING] = "read_backing",
|
||||
[BLKDBG_READ_BACKING_AIO] = "read_backing_aio",
|
||||
[BLKDBG_READ_COMPRESSED] = "read_compressed",
|
||||
|
||||
@@ -219,7 +228,6 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0);
|
||||
rule->options.inject.immediately =
|
||||
qemu_opt_get_bool(opts, "immediately", 0);
|
||||
rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
|
||||
break;
|
||||
|
||||
case ACTION_SET_STATE:
|
||||
@@ -294,7 +302,7 @@ static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
filename = c + 1;
|
||||
|
||||
/* Set initial state */
|
||||
s->state = 1;
|
||||
s->vars.state = 1;
|
||||
|
||||
/* Open the backing file */
|
||||
ret = bdrv_file_open(&bs->file, filename, flags);
|
||||
@@ -320,22 +328,22 @@ static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
int error = rule->options.inject.error;
|
||||
int error = s->vars.inject_errno;
|
||||
struct BlkdebugAIOCB *acb;
|
||||
QEMUBH *bh;
|
||||
|
||||
if (rule->options.inject.once) {
|
||||
QSIMPLEQ_INIT(&s->active_rules);
|
||||
if (s->vars.inject_once) {
|
||||
s->vars.inject_errno = 0;
|
||||
}
|
||||
|
||||
if (rule->options.inject.immediately) {
|
||||
if (s->vars.inject_immediately) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque);
|
||||
acb->ret = -error;
|
||||
|
||||
bh = qemu_bh_new(error_callback_bh, acb);
|
||||
@@ -350,21 +358,14 @@ static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
if (rule->options.inject.sector == -1 ||
|
||||
(rule->options.inject.sector >= sector_num &&
|
||||
rule->options.inject.sector < sector_num + nb_sectors)) {
|
||||
break;
|
||||
}
|
||||
if (s->vars.inject_errno) {
|
||||
return inject_error(bs, cb, opaque);
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
BlockDriverAIOCB *acb =
|
||||
bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
return acb;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
@@ -372,21 +373,14 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
if (rule->options.inject.sector == -1 ||
|
||||
(rule->options.inject.sector >= sector_num &&
|
||||
rule->options.inject.sector < sector_num + nb_sectors)) {
|
||||
break;
|
||||
}
|
||||
if (s->vars.inject_errno) {
|
||||
return inject_error(bs, cb, opaque);
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
BlockDriverAIOCB *acb =
|
||||
bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
return acb;
|
||||
}
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
@@ -403,52 +397,42 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
bool injected)
|
||||
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
BlkdebugVars *old_vars)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugVars *vars = &s->vars;
|
||||
|
||||
/* Only process rules for the current state */
|
||||
if (rule->state && rule->state != s->state) {
|
||||
return injected;
|
||||
if (rule->state && rule->state != old_vars->state) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Take the action */
|
||||
switch (rule->action) {
|
||||
case ACTION_INJECT_ERROR:
|
||||
if (!injected) {
|
||||
QSIMPLEQ_INIT(&s->active_rules);
|
||||
injected = true;
|
||||
}
|
||||
QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
|
||||
vars->inject_errno = rule->options.inject.error;
|
||||
vars->inject_once = rule->options.inject.once;
|
||||
vars->inject_immediately = rule->options.inject.immediately;
|
||||
break;
|
||||
|
||||
case ACTION_SET_STATE:
|
||||
s->new_state = rule->options.set_state.new_state;
|
||||
vars->state = rule->options.set_state.new_state;
|
||||
break;
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
bool injected;
|
||||
BlkdebugVars old_vars = s->vars;
|
||||
|
||||
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
|
||||
|
||||
injected = false;
|
||||
s->new_state = s->state;
|
||||
QLIST_FOREACH(rule, &s->rules[event], next) {
|
||||
injected = process_rule(bs, rule, injected);
|
||||
process_rule(bs, rule, &old_vars);
|
||||
}
|
||||
s->state = s->new_state;
|
||||
}
|
||||
|
||||
static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file);
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkdebug = {
|
||||
@@ -459,7 +443,6 @@ static BlockDriver bdrv_blkdebug = {
|
||||
|
||||
.bdrv_file_open = blkdebug_open,
|
||||
.bdrv_close = blkdebug_close,
|
||||
.bdrv_getlength = blkdebug_getlength,
|
||||
|
||||
.bdrv_aio_readv = blkdebug_aio_readv,
|
||||
.bdrv_aio_writev = blkdebug_aio_writev,
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo blkverify_aiocb_info = {
|
||||
static AIOPool blkverify_aio_pool = {
|
||||
.aiocb_size = sizeof(BlkverifyAIOCB),
|
||||
.cancel = blkverify_aio_cancel,
|
||||
};
|
||||
@@ -233,7 +233,7 @@ static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque);
|
||||
BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->bh = NULL;
|
||||
acb->is_write = is_write;
|
||||
|
||||
-259
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* Live block commit
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2012
|
||||
*
|
||||
* Authors:
|
||||
* Jeff Cody <jcody@redhat.com>
|
||||
* Based on stream.c by Stefan Hajnoczi
|
||||
*
|
||||
* 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"
|
||||
#include "blockjob.h"
|
||||
#include "qemu/ratelimit.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.
|
||||
*/
|
||||
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct CommitBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *active;
|
||||
BlockDriverState *top;
|
||||
BlockDriverState *base;
|
||||
BlockdevOnError on_error;
|
||||
int base_flags;
|
||||
int orig_overlay_flags;
|
||||
} CommitBlockJob;
|
||||
|
||||
static int coroutine_fn commit_populate(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
void *buf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = bdrv_read(bs, sector_num, buf, nb_sectors);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_write(base, sector_num, buf, nb_sectors);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coroutine_fn commit_run(void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = opaque;
|
||||
BlockDriverState *active = s->active;
|
||||
BlockDriverState *top = s->top;
|
||||
BlockDriverState *base = s->base;
|
||||
BlockDriverState *overlay_bs = NULL;
|
||||
int64_t sector_num, end;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
void *buf;
|
||||
int bytes_written = 0;
|
||||
int64_t base_len;
|
||||
|
||||
ret = s->common.len = bdrv_getlength(top);
|
||||
|
||||
|
||||
if (s->common.len < 0) {
|
||||
goto exit_restore_reopen;
|
||||
}
|
||||
|
||||
ret = base_len = bdrv_getlength(base);
|
||||
if (base_len < 0) {
|
||||
goto exit_restore_reopen;
|
||||
}
|
||||
|
||||
if (base_len < s->common.len) {
|
||||
ret = bdrv_truncate(base, s->common.len);
|
||||
if (ret) {
|
||||
goto exit_restore_reopen;
|
||||
}
|
||||
}
|
||||
|
||||
overlay_bs = bdrv_find_overlay(active, top);
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE);
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
uint64_t delay_ns = 0;
|
||||
bool copy;
|
||||
|
||||
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;
|
||||
}
|
||||
/* Copy if allocated above the base */
|
||||
ret = bdrv_co_is_allocated_above(top, base, sector_num,
|
||||
COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
|
||||
&n);
|
||||
copy = (ret == 1);
|
||||
trace_commit_one_iteration(s, sector_num, n, ret);
|
||||
if (copy) {
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
if (delay_ns > 0) {
|
||||
goto wait;
|
||||
}
|
||||
}
|
||||
ret = commit_populate(top, base, sector_num, n, buf);
|
||||
bytes_written += n * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
s->on_error == BLOCKDEV_ON_ERROR_REPORT||
|
||||
(s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
|
||||
goto exit_free_buf;
|
||||
} else {
|
||||
n = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Publish progress */
|
||||
s->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(active, top, base);
|
||||
}
|
||||
|
||||
exit_free_buf:
|
||||
qemu_vfree(buf);
|
||||
|
||||
exit_restore_reopen:
|
||||
/* restore base open flags here if appropriate (e.g., change the base back
|
||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||
* even on failure here */
|
||||
if (s->base_flags != bdrv_get_flags(base)) {
|
||||
bdrv_reopen(base, s->base_flags, NULL);
|
||||
}
|
||||
if (s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
||||
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
||||
}
|
||||
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
|
||||
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
}
|
||||
|
||||
static BlockJobType commit_job_type = {
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = "commit",
|
||||
.set_speed = commit_set_speed,
|
||||
};
|
||||
|
||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
int orig_overlay_flags;
|
||||
int orig_base_flags;
|
||||
BlockDriverState *overlay_bs;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Once we support top == active layer, remove this check */
|
||||
if (top == bs) {
|
||||
error_setg(errp,
|
||||
"Top image as the active layer is currently unsupported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (top == base) {
|
||||
error_setg(errp, "Invalid files for merge: top and base are the same");
|
||||
return;
|
||||
}
|
||||
|
||||
overlay_bs = bdrv_find_overlay(bs, top);
|
||||
|
||||
if (overlay_bs == NULL) {
|
||||
error_setg(errp, "Could not find overlay image for %s:", top->filename);
|
||||
return;
|
||||
}
|
||||
|
||||
orig_base_flags = bdrv_get_flags(base);
|
||||
orig_overlay_flags = bdrv_get_flags(overlay_bs);
|
||||
|
||||
/* convert base & overlay_bs to r/w, if necessary */
|
||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, base,
|
||||
orig_base_flags | BDRV_O_RDWR);
|
||||
}
|
||||
if (!(orig_overlay_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs,
|
||||
orig_overlay_flags | BDRV_O_RDWR);
|
||||
}
|
||||
if (reopen_queue) {
|
||||
bdrv_reopen_multiple(reopen_queue, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s = block_job_create(&commit_job_type, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->base = base;
|
||||
s->top = top;
|
||||
s->active = bs;
|
||||
|
||||
s->base_flags = orig_base_flags;
|
||||
s->orig_overlay_flags = orig_overlay_flags;
|
||||
|
||||
s->on_error = on_error;
|
||||
s->common.co = qemu_coroutine_create(commit_run);
|
||||
|
||||
trace_commit_start(bs, base, top, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
}
|
||||
+7
-6
@@ -140,8 +140,8 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||
continue;
|
||||
|
||||
if ((s->buf_off >= acb->end)) {
|
||||
qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
|
||||
acb->end - acb->start);
|
||||
qemu_iovec_from_buffer(acb->qiov, s->orig_buf + acb->start,
|
||||
acb->end - acb->start);
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_release(acb);
|
||||
s->acb[i] = NULL;
|
||||
@@ -176,7 +176,7 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
||||
{
|
||||
char *buf = state->orig_buf + (start - state->buf_start);
|
||||
|
||||
qemu_iovec_from_buf(acb->qiov, 0, buf, len);
|
||||
qemu_iovec_from_buffer(acb->qiov, buf, len);
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
|
||||
return FIND_RET_OK;
|
||||
@@ -438,7 +438,7 @@ static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
// Do we have to implement canceling? Seems to work without...
|
||||
}
|
||||
|
||||
static const AIOCBInfo curl_aiocb_info = {
|
||||
static AIOPool curl_aio_pool = {
|
||||
.aiocb_size = sizeof(CURLAIOCB),
|
||||
.cancel = curl_aio_cancel,
|
||||
};
|
||||
@@ -505,7 +505,7 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
|
||||
{
|
||||
CURLAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->qiov = qiov;
|
||||
acb->sector_num = sector_num;
|
||||
@@ -542,7 +542,8 @@ static void curl_close(BlockDriverState *bs)
|
||||
}
|
||||
if (s->multi)
|
||||
curl_multi_cleanup(s->multi);
|
||||
g_free(s->url);
|
||||
if (s->url)
|
||||
free(s->url);
|
||||
}
|
||||
|
||||
static int64_t curl_getlength(BlockDriverState *bs)
|
||||
|
||||
-624
@@ -1,624 +0,0 @@
|
||||
/*
|
||||
* GlusterFS backend for QEMU
|
||||
*
|
||||
* Copyright (C) 2012 Bharata B Rao <bharata@linux.vnet.ibm.com>
|
||||
*
|
||||
* Pipe handling mechanism in AIO implementation is derived from
|
||||
* block/rbd.c. Hence,
|
||||
*
|
||||
* Copyright (C) 2010-2011 Christian Brunner <chb@muc.de>,
|
||||
* Josh Durgin <josh.durgin@dreamhost.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
#include <glusterfs/api/glfs.h>
|
||||
#include "block_int.h"
|
||||
#include "qemu_socket.h"
|
||||
#include "uri.h"
|
||||
|
||||
typedef struct GlusterAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
int64_t size;
|
||||
int ret;
|
||||
bool *finished;
|
||||
QEMUBH *bh;
|
||||
} GlusterAIOCB;
|
||||
|
||||
typedef struct BDRVGlusterState {
|
||||
struct glfs *glfs;
|
||||
int fds[2];
|
||||
struct glfs_fd *fd;
|
||||
int qemu_aio_count;
|
||||
int event_reader_pos;
|
||||
GlusterAIOCB *event_acb;
|
||||
} BDRVGlusterState;
|
||||
|
||||
#define GLUSTER_FD_READ 0
|
||||
#define GLUSTER_FD_WRITE 1
|
||||
|
||||
typedef struct GlusterConf {
|
||||
char *server;
|
||||
int port;
|
||||
char *volname;
|
||||
char *image;
|
||||
char *transport;
|
||||
} GlusterConf;
|
||||
|
||||
static void qemu_gluster_gconf_free(GlusterConf *gconf)
|
||||
{
|
||||
g_free(gconf->server);
|
||||
g_free(gconf->volname);
|
||||
g_free(gconf->image);
|
||||
g_free(gconf->transport);
|
||||
g_free(gconf);
|
||||
}
|
||||
|
||||
static int parse_volume_options(GlusterConf *gconf, char *path)
|
||||
{
|
||||
char *p, *q;
|
||||
|
||||
if (!path) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* volume */
|
||||
p = q = path + strspn(path, "/");
|
||||
p += strcspn(p, "/");
|
||||
if (*p == '\0') {
|
||||
return -EINVAL;
|
||||
}
|
||||
gconf->volname = g_strndup(q, p - q);
|
||||
|
||||
/* image */
|
||||
p += strspn(p, "/");
|
||||
if (*p == '\0') {
|
||||
return -EINVAL;
|
||||
}
|
||||
gconf->image = g_strdup(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* file=gluster[+transport]://[server[:port]]/volname/image[?socket=...]
|
||||
*
|
||||
* 'gluster' is the protocol.
|
||||
*
|
||||
* 'transport' specifies the transport type used to connect to gluster
|
||||
* management daemon (glusterd). Valid transport types are
|
||||
* tcp, unix and rdma. If a transport type isn't specified, then tcp
|
||||
* type is assumed.
|
||||
*
|
||||
* 'server' specifies the server where the volume file specification for
|
||||
* the given volume resides. This can be either hostname, ipv4 address
|
||||
* or ipv6 address. ipv6 address needs to be within square brackets [ ].
|
||||
* If transport type is 'unix', then 'server' field should not be specifed.
|
||||
* The 'socket' field needs to be populated with the path to unix domain
|
||||
* socket.
|
||||
*
|
||||
* 'port' is the port number on which glusterd is listening. This is optional
|
||||
* and if not specified, QEMU will send 0 which will make gluster to use the
|
||||
* default port. If the transport type is unix, then 'port' should not be
|
||||
* specified.
|
||||
*
|
||||
* 'volname' is the name of the gluster volume which contains the VM image.
|
||||
*
|
||||
* 'image' is the path to the actual VM image that resides on gluster volume.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* file=gluster://1.2.3.4/testvol/a.img
|
||||
* file=gluster+tcp://1.2.3.4/testvol/a.img
|
||||
* file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img
|
||||
* file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img
|
||||
* file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img
|
||||
* file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img
|
||||
* file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket
|
||||
* file=gluster+rdma://1.2.3.4:24007/testvol/a.img
|
||||
*/
|
||||
static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
|
||||
{
|
||||
URI *uri;
|
||||
QueryParams *qp = NULL;
|
||||
bool is_unix = false;
|
||||
int ret = 0;
|
||||
|
||||
uri = uri_parse(filename);
|
||||
if (!uri) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* transport */
|
||||
if (!strcmp(uri->scheme, "gluster")) {
|
||||
gconf->transport = g_strdup("tcp");
|
||||
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
|
||||
gconf->transport = g_strdup("tcp");
|
||||
} else if (!strcmp(uri->scheme, "gluster+unix")) {
|
||||
gconf->transport = g_strdup("unix");
|
||||
is_unix = true;
|
||||
} else if (!strcmp(uri->scheme, "gluster+rdma")) {
|
||||
gconf->transport = g_strdup("rdma");
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = parse_volume_options(gconf, uri->path);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
qp = query_params_parse(uri->query);
|
||||
if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_unix) {
|
||||
if (uri->server || uri->port) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(qp->p[0].name, "socket")) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
gconf->server = g_strdup(qp->p[0].value);
|
||||
} else {
|
||||
gconf->server = g_strdup(uri->server);
|
||||
gconf->port = uri->port;
|
||||
}
|
||||
|
||||
out:
|
||||
if (qp) {
|
||||
query_params_free(qp);
|
||||
}
|
||||
uri_free(uri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
|
||||
{
|
||||
struct glfs *glfs = NULL;
|
||||
int ret;
|
||||
int old_errno;
|
||||
|
||||
ret = qemu_gluster_parseuri(gconf, filename);
|
||||
if (ret < 0) {
|
||||
error_report("Usage: file=gluster[+transport]://[server[:port]]/"
|
||||
"volname/image[?socket=...]");
|
||||
errno = -ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
glfs = glfs_new(gconf->volname);
|
||||
if (!glfs) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->server,
|
||||
gconf->port);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when
|
||||
* GlusterFS makes GF_LOG_* macros available to libgfapi users.
|
||||
*/
|
||||
ret = glfs_set_logging(glfs, "-", 4);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = glfs_init(glfs);
|
||||
if (ret) {
|
||||
error_report("Gluster connection failed for server=%s port=%d "
|
||||
"volume=%s image=%s transport=%s\n", gconf->server, gconf->port,
|
||||
gconf->volname, gconf->image, gconf->transport);
|
||||
goto out;
|
||||
}
|
||||
return glfs;
|
||||
|
||||
out:
|
||||
if (glfs) {
|
||||
old_errno = errno;
|
||||
glfs_fini(glfs);
|
||||
errno = old_errno;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void qemu_gluster_complete_aio(GlusterAIOCB *acb, BDRVGlusterState *s)
|
||||
{
|
||||
int ret;
|
||||
bool *finished = acb->finished;
|
||||
BlockDriverCompletionFunc *cb = acb->common.cb;
|
||||
void *opaque = acb->common.opaque;
|
||||
|
||||
if (!acb->ret || acb->ret == acb->size) {
|
||||
ret = 0; /* Success */
|
||||
} else if (acb->ret < 0) {
|
||||
ret = acb->ret; /* Read/Write failed */
|
||||
} else {
|
||||
ret = -EIO; /* Partial read/write - fail it */
|
||||
}
|
||||
|
||||
s->qemu_aio_count--;
|
||||
qemu_aio_release(acb);
|
||||
cb(opaque, ret);
|
||||
if (finished) {
|
||||
*finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_gluster_aio_event_reader(void *opaque)
|
||||
{
|
||||
BDRVGlusterState *s = opaque;
|
||||
ssize_t ret;
|
||||
|
||||
do {
|
||||
char *p = (char *)&s->event_acb;
|
||||
|
||||
ret = read(s->fds[GLUSTER_FD_READ], p + s->event_reader_pos,
|
||||
sizeof(s->event_acb) - s->event_reader_pos);
|
||||
if (ret > 0) {
|
||||
s->event_reader_pos += ret;
|
||||
if (s->event_reader_pos == sizeof(s->event_acb)) {
|
||||
s->event_reader_pos = 0;
|
||||
qemu_gluster_complete_aio(s->event_acb, s);
|
||||
}
|
||||
}
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
static int qemu_gluster_aio_flush_cb(void *opaque)
|
||||
{
|
||||
BDRVGlusterState *s = opaque;
|
||||
|
||||
return (s->qemu_aio_count > 0);
|
||||
}
|
||||
|
||||
static int qemu_gluster_open(BlockDriverState *bs, const char *filename,
|
||||
int bdrv_flags)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int open_flags = O_BINARY;
|
||||
int ret = 0;
|
||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||
|
||||
s->glfs = qemu_gluster_init(gconf, filename);
|
||||
if (!s->glfs) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_flags & BDRV_O_RDWR) {
|
||||
open_flags |= O_RDWR;
|
||||
} else {
|
||||
open_flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
||||
open_flags |= O_DIRECT;
|
||||
}
|
||||
|
||||
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
|
||||
if (!s->fd) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_pipe(s->fds);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
fcntl(s->fds[GLUSTER_FD_READ], F_SETFL, O_NONBLOCK);
|
||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ],
|
||||
qemu_gluster_aio_event_reader, NULL, qemu_gluster_aio_flush_cb, s);
|
||||
|
||||
out:
|
||||
qemu_gluster_gconf_free(gconf);
|
||||
if (!ret) {
|
||||
return ret;
|
||||
}
|
||||
if (s->fd) {
|
||||
glfs_close(s->fd);
|
||||
}
|
||||
if (s->glfs) {
|
||||
glfs_fini(s->glfs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qemu_gluster_create(const char *filename,
|
||||
QEMUOptionParameter *options)
|
||||
{
|
||||
struct glfs *glfs;
|
||||
struct glfs_fd *fd;
|
||||
int ret = 0;
|
||||
int64_t total_size = 0;
|
||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||
|
||||
glfs = qemu_gluster_init(gconf, filename);
|
||||
if (!glfs) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n / BDRV_SECTOR_SIZE;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = glfs_creat(glfs, gconf->image,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
|
||||
if (!fd) {
|
||||
ret = -errno;
|
||||
} else {
|
||||
if (glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
if (glfs_close(fd) != 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
}
|
||||
out:
|
||||
qemu_gluster_gconf_free(gconf);
|
||||
if (glfs) {
|
||||
glfs_fini(glfs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qemu_gluster_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)blockacb;
|
||||
bool finished = false;
|
||||
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo gluster_aiocb_info = {
|
||||
.aiocb_size = sizeof(GlusterAIOCB),
|
||||
.cancel = qemu_gluster_aio_cancel,
|
||||
};
|
||||
|
||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int retval;
|
||||
|
||||
acb->ret = ret;
|
||||
retval = qemu_write_full(s->fds[GLUSTER_FD_WRITE], &acb, sizeof(acb));
|
||||
if (retval != sizeof(acb)) {
|
||||
/*
|
||||
* Gluster AIO callback thread failed to notify the waiting
|
||||
* QEMU thread about IO completion.
|
||||
*
|
||||
* Complete this IO request and make the disk inaccessible for
|
||||
* subsequent reads and writes.
|
||||
*/
|
||||
error_report("Gluster failed to notify QEMU about IO completion");
|
||||
|
||||
qemu_mutex_lock_iothread(); /* We are in gluster thread context */
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
qemu_aio_release(acb);
|
||||
s->qemu_aio_count--;
|
||||
close(s->fds[GLUSTER_FD_READ]);
|
||||
close(s->fds[GLUSTER_FD_WRITE]);
|
||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL,
|
||||
NULL);
|
||||
bs->drv = NULL; /* Make the disk inaccessible */
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int write)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB *acb;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
size_t size;
|
||||
off_t offset;
|
||||
|
||||
offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
s->qemu_aio_count++;
|
||||
|
||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
||||
acb->size = size;
|
||||
acb->ret = 0;
|
||||
acb->finished = NULL;
|
||||
|
||||
if (write) {
|
||||
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||
&gluster_finish_aiocb, acb);
|
||||
} else {
|
||||
ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||
&gluster_finish_aiocb, acb);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
return &acb->common;
|
||||
|
||||
out:
|
||||
s->qemu_aio_count--;
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qemu_gluster_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
GlusterAIOCB *acb;
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
|
||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
||||
acb->size = 0;
|
||||
acb->ret = 0;
|
||||
acb->finished = NULL;
|
||||
s->qemu_aio_count++;
|
||||
|
||||
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
return &acb->common;
|
||||
|
||||
out:
|
||||
s->qemu_aio_count--;
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int64_t qemu_gluster_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int64_t ret;
|
||||
|
||||
ret = glfs_lseek(s->fd, 0, SEEK_END);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
ret = glfs_fstat(s->fd, &st);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
} else {
|
||||
return st.st_blocks * 512;
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_gluster_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
|
||||
close(s->fds[GLUSTER_FD_READ]);
|
||||
close(s->fds[GLUSTER_FD_WRITE]);
|
||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL, NULL);
|
||||
|
||||
if (s->fd) {
|
||||
glfs_close(s->fd);
|
||||
s->fd = NULL;
|
||||
}
|
||||
glfs_fini(s->glfs);
|
||||
}
|
||||
|
||||
static QEMUOptionParameter qemu_gluster_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster = {
|
||||
.format_name = "gluster",
|
||||
.protocol_name = "gluster",
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster_tcp = {
|
||||
.format_name = "gluster",
|
||||
.protocol_name = "gluster+tcp",
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster_unix = {
|
||||
.format_name = "gluster",
|
||||
.protocol_name = "gluster+unix",
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_gluster_rdma = {
|
||||
.format_name = "gluster",
|
||||
.protocol_name = "gluster+rdma",
|
||||
.instance_size = sizeof(BDRVGlusterState),
|
||||
.bdrv_file_open = qemu_gluster_open,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_create = qemu_gluster_create,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
||||
.create_options = qemu_gluster_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_gluster_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_gluster_rdma);
|
||||
bdrv_register(&bdrv_gluster_unix);
|
||||
bdrv_register(&bdrv_gluster_tcp);
|
||||
bdrv_register(&bdrv_gluster);
|
||||
}
|
||||
|
||||
block_init(bdrv_gluster_init);
|
||||
+223
-276
@@ -35,10 +35,6 @@
|
||||
#include <iscsi/iscsi.h>
|
||||
#include <iscsi/scsi-lowlevel.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <scsi/sg.h>
|
||||
#include <hw/scsi-defs.h>
|
||||
#endif
|
||||
|
||||
typedef struct IscsiLun {
|
||||
struct iscsi_context *iscsi;
|
||||
@@ -60,11 +56,15 @@ typedef struct IscsiAIOCB {
|
||||
int canceled;
|
||||
size_t read_size;
|
||||
size_t read_offset;
|
||||
#ifdef __linux__
|
||||
sg_io_hdr_t *ioh;
|
||||
#endif
|
||||
} IscsiAIOCB;
|
||||
|
||||
struct IscsiTask {
|
||||
IscsiLun *iscsilun;
|
||||
BlockDriverState *bs;
|
||||
int status;
|
||||
int complete;
|
||||
};
|
||||
|
||||
static void
|
||||
iscsi_bh_cb(void *p)
|
||||
{
|
||||
@@ -126,7 +126,7 @@ iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo iscsi_aiocb_info = {
|
||||
static AIOPool iscsi_aio_pool = {
|
||||
.aiocb_size = sizeof(IscsiAIOCB),
|
||||
.cancel = iscsi_aio_cancel,
|
||||
};
|
||||
@@ -160,6 +160,12 @@ iscsi_set_events(IscsiLun *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;
|
||||
}
|
||||
|
||||
@@ -227,7 +233,7 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
uint64_t lba;
|
||||
struct iscsi_data data;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
@@ -241,7 +247,8 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
/* this will allow us to get rid of 'buf' completely */
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
acb->buf = g_malloc(size);
|
||||
qemu_iovec_to_buf(acb->qiov, 0, acb->buf, size);
|
||||
qemu_iovec_to_buffer(acb->qiov, acb->buf);
|
||||
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
@@ -255,6 +262,10 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
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);
|
||||
@@ -318,7 +329,7 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
@@ -373,7 +384,7 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
*(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,
|
||||
@@ -423,7 +434,7 @@ iscsi_aio_flush(BlockDriverState *bs,
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
@@ -476,7 +487,7 @@ iscsi_aio_discard(BlockDriverState *bs,
|
||||
IscsiAIOCB *acb;
|
||||
struct unmap_list list[1];
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
@@ -502,150 +513,6 @@ iscsi_aio_discard(BlockDriverState *bs,
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void
|
||||
iscsi_aio_ioctl_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 ioctl(SG_IO) to iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
acb->ioh->driver_status = 0;
|
||||
acb->ioh->host_status = 0;
|
||||
acb->ioh->resid = 0;
|
||||
|
||||
#define SG_ERR_DRIVER_SENSE 0x08
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->datain.size >= 2) {
|
||||
int ss;
|
||||
|
||||
acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE;
|
||||
|
||||
acb->ioh->sb_len_wr = acb->task->datain.size - 2;
|
||||
ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ?
|
||||
acb->ioh->mx_sb_len : acb->ioh->sb_len_wr;
|
||||
memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss);
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
struct iscsi_data data;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
assert(req == SG_IO);
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
acb->buf = NULL;
|
||||
acb->ioh = buf;
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
||||
switch (acb->ioh->dxfer_direction) {
|
||||
case SG_DXFER_TO_DEV:
|
||||
acb->task->xfer_dir = SCSI_XFER_WRITE;
|
||||
break;
|
||||
case SG_DXFER_FROM_DEV:
|
||||
acb->task->xfer_dir = SCSI_XFER_READ;
|
||||
break;
|
||||
default:
|
||||
acb->task->xfer_dir = SCSI_XFER_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
acb->task->cdb_size = acb->ioh->cmd_len;
|
||||
memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len);
|
||||
acb->task->expxferlen = acb->ioh->dxfer_len;
|
||||
|
||||
if (acb->task->xfer_dir == SCSI_XFER_WRITE) {
|
||||
data.data = acb->ioh->dxferp;
|
||||
data.size = acb->ioh->dxfer_len;
|
||||
}
|
||||
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
|
||||
iscsi_aio_ioctl_cb,
|
||||
(acb->task->xfer_dir == SCSI_XFER_WRITE) ?
|
||||
&data : NULL,
|
||||
acb) != 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* tell libiscsi to read straight into the buffer we got from ioctl */
|
||||
if (acb->task->xfer_dir == SCSI_XFER_READ) {
|
||||
scsi_task_add_data_in_buffer(acb->task,
|
||||
acb->ioh->dxfer_len,
|
||||
acb->ioh->dxferp);
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
|
||||
static void ioctl_cb(void *opaque, int status)
|
||||
{
|
||||
int *p_status = opaque;
|
||||
*p_status = status;
|
||||
}
|
||||
|
||||
static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int status;
|
||||
|
||||
switch (req) {
|
||||
case SG_GET_VERSION_NUM:
|
||||
*(int *)buf = 30000;
|
||||
break;
|
||||
case SG_GET_SCSI_ID:
|
||||
((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
|
||||
break;
|
||||
case SG_IO:
|
||||
status = -EINPROGRESS;
|
||||
iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status);
|
||||
|
||||
while (status == -EINPROGRESS) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int64_t
|
||||
iscsi_getlength(BlockDriverState *bs)
|
||||
{
|
||||
@@ -658,6 +525,158 @@ 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)
|
||||
{
|
||||
struct IscsiTask *itask = opaque;
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
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;
|
||||
}
|
||||
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
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->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,
|
||||
void *opaque)
|
||||
{
|
||||
struct IscsiTask *itask = opaque;
|
||||
struct scsi_task *task;
|
||||
|
||||
if (status != 0) {
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
task = iscsi_inquiry_task(iscsi, itask->iscsilun->lun,
|
||||
0, 0, 36,
|
||||
iscsi_inquiry_cb, opaque);
|
||||
if (task == NULL) {
|
||||
error_report("iSCSI: failed to send inquiry command.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_chap(struct iscsi_context *iscsi, const char *target)
|
||||
{
|
||||
QemuOptsList *list;
|
||||
@@ -739,26 +758,26 @@ static char *parse_initiator_name(const char *target)
|
||||
QemuOptsList *list;
|
||||
QemuOpts *opts;
|
||||
const char *name = NULL;
|
||||
const char *iscsi_name = qemu_get_vm_name();
|
||||
|
||||
list = qemu_find_opts("iscsi");
|
||||
if (list) {
|
||||
opts = qemu_opts_find(list, target);
|
||||
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) {
|
||||
opts = QTAILQ_FIRST(&list->head);
|
||||
}
|
||||
if (opts) {
|
||||
name = qemu_opt_get(opts, "initiator-name");
|
||||
return g_strdup("iqn.2008-11.org.linux-kvm");
|
||||
}
|
||||
}
|
||||
|
||||
if (name) {
|
||||
return g_strdup(name);
|
||||
} else {
|
||||
return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
|
||||
iscsi_name ? ":" : "",
|
||||
iscsi_name ? iscsi_name : "");
|
||||
name = qemu_opt_get(opts, "initiator-name");
|
||||
if (!name) {
|
||||
return g_strdup("iqn.2008-11.org.linux-kvm");
|
||||
}
|
||||
|
||||
return g_strdup(name);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -770,10 +789,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = NULL;
|
||||
struct iscsi_url *iscsi_url = NULL;
|
||||
struct scsi_task *task = NULL;
|
||||
struct scsi_inquiry_standard *inq = NULL;
|
||||
struct scsi_readcapacity10 *rc10 = NULL;
|
||||
struct scsi_readcapacity16 *rc16 = NULL;
|
||||
struct IscsiTask task;
|
||||
char *initiator_name = NULL;
|
||||
int ret;
|
||||
|
||||
@@ -786,9 +802,10 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
iscsi_url = iscsi_parse_full_url(iscsi, filename);
|
||||
if (iscsi_url == NULL) {
|
||||
error_report("Failed to parse URL : %s", filename);
|
||||
error_report("Failed to parse URL : %s %s", filename,
|
||||
iscsi_get_error(iscsi));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
@@ -799,13 +816,13 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
if (iscsi == NULL) {
|
||||
error_report("iSCSI: Failed to create iSCSI context.");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
|
||||
error_report("iSCSI: Failed to set target name.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_url->user != NULL) {
|
||||
@@ -814,7 +831,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
if (ret != 0) {
|
||||
error_report("Failed to set initiator username and password");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,13 +839,13 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
if (parse_chap(iscsi, iscsi_url->target) != 0) {
|
||||
error_report("iSCSI: Failed to set CHAP user/password");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
||||
error_report("iSCSI: Failed to set session type to normal.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
@@ -836,108 +853,49 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
/* check if we got HEADER_DIGEST via the options */
|
||||
parse_header_digest(iscsi, iscsi_url->target);
|
||||
|
||||
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
|
||||
error_report("iSCSI: Failed to connect to LUN : %s",
|
||||
iscsi_get_error(iscsi));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
task.iscsilun = iscsilun;
|
||||
task.status = 0;
|
||||
task.complete = 0;
|
||||
task.bs = bs;
|
||||
|
||||
iscsilun->iscsi = iscsi;
|
||||
iscsilun->lun = iscsi_url->lun;
|
||||
|
||||
task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36);
|
||||
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
error_report("iSCSI: failed to send inquiry command.");
|
||||
if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,
|
||||
iscsi_connect_cb, &task)
|
||||
!= 0) {
|
||||
error_report("iSCSI: Failed to start async connect.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
inq = scsi_datain_unmarshall(task);
|
||||
if (inq == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall inquiry data.");
|
||||
while (!task.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
if (task.status != 0) {
|
||||
error_report("iSCSI: Failed to connect to LUN : %s",
|
||||
iscsi_get_error(iscsi));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
iscsilun->type = inq->periperal_device_type;
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
switch (iscsilun->type) {
|
||||
case TYPE_DISK:
|
||||
task = iscsi_readcapacity16_sync(iscsi, iscsilun->lun);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
error_report("iSCSI: failed to send readcapacity16 command.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rc16 = scsi_datain_unmarshall(task);
|
||||
if (rc16 == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
iscsilun->block_size = rc16->block_length;
|
||||
iscsilun->num_blocks = rc16->returned_lba + 1;
|
||||
break;
|
||||
case TYPE_ROM:
|
||||
task = iscsi_readcapacity10_sync(iscsi, iscsilun->lun, 0, 0);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
error_report("iSCSI: failed to send readcapacity10 command.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
iscsilun->block_size = rc10->block_size;
|
||||
if (rc10->lba == 0) {
|
||||
/* blank disk loaded */
|
||||
iscsilun->num_blocks = 0;
|
||||
} else {
|
||||
iscsilun->num_blocks = rc10->lba + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (iscsi_url != NULL) {
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
}
|
||||
return 0;
|
||||
|
||||
bs->total_sectors = iscsilun->num_blocks *
|
||||
iscsilun->block_size / BDRV_SECTOR_SIZE ;
|
||||
|
||||
/* Medium changer or tape. We dont have any emulation for this so this must
|
||||
* be sg ioctl compatible. We force it to be sg, otherwise qemu will try
|
||||
* to read from the device to guess the image format.
|
||||
*/
|
||||
if (iscsilun->type == TYPE_MEDIUM_CHANGER ||
|
||||
iscsilun->type == TYPE_TAPE) {
|
||||
bs->sg = 1;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
failed:
|
||||
if (initiator_name != NULL) {
|
||||
g_free(initiator_name);
|
||||
}
|
||||
if (iscsi_url != NULL) {
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
}
|
||||
if (task != NULL) {
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (iscsi != NULL) {
|
||||
iscsi_destroy_context(iscsi);
|
||||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
if (iscsi != NULL) {
|
||||
iscsi_destroy_context(iscsi);
|
||||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -951,11 +909,6 @@ static void iscsi_close(BlockDriverState *bs)
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
|
||||
static int iscsi_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_iscsi = {
|
||||
.format_name = "iscsi",
|
||||
.protocol_name = "iscsi",
|
||||
@@ -971,12 +924,6 @@ static BlockDriver bdrv_iscsi = {
|
||||
.bdrv_aio_flush = iscsi_aio_flush,
|
||||
|
||||
.bdrv_aio_discard = iscsi_aio_discard,
|
||||
.bdrv_has_zero_init = iscsi_has_zero_init,
|
||||
|
||||
#ifdef __linux__
|
||||
.bdrv_ioctl = iscsi_ioctl,
|
||||
.bdrv_aio_ioctl = iscsi_aio_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void iscsi_block_init(void)
|
||||
|
||||
-322
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
* Image mirroring
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2012
|
||||
*
|
||||
* Authors:
|
||||
* Paolo Bonzini <pbonzini@redhat.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 "blockjob.h"
|
||||
#include "block_int.h"
|
||||
#include "qemu/ratelimit.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.
|
||||
*/
|
||||
BLOCK_SIZE = 512 * BDRV_SECTORS_PER_DIRTY_CHUNK, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct MirrorBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *target;
|
||||
MirrorSyncMode mode;
|
||||
BlockdevOnError on_source_error, on_target_error;
|
||||
bool synced;
|
||||
bool should_complete;
|
||||
int64_t sector_num;
|
||||
uint8_t *buf;
|
||||
} MirrorBlockJob;
|
||||
|
||||
static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
||||
int error)
|
||||
{
|
||||
s->synced = false;
|
||||
if (read) {
|
||||
return block_job_error_action(&s->common, s->common.bs,
|
||||
s->on_source_error, true, error);
|
||||
} else {
|
||||
return block_job_error_action(&s->common, s->target,
|
||||
s->on_target_error, false, error);
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
|
||||
BlockErrorAction *p_action)
|
||||
{
|
||||
BlockDriverState *source = s->common.bs;
|
||||
BlockDriverState *target = s->target;
|
||||
QEMUIOVector qiov;
|
||||
int ret, nb_sectors;
|
||||
int64_t end;
|
||||
struct iovec iov;
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
s->sector_num = bdrv_get_next_dirty(source, s->sector_num);
|
||||
nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num);
|
||||
bdrv_reset_dirty(source, s->sector_num, nb_sectors);
|
||||
|
||||
/* Copy the dirty cluster. */
|
||||
iov.iov_base = s->buf;
|
||||
iov.iov_len = nb_sectors * 512;
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
trace_mirror_one_iteration(s, s->sector_num, nb_sectors);
|
||||
ret = bdrv_co_readv(source, s->sector_num, nb_sectors, &qiov);
|
||||
if (ret < 0) {
|
||||
*p_action = mirror_error_action(s, true, -ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = bdrv_co_writev(target, s->sector_num, nb_sectors, &qiov);
|
||||
if (ret < 0) {
|
||||
*p_action = mirror_error_action(s, false, -ret);
|
||||
s->synced = false;
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* Try again later. */
|
||||
bdrv_set_dirty(source, s->sector_num, nb_sectors);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void coroutine_fn mirror_run(void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = opaque;
|
||||
BlockDriverState *bs = s->common.bs;
|
||||
int64_t sector_num, end;
|
||||
int ret = 0;
|
||||
int n;
|
||||
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
block_job_completed(&s->common, s->common.len);
|
||||
return;
|
||||
}
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
s->buf = qemu_blockalign(bs, BLOCK_SIZE);
|
||||
|
||||
if (s->mode != MIRROR_SYNC_MODE_NONE) {
|
||||
/* First part, loop on the sectors and initialize the dirty bitmap. */
|
||||
BlockDriverState *base;
|
||||
base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd;
|
||||
for (sector_num = 0; sector_num < end; ) {
|
||||
int64_t next = (sector_num | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1;
|
||||
ret = bdrv_co_is_allocated_above(bs, base,
|
||||
sector_num, next - sector_num, &n);
|
||||
|
||||
if (ret < 0) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
assert(n > 0);
|
||||
if (ret == 1) {
|
||||
bdrv_set_dirty(bs, sector_num, n);
|
||||
sector_num = next;
|
||||
} else {
|
||||
sector_num += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->sector_num = -1;
|
||||
for (;;) {
|
||||
uint64_t delay_ns;
|
||||
int64_t cnt;
|
||||
bool should_complete;
|
||||
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
if (cnt != 0) {
|
||||
BlockErrorAction action = BDRV_ACTION_REPORT;
|
||||
ret = mirror_iteration(s, &action);
|
||||
if (ret < 0 && action == BDRV_ACTION_REPORT) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
}
|
||||
|
||||
should_complete = false;
|
||||
if (cnt == 0) {
|
||||
trace_mirror_before_flush(s);
|
||||
ret = bdrv_flush(s->target);
|
||||
if (ret < 0) {
|
||||
if (mirror_error_action(s, false, -ret) == BDRV_ACTION_REPORT) {
|
||||
goto immediate_exit;
|
||||
}
|
||||
} else {
|
||||
/* We're out of the streaming phase. From now on, if the job
|
||||
* is cancelled we will actually complete all pending I/O and
|
||||
* report completion. This way, block-job-cancel will leave
|
||||
* the target in a consistent state.
|
||||
*/
|
||||
s->common.offset = end * BDRV_SECTOR_SIZE;
|
||||
if (!s->synced) {
|
||||
block_job_ready(&s->common);
|
||||
s->synced = true;
|
||||
}
|
||||
|
||||
should_complete = s->should_complete ||
|
||||
block_job_is_cancelled(&s->common);
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt == 0 && should_complete) {
|
||||
/* The dirty bitmap is not updated while operations are pending.
|
||||
* If we're about to exit, wait for pending operations before
|
||||
* calling bdrv_get_dirty_count(bs), or we may exit while the
|
||||
* source has dirty data to copy!
|
||||
*
|
||||
* Note that I/O can be submitted by the guest while
|
||||
* mirror_populate runs.
|
||||
*/
|
||||
trace_mirror_before_drain(s, cnt);
|
||||
bdrv_drain_all();
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
trace_mirror_before_sleep(s, cnt, s->synced);
|
||||
if (!s->synced) {
|
||||
/* Publish progress */
|
||||
s->common.offset = end * BDRV_SECTOR_SIZE - cnt * BLOCK_SIZE;
|
||||
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, BDRV_SECTORS_PER_DIRTY_CHUNK);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
} else if (!should_complete) {
|
||||
delay_ns = (cnt == 0 ? SLICE_TIME : 0);
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
} else if (cnt == 0) {
|
||||
/* The two disks are in sync. Exit and report successful
|
||||
* completion.
|
||||
*/
|
||||
assert(QLIST_EMPTY(&bs->tracked_requests));
|
||||
s->common.cancelled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
immediate_exit:
|
||||
g_free(s->buf);
|
||||
bdrv_set_dirty_tracking(bs, false);
|
||||
bdrv_iostatus_disable(s->target);
|
||||
if (s->should_complete && ret == 0) {
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) {
|
||||
bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL);
|
||||
}
|
||||
bdrv_swap(s->target, s->common.bs);
|
||||
}
|
||||
bdrv_close(s->target);
|
||||
bdrv_delete(s->target);
|
||||
block_job_completed(&s->common, ret);
|
||||
}
|
||||
|
||||
static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void mirror_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
|
||||
bdrv_iostatus_reset(s->target);
|
||||
}
|
||||
|
||||
static void mirror_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open_backing_file(s->target);
|
||||
if (ret < 0) {
|
||||
char backing_filename[PATH_MAX];
|
||||
bdrv_get_full_backing_filename(s->target, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, backing_filename);
|
||||
return;
|
||||
}
|
||||
if (!s->synced) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
s->should_complete = true;
|
||||
block_job_resume(job);
|
||||
}
|
||||
|
||||
static BlockJobType mirror_job_type = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = "mirror",
|
||||
.set_speed = mirror_set_speed,
|
||||
.iostatus_reset= mirror_iostatus_reset,
|
||||
.complete = mirror_complete,
|
||||
};
|
||||
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, MirrorSyncMode mode,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
|
||||
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "on-source-error");
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(&mirror_job_type, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->on_source_error = on_source_error;
|
||||
s->on_target_error = on_target_error;
|
||||
s->target = target;
|
||||
s->mode = mode;
|
||||
bdrv_set_dirty_tracking(bs, true);
|
||||
bdrv_set_enable_write_cache(s->target, true);
|
||||
bdrv_set_on_error(s->target, on_target_error, on_target_error);
|
||||
bdrv_iostatus_enable(s->target);
|
||||
s->common.co = qemu_coroutine_create(mirror_run);
|
||||
trace_mirror_start(bs, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
}
|
||||
+26
-121
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "nbd.h"
|
||||
#include "uri.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "qemu_socket.h"
|
||||
@@ -56,6 +55,7 @@ typedef struct BDRVNBDState {
|
||||
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;
|
||||
@@ -65,75 +65,13 @@ typedef struct BDRVNBDState {
|
||||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
||||
struct nbd_reply reply;
|
||||
|
||||
int is_unix;
|
||||
/* If it begins with '/', this is a UNIX domain socket. Otherwise,
|
||||
* it's a string of the form <hostname|ip4|\[ip6\]>:port
|
||||
*/
|
||||
char *host_spec;
|
||||
char *export_name; /* An NBD server may export several devices */
|
||||
} BDRVNBDState;
|
||||
|
||||
static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
|
||||
{
|
||||
URI *uri;
|
||||
const char *p;
|
||||
QueryParams *qp = NULL;
|
||||
int ret = 0;
|
||||
|
||||
uri = uri_parse(filename);
|
||||
if (!uri) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* transport */
|
||||
if (!strcmp(uri->scheme, "nbd")) {
|
||||
s->is_unix = false;
|
||||
} else if (!strcmp(uri->scheme, "nbd+tcp")) {
|
||||
s->is_unix = false;
|
||||
} else if (!strcmp(uri->scheme, "nbd+unix")) {
|
||||
s->is_unix = true;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p = uri->path ? uri->path : "/";
|
||||
p += strspn(p, "/");
|
||||
if (p[0]) {
|
||||
s->export_name = g_strdup(p);
|
||||
}
|
||||
|
||||
qp = query_params_parse(uri->query);
|
||||
if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (s->is_unix) {
|
||||
/* nbd+unix:///export?socket=path */
|
||||
if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
s->host_spec = g_strdup(qp->p[0].value);
|
||||
} else {
|
||||
/* nbd[+tcp]://host:port/export */
|
||||
if (!uri->server) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!uri->port) {
|
||||
uri->port = NBD_DEFAULT_PORT;
|
||||
}
|
||||
s->host_spec = g_strdup_printf("%s:%d", uri->server, uri->port);
|
||||
}
|
||||
|
||||
out:
|
||||
if (qp) {
|
||||
query_params_free(qp);
|
||||
}
|
||||
uri_free(uri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nbd_config(BDRVNBDState *s, const char *filename)
|
||||
static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
|
||||
{
|
||||
char *file;
|
||||
char *export_name;
|
||||
@@ -141,10 +79,6 @@ static int nbd_config(BDRVNBDState *s, const char *filename)
|
||||
const char *unixpath;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (strstr(filename, "://")) {
|
||||
return nbd_parse_uri(s, filename);
|
||||
}
|
||||
|
||||
file = g_strdup(filename);
|
||||
|
||||
export_name = strstr(file, EN_OPTSTR);
|
||||
@@ -164,10 +98,11 @@ static int nbd_config(BDRVNBDState *s, const char *filename)
|
||||
|
||||
/* are we a UNIX or TCP socket? */
|
||||
if (strstart(host_spec, "unix:", &unixpath)) {
|
||||
s->is_unix = true;
|
||||
if (unixpath[0] != '/') { /* We demand an absolute path*/
|
||||
goto out;
|
||||
}
|
||||
s->host_spec = g_strdup(unixpath);
|
||||
} else {
|
||||
s->is_unix = false;
|
||||
s->host_spec = g_strdup(host_spec);
|
||||
}
|
||||
|
||||
@@ -261,7 +196,7 @@ static void nbd_restart_write(void *opaque)
|
||||
}
|
||||
|
||||
static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
struct iovec *iov, int offset)
|
||||
{
|
||||
int rc, ret;
|
||||
|
||||
@@ -270,9 +205,8 @@ static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
|
||||
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 && qiov) {
|
||||
ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (rc >= 0 && iov) {
|
||||
ret = qemu_co_sendv(s->sock, iov, request->len, offset);
|
||||
if (ret != request->len) {
|
||||
return -EIO;
|
||||
}
|
||||
@@ -286,7 +220,7 @@ static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
|
||||
|
||||
static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request,
|
||||
struct nbd_reply *reply,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
struct iovec *iov, int offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -297,9 +231,8 @@ static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request,
|
||||
if (reply->handle != request->handle) {
|
||||
reply->error = EIO;
|
||||
} else {
|
||||
if (qiov && reply->error == 0) {
|
||||
ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
|
||||
offset, request->len);
|
||||
if (iov && reply->error == 0) {
|
||||
ret = qemu_co_recvv(s->sock, iov, request->len, offset);
|
||||
if (ret != request->len) {
|
||||
reply->error = EIO;
|
||||
}
|
||||
@@ -327,7 +260,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
|
||||
if (s->is_unix) {
|
||||
if (s->host_spec[0] == '/') {
|
||||
sock = unix_socket_outgoing(s->host_spec);
|
||||
} else {
|
||||
sock = tcp_socket_outgoing_spec(s->host_spec);
|
||||
@@ -385,7 +318,7 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
|
||||
qemu_co_mutex_init(&s->free_sema);
|
||||
|
||||
/* Pop the config into our state object. Exit if invalid. */
|
||||
result = nbd_config(s, filename);
|
||||
result = nbd_config(s, filename, flags);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
@@ -416,7 +349,7 @@ static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
nbd_co_receive_reply(s, &request, &reply, qiov, offset);
|
||||
nbd_co_receive_reply(s, &request, &reply, qiov->iov, offset);
|
||||
}
|
||||
nbd_coroutine_end(s, &request);
|
||||
return -reply.error;
|
||||
@@ -441,7 +374,7 @@ static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
|
||||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(s, &request);
|
||||
ret = nbd_co_send_request(s, &request, qiov, offset);
|
||||
ret = nbd_co_send_request(s, &request, qiov->iov, offset);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
@@ -563,48 +496,20 @@ 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",
|
||||
.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,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_tcp = {
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd+tcp",
|
||||
.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,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_unix = {
|
||||
.format_name = "nbd",
|
||||
.protocol_name = "nbd+unix",
|
||||
.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,
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_nbd);
|
||||
bdrv_register(&bdrv_nbd_tcp);
|
||||
bdrv_register(&bdrv_nbd_unix);
|
||||
}
|
||||
|
||||
block_init(bdrv_nbd_init);
|
||||
+2
-12
@@ -197,15 +197,6 @@ static int qcow_open(BlockDriverState *bs, int flags)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* We have nothing to do for QCOW reopen, stubs just return
|
||||
* success */
|
||||
static int qcow_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -549,7 +540,7 @@ done:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
qemu_iovec_from_buf(qiov, 0, orig_buf, qiov->size);
|
||||
qemu_iovec_from_buffer(qiov, orig_buf, qiov->size);
|
||||
qemu_vfree(orig_buf);
|
||||
}
|
||||
|
||||
@@ -578,7 +569,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
buf = orig_buf = qemu_blockalign(bs, qiov->size);
|
||||
qemu_iovec_to_buf(qiov, 0, buf, qiov->size);
|
||||
qemu_iovec_to_buffer(qiov, buf);
|
||||
} else {
|
||||
orig_buf = NULL;
|
||||
buf = (uint8_t *)qiov->iov->iov_base;
|
||||
@@ -877,7 +868,6 @@ static BlockDriver bdrv_qcow = {
|
||||
.bdrv_probe = qcow_probe,
|
||||
.bdrv_open = qcow_open,
|
||||
.bdrv_close = qcow_close,
|
||||
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
||||
.bdrv_create = qcow_create,
|
||||
|
||||
.bdrv_co_readv = qcow_co_readv,
|
||||
|
||||
+23
-2
@@ -40,9 +40,11 @@ struct Qcow2Cache {
|
||||
struct Qcow2Cache* depends;
|
||||
int size;
|
||||
bool depends_on_flush;
|
||||
bool writethrough;
|
||||
};
|
||||
|
||||
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
|
||||
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
|
||||
bool writethrough)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
Qcow2Cache *c;
|
||||
@@ -51,6 +53,7 @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
|
||||
c = g_malloc0(sizeof(*c));
|
||||
c->size = num_tables;
|
||||
c->entries = g_malloc0(sizeof(*c->entries) * num_tables);
|
||||
c->writethrough = writethrough;
|
||||
|
||||
for (i = 0; i < c->size; i++) {
|
||||
c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
|
||||
@@ -304,7 +307,12 @@ found:
|
||||
*table = NULL;
|
||||
|
||||
assert(c->entries[i].ref >= 0);
|
||||
return 0;
|
||||
|
||||
if (c->writethrough) {
|
||||
return qcow2_cache_entry_flush(bs, c, i);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
|
||||
@@ -321,3 +329,16 @@ void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
|
||||
found:
|
||||
c->entries[i].dirty = true;
|
||||
}
|
||||
|
||||
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
|
||||
bool enable)
|
||||
{
|
||||
bool old = c->writethrough;
|
||||
|
||||
if (!old && enable) {
|
||||
qcow2_cache_flush(bs, c);
|
||||
}
|
||||
|
||||
c->writethrough = enable;
|
||||
return old;
|
||||
}
|
||||
+6
-15
@@ -540,6 +540,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;
|
||||
}
|
||||
|
||||
/* find the cluster offset for the given disk offset */
|
||||
@@ -642,10 +643,11 @@ 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, cluster_offset, m->nb_available,
|
||||
align_offset(m->nb_available, s->cluster_sectors));
|
||||
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;
|
||||
@@ -662,10 +664,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
qcow2_cache_depends_on_flush(s->l2_table_cache);
|
||||
}
|
||||
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||
s->refcount_block_cache);
|
||||
}
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
|
||||
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
@@ -950,16 +949,8 @@ again:
|
||||
|
||||
/* save info needed for meta data update */
|
||||
if (nb_clusters > 0) {
|
||||
/*
|
||||
* requested_sectors: Number of sectors from the start of the first
|
||||
* newly allocated cluster to the end of the (possibly shortened
|
||||
* before) write request.
|
||||
*
|
||||
* avail_sectors: Number of sectors from the start of the first
|
||||
* newly allocated to the end of the last newly allocated cluster.
|
||||
*/
|
||||
int requested_sectors = n_end - keep_clusters * s->cluster_sectors;
|
||||
int avail_sectors = nb_clusters
|
||||
int avail_sectors = (keep_clusters + nb_clusters)
|
||||
<< (s->cluster_bits - BDRV_SECTOR_BITS);
|
||||
|
||||
*m = (QCowL2Meta) {
|
||||
|
||||
+25
-40
@@ -301,8 +301,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
uint64_t last_table_size;
|
||||
uint64_t blocks_clusters;
|
||||
do {
|
||||
uint64_t table_clusters =
|
||||
size_to_clusters(s, table_size * sizeof(uint64_t));
|
||||
uint64_t table_clusters = size_to_clusters(s, table_size);
|
||||
blocks_clusters = 1 +
|
||||
((table_clusters + refcount_block_clusters - 1)
|
||||
/ refcount_block_clusters);
|
||||
@@ -628,11 +627,10 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES);
|
||||
assert(size > 0 && size <= s->cluster_size);
|
||||
if (s->free_byte_offset == 0) {
|
||||
offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
s->free_byte_offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
if (s->free_byte_offset < 0) {
|
||||
return s->free_byte_offset;
|
||||
}
|
||||
s->free_byte_offset = offset;
|
||||
}
|
||||
redo:
|
||||
free_in_cluster = s->cluster_size -
|
||||
@@ -728,6 +726,13 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t old_offset, old_l2_offset;
|
||||
int i, j, l1_modified = 0, nb_csectors, refcount;
|
||||
int ret;
|
||||
bool old_l2_writethrough, old_refcount_writethrough;
|
||||
|
||||
/* Switch caches to writeback mode during update */
|
||||
old_l2_writethrough =
|
||||
qcow2_cache_set_writethrough(bs, s->l2_table_cache, false);
|
||||
old_refcount_writethrough =
|
||||
qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false);
|
||||
|
||||
l2_table = NULL;
|
||||
l1_table = NULL;
|
||||
@@ -851,6 +856,11 @@ fail:
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
}
|
||||
|
||||
/* Enable writethrough cache mode again */
|
||||
qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough);
|
||||
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) {
|
||||
for(i = 0; i < l1_size; i++)
|
||||
@@ -1112,12 +1122,11 @@ fail:
|
||||
* Returns 0 if no errors are found, the number of errors in case the image is
|
||||
* detected as corrupted, and -errno when an internal error occurred.
|
||||
*/
|
||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t size, i;
|
||||
int nb_clusters, refcount1, refcount2;
|
||||
int64_t size;
|
||||
int nb_clusters, refcount1, refcount2, i;
|
||||
QCowSnapshot *sn;
|
||||
uint16_t *refcount_table;
|
||||
int ret;
|
||||
@@ -1161,15 +1170,14 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
|
||||
/* Refcount blocks are cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
|
||||
fprintf(stderr, "ERROR refcount block %d is not "
|
||||
"cluster aligned; refcount table entry corrupted\n", i);
|
||||
res->corruptions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cluster >= nb_clusters) {
|
||||
fprintf(stderr, "ERROR refcount block %" PRId64
|
||||
" is outside image\n", i);
|
||||
fprintf(stderr, "ERROR refcount block %d is outside image\n", i);
|
||||
res->corruptions++;
|
||||
continue;
|
||||
}
|
||||
@@ -1178,8 +1186,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
||||
offset, s->cluster_size);
|
||||
if (refcount_table[cluster] != 1) {
|
||||
fprintf(stderr, "ERROR refcount block %" PRId64
|
||||
" refcount=%d\n",
|
||||
fprintf(stderr, "ERROR refcount block %d refcount=%d\n",
|
||||
i, refcount_table[cluster]);
|
||||
res->corruptions++;
|
||||
}
|
||||
@@ -1190,7 +1197,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
for(i = 0; i < nb_clusters; i++) {
|
||||
refcount1 = get_refcount(bs, i);
|
||||
if (refcount1 < 0) {
|
||||
fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
|
||||
fprintf(stderr, "Can't get refcount for cluster %d: %s\n",
|
||||
i, strerror(-refcount1));
|
||||
res->check_errors++;
|
||||
continue;
|
||||
@@ -1198,31 +1205,9 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
|
||||
refcount2 = refcount_table[i];
|
||||
if (refcount1 != refcount2) {
|
||||
|
||||
/* Check if we're allowed to fix the mismatch */
|
||||
int *num_fixed = NULL;
|
||||
if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) {
|
||||
num_fixed = &res->leaks_fixed;
|
||||
} else if (refcount1 < refcount2 && (fix & BDRV_FIX_ERRORS)) {
|
||||
num_fixed = &res->corruptions_fixed;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s cluster %" PRId64 " refcount=%d reference=%d\n",
|
||||
num_fixed != NULL ? "Repairing" :
|
||||
refcount1 < refcount2 ? "ERROR" :
|
||||
"Leaked",
|
||||
fprintf(stderr, "%s cluster %d refcount=%d reference=%d\n",
|
||||
refcount1 < refcount2 ? "ERROR" : "Leaked",
|
||||
i, refcount1, refcount2);
|
||||
|
||||
if (num_fixed) {
|
||||
ret = update_refcount(bs, i << s->cluster_bits, 1,
|
||||
refcount2 - refcount1);
|
||||
if (ret >= 0) {
|
||||
(*num_fixed)++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* And if we couldn't, print an error */
|
||||
if (refcount1 < refcount2) {
|
||||
res->corruptions++;
|
||||
} else {
|
||||
|
||||
@@ -405,7 +405,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
qcow2_check_refcounts(bs, &result, 0);
|
||||
qcow2_check_refcounts(bs, &result);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
@@ -522,7 +522,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
qcow2_check_refcounts(bs, &result, 0);
|
||||
qcow2_check_refcounts(bs, &result);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
@@ -582,7 +582,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
qcow2_check_refcounts(bs, &result, 0);
|
||||
qcow2_check_refcounts(bs, &result);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
+31
-148
@@ -52,7 +52,6 @@ typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t len;
|
||||
} QCowExtension;
|
||||
|
||||
#define QCOW2_EXT_MAGIC_END 0
|
||||
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
|
||||
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
|
||||
@@ -215,82 +214,13 @@ static void report_unsupported_feature(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the dirty bit and flushes afterwards if necessary.
|
||||
*
|
||||
* The incompatible_features bit is only set if the image file header was
|
||||
* updated successfully. Therefore it is not required to check the return
|
||||
* value of this function.
|
||||
*/
|
||||
static int qcow2_mark_dirty(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t val;
|
||||
int ret;
|
||||
|
||||
assert(s->qcow_version >= 3);
|
||||
|
||||
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
|
||||
return 0; /* already dirty */
|
||||
}
|
||||
|
||||
val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY);
|
||||
ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features),
|
||||
&val, sizeof(val));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = bdrv_flush(bs->file);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Only treat image as dirty if the header was updated successfully */
|
||||
s->incompatible_features |= QCOW2_INCOMPAT_DIRTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the dirty bit and flushes before if necessary. Only call this
|
||||
* function when there are no pending requests, it does not guard against
|
||||
* concurrent requests dirtying the image.
|
||||
*/
|
||||
static int qcow2_mark_clean(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
|
||||
int ret = bdrv_flush(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
|
||||
return qcow2_update_header(bs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
int ret = qcow2_check_refcounts(bs, result, fix);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fix && result->check_errors == 0 && result->corruptions == 0) {
|
||||
return qcow2_mark_clean(bs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, ret = 0;
|
||||
QCowHeader header;
|
||||
uint64_t ext_end;
|
||||
bool writethrough;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
@@ -358,13 +288,12 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
s->compatible_features = header.compatible_features;
|
||||
s->autoclear_features = header.autoclear_features;
|
||||
|
||||
if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
|
||||
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 &
|
||||
~QCOW2_INCOMPAT_MASK);
|
||||
s->incompatible_features);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
@@ -430,8 +359,10 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
}
|
||||
|
||||
/* alloc L2 table/refcount block cache */
|
||||
s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE);
|
||||
s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE);
|
||||
writethrough = ((flags & BDRV_O_CACHE_WB) == 0);
|
||||
s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE, writethrough);
|
||||
s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE,
|
||||
writethrough);
|
||||
|
||||
s->cluster_cache = g_malloc(s->cluster_size);
|
||||
/* one more sector for decompressed data alignment */
|
||||
@@ -484,21 +415,10 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
/* Initialise locks */
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
/* Repair image if dirty */
|
||||
if (!(flags & BDRV_O_CHECK) && !bs->read_only &&
|
||||
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
qcow2_check_refcounts(bs, &result, 0);
|
||||
qcow2_check_refcounts(bs, &result);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
@@ -559,14 +479,6 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We have nothing to do for QCOW2 reopen, stubs just return
|
||||
* success */
|
||||
static int qcow2_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
@@ -599,7 +511,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
else
|
||||
n1 = bs->total_sectors - sector_num;
|
||||
|
||||
qemu_iovec_memset(qiov, 512 * n1, 0, 512 * (nb_sectors - n1));
|
||||
qemu_iovec_memset_skip(qiov, 0, 512 * (nb_sectors - n1), 512 * n1);
|
||||
|
||||
return n1;
|
||||
}
|
||||
@@ -638,7 +550,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done,
|
||||
qemu_iovec_copy(&hd_qiov, qiov, bytes_done,
|
||||
cur_nr_sectors * 512);
|
||||
|
||||
switch (ret) {
|
||||
@@ -660,7 +572,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
} else {
|
||||
/* Note: in this case, no need to wait */
|
||||
qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors);
|
||||
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -669,7 +581,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors);
|
||||
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
@@ -679,7 +591,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_iovec_from_buf(&hd_qiov, 0,
|
||||
qemu_iovec_from_buffer(&hd_qiov,
|
||||
s->cluster_cache + index_in_cluster * 512,
|
||||
512 * cur_nr_sectors);
|
||||
break;
|
||||
@@ -719,8 +631,11 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
if (s->crypt_method) {
|
||||
qcow2_encrypt_sectors(s, sector_num, cluster_data,
|
||||
cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
|
||||
qemu_iovec_from_buf(qiov, bytes_done,
|
||||
cluster_data, 512 * cur_nr_sectors);
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_copy(&hd_qiov, qiov, bytes_done,
|
||||
cur_nr_sectors * 512);
|
||||
qemu_iovec_from_buffer(&hd_qiov, cluster_data,
|
||||
512 * cur_nr_sectors);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -805,16 +720,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (l2meta.nb_clusters > 0 &&
|
||||
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)) {
|
||||
qcow2_mark_dirty(bs);
|
||||
}
|
||||
|
||||
cluster_offset = l2meta.cluster_offset;
|
||||
assert((cluster_offset & 511) == 0);
|
||||
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done,
|
||||
qemu_iovec_copy(&hd_qiov, qiov, bytes_done,
|
||||
cur_nr_sectors * 512);
|
||||
|
||||
if (s->crypt_method) {
|
||||
@@ -825,7 +735,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
|
||||
assert(hd_qiov.size <=
|
||||
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
|
||||
qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
|
||||
qemu_iovec_to_buffer(&hd_qiov, cluster_data);
|
||||
|
||||
qcow2_encrypt_sectors(s, sector_num, cluster_data,
|
||||
cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
|
||||
@@ -881,8 +791,6 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
|
||||
qcow2_mark_clean(bs);
|
||||
|
||||
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||
qcow2_cache_destroy(bs, s->refcount_block_cache);
|
||||
|
||||
@@ -1047,16 +955,7 @@ int qcow2_update_header(BlockDriverState *bs)
|
||||
|
||||
/* Feature table */
|
||||
Qcow2Feature features[] = {
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||
.name = "dirty bit",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
|
||||
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
||||
.name = "lazy refcounts",
|
||||
},
|
||||
/* no feature defined yet */
|
||||
};
|
||||
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
|
||||
@@ -1096,7 +995,6 @@ int qcow2_update_header(BlockDriverState *bs)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Using strncpy is ok here, since buf is not NUL-terminated. */
|
||||
strncpy(buf, bs->backing_file, buflen);
|
||||
|
||||
header->backing_file_offset = cpu_to_be64(buf - ((char*) header));
|
||||
@@ -1240,11 +1138,6 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
}
|
||||
|
||||
if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
|
||||
header.compatible_features |=
|
||||
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -1358,8 +1251,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
|
||||
options->value.s);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
|
||||
flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
@@ -1370,12 +1261,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
|
||||
fprintf(stderr, "Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
|
||||
cluster_size, prealloc, options, version);
|
||||
}
|
||||
@@ -1562,12 +1447,10 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
@@ -1587,6 +1470,12 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result)
|
||||
{
|
||||
return qcow2_check_refcounts(bs, result);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void dump_refcounts(BlockDriverState *bs)
|
||||
{
|
||||
@@ -1675,11 +1564,6 @@ static QEMUOptionParameter qcow2_create_options[] = {
|
||||
.type = OPT_STRING,
|
||||
.help = "Preallocation mode (allowed values: off, metadata)"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_LAZY_REFCOUNTS,
|
||||
.type = OPT_FLAG,
|
||||
.help = "Postpone refcount updates",
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -1689,7 +1573,6 @@ static BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_probe = qcow2_probe,
|
||||
.bdrv_open = qcow2_open,
|
||||
.bdrv_close = qcow2_close,
|
||||
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
||||
.bdrv_create = qcow2_create,
|
||||
.bdrv_co_is_allocated = qcow2_co_is_allocated,
|
||||
.bdrv_set_key = qcow2_set_key,
|
||||
|
||||
+5
-24
@@ -110,22 +110,6 @@ enum {
|
||||
QCOW2_FEAT_TYPE_AUTOCLEAR = 2,
|
||||
};
|
||||
|
||||
/* Incompatible feature bits */
|
||||
enum {
|
||||
QCOW2_INCOMPAT_DIRTY_BITNR = 0,
|
||||
QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||
|
||||
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY,
|
||||
};
|
||||
|
||||
/* Compatible feature bits */
|
||||
enum {
|
||||
QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0,
|
||||
QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
||||
|
||||
QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS,
|
||||
};
|
||||
|
||||
typedef struct Qcow2Feature {
|
||||
uint8_t type;
|
||||
uint8_t bit;
|
||||
@@ -253,11 +237,6 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether refcounts are eager or lazy */
|
||||
static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
|
||||
{
|
||||
return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY);
|
||||
}
|
||||
|
||||
// FIXME Need qcow2_ prefix to global functions
|
||||
|
||||
@@ -282,8 +261,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs,
|
||||
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size, int addend);
|
||||
|
||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix);
|
||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
|
||||
|
||||
/* qcow2-cluster.c functions */
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);
|
||||
@@ -318,8 +296,11 @@ void qcow2_free_snapshots(BlockDriverState *bs);
|
||||
int qcow2_read_snapshots(BlockDriverState *bs);
|
||||
|
||||
/* qcow2-cache.c functions */
|
||||
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables);
|
||||
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
|
||||
bool writethrough);
|
||||
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
|
||||
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
|
||||
bool enable);
|
||||
|
||||
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
|
||||
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
|
||||
|
||||
@@ -87,7 +87,6 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
|
||||
if (!qed_check_cluster_offset(s, offset)) {
|
||||
if (check->fix) {
|
||||
table->offsets[i] = 0;
|
||||
check->result->corruptions_fixed++;
|
||||
} else {
|
||||
check->result->corruptions++;
|
||||
}
|
||||
@@ -128,7 +127,6 @@ static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
|
||||
/* Clear invalid offset */
|
||||
if (check->fix) {
|
||||
table->offsets[i] = 0;
|
||||
check->result->corruptions_fixed++;
|
||||
} else {
|
||||
check->result->corruptions++;
|
||||
}
|
||||
@@ -194,28 +192,6 @@ static void qed_check_for_leaks(QEDCheck *check)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an image clean once it passes check or has been repaired
|
||||
*/
|
||||
static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
|
||||
{
|
||||
/* Skip if there were unfixable corruptions or I/O errors */
|
||||
if (result->corruptions > 0 || result->check_errors > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip if image is already marked clean */
|
||||
if (!(s->header.features & QED_F_NEED_CHECK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure fixes reach storage before clearing check bit */
|
||||
bdrv_flush(s->bs);
|
||||
|
||||
s->header.features &= ~QED_F_NEED_CHECK;
|
||||
qed_write_header_sync(s);
|
||||
}
|
||||
|
||||
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
{
|
||||
QEDCheck check = {
|
||||
@@ -237,10 +213,6 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
if (ret == 0) {
|
||||
/* Only check for leaks if entire image was scanned successfully */
|
||||
qed_check_for_leaks(&check);
|
||||
|
||||
if (fix) {
|
||||
qed_check_mark_clean(s, result);
|
||||
}
|
||||
}
|
||||
|
||||
g_free(check.used_clusters);
|
||||
|
||||
@@ -103,6 +103,7 @@ static void qed_write_table_cb(void *opaque, int ret)
|
||||
out:
|
||||
qemu_vfree(write_table_cb->table);
|
||||
gencb_complete(&write_table_cb->gencb, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+19
-33
@@ -30,7 +30,7 @@ static void qed_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo qed_aiocb_info = {
|
||||
static AIOPool qed_aio_pool = {
|
||||
.aiocb_size = sizeof(QEDAIOCB),
|
||||
.cancel = qed_aio_cancel,
|
||||
};
|
||||
@@ -89,7 +89,7 @@ static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le)
|
||||
le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size);
|
||||
}
|
||||
|
||||
int qed_write_header_sync(BDRVQEDState *s)
|
||||
static int qed_write_header_sync(BDRVQEDState *s)
|
||||
{
|
||||
QEDHeader le;
|
||||
int ret;
|
||||
@@ -477,7 +477,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
}
|
||||
|
||||
/* If image was not closed cleanly, check consistency */
|
||||
if (!(flags & BDRV_O_CHECK) && (s->header.features & QED_F_NEED_CHECK)) {
|
||||
if (s->header.features & QED_F_NEED_CHECK) {
|
||||
/* Read-only images cannot be fixed. There is no risk of corruption
|
||||
* since write operations are not possible. Therefore, allow
|
||||
* potentially inconsistent images to be opened read-only. This can
|
||||
@@ -491,6 +491,13 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
if (!result.corruptions && !result.check_errors) {
|
||||
/* Ensure fixes reach storage before clearing check bit */
|
||||
bdrv_flush(s->bs);
|
||||
|
||||
s->header.features &= ~QED_F_NEED_CHECK;
|
||||
qed_write_header_sync(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,14 +512,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We have nothing to do for QED reopen, stubs just return
|
||||
* success */
|
||||
static int bdrv_qed_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bdrv_qed_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
@@ -737,7 +736,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
/* Zero all sectors if reading beyond the end of the backing file */
|
||||
if (pos >= backing_length ||
|
||||
pos + qiov->size > backing_length) {
|
||||
qemu_iovec_memset(qiov, 0, 0, qiov->size);
|
||||
qemu_iovec_memset(qiov, 0, qiov->size);
|
||||
}
|
||||
|
||||
/* Complete now if there are no backing file sectors to read */
|
||||
@@ -749,7 +748,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
/* If the read straddles the end of the backing file, shorten it */
|
||||
size = MIN((uint64_t)backing_length - pos, qiov->size);
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
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);
|
||||
}
|
||||
@@ -1132,7 +1131,7 @@ 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);
|
||||
qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
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 */
|
||||
@@ -1178,7 +1177,7 @@ static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
|
||||
|
||||
/* Calculate the I/O vector */
|
||||
acb->cur_cluster = offset;
|
||||
qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
|
||||
/* Do the actual write */
|
||||
qed_aio_write_main(acb, 0);
|
||||
@@ -1248,11 +1247,11 @@ static void qed_aio_read_data(void *opaque, int ret,
|
||||
goto err;
|
||||
}
|
||||
|
||||
qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
|
||||
|
||||
/* Handle zero cluster and backing file reads */
|
||||
if (ret == QED_CLUSTER_ZERO) {
|
||||
qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size);
|
||||
qemu_iovec_memset(&acb->cur_qiov, 0, acb->cur_qiov.size);
|
||||
qed_aio_next_io(acb, 0);
|
||||
return;
|
||||
} else if (ret != QED_CLUSTER_FOUND) {
|
||||
@@ -1311,7 +1310,7 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, int flags)
|
||||
{
|
||||
QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, cb, opaque);
|
||||
QEDAIOCB *acb = qemu_aio_get(&qed_aio_pool, bs, cb, opaque);
|
||||
|
||||
trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors,
|
||||
opaque, flags);
|
||||
@@ -1371,21 +1370,10 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
|
||||
int nb_sectors)
|
||||
{
|
||||
BlockDriverAIOCB *blockacb;
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
QEDWriteZeroesCB cb = { .done = false };
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov;
|
||||
|
||||
/* Refuse if there are untouched backing file sectors */
|
||||
if (bs->backing_hd) {
|
||||
if (qed_offset_into_cluster(s, sector_num * BDRV_SECTOR_SIZE) != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (qed_offset_into_cluster(s, nb_sectors * BDRV_SECTOR_SIZE) != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* Zero writes start without an I/O buffer. If a buffer becomes necessary
|
||||
* then it will be allocated during request processing.
|
||||
*/
|
||||
@@ -1529,12 +1517,11 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
|
||||
bdrv_qed_open(bs, bs->open_flags);
|
||||
}
|
||||
|
||||
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
|
||||
return qed_check(s, result, !!fix);
|
||||
return qed_check(s, result, false);
|
||||
}
|
||||
|
||||
static QEMUOptionParameter qed_create_options[] = {
|
||||
@@ -1572,7 +1559,6 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_rebind = bdrv_qed_rebind,
|
||||
.bdrv_open = bdrv_qed_open,
|
||||
.bdrv_close = bdrv_qed_close,
|
||||
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
||||
.bdrv_create = bdrv_qed_create,
|
||||
.bdrv_co_is_allocated = bdrv_qed_co_is_allocated,
|
||||
.bdrv_make_empty = bdrv_qed_make_empty,
|
||||
|
||||
@@ -210,11 +210,6 @@ typedef struct {
|
||||
void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void gencb_complete(void *opaque, int ret);
|
||||
|
||||
/**
|
||||
* Header functions
|
||||
*/
|
||||
int qed_write_header_sync(BDRVQEDState *s);
|
||||
|
||||
/**
|
||||
* L2 cache functions
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Declarations for AIO in the raw protocol
|
||||
* QEMU Posix block I/O backend AIO support
|
||||
*
|
||||
* Copyright IBM, Corp. 2008
|
||||
*
|
||||
@@ -12,8 +12,8 @@
|
||||
* 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_AIO_H
|
||||
#define QEMU_RAW_AIO_H
|
||||
#ifndef QEMU_RAW_POSIX_AIO_H
|
||||
#define QEMU_RAW_POSIX_AIO_H
|
||||
|
||||
/* AIO request types */
|
||||
#define QEMU_AIO_READ 0x0001
|
||||
@@ -27,22 +27,19 @@
|
||||
#define QEMU_AIO_MISALIGNED 0x1000
|
||||
|
||||
|
||||
/* posix-aio-compat.c - thread pool based implementation */
|
||||
int paio_init(void);
|
||||
BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
|
||||
/* linux-aio.c - Linux native implementation */
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
void *laio_init(void);
|
||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef struct QEMUWin32AIOState QEMUWin32AIOState;
|
||||
QEMUWin32AIOState *win32_aio_init(void);
|
||||
int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile);
|
||||
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
QEMUWin32AIOState *aio, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
#endif
|
||||
|
||||
#endif /* QEMU_RAW_AIO_H */
|
||||
#endif /* QEMU_RAW_POSIX_AIO_H */
|
||||
+83
-603
@@ -27,10 +27,7 @@
|
||||
#include "qemu-log.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "trace.h"
|
||||
#include "thread-pool.h"
|
||||
#include "iov.h"
|
||||
#include "raw-aio.h"
|
||||
#include "block/raw-posix-aio.h"
|
||||
|
||||
#if defined(__APPLE__) && (__MACH__)
|
||||
#include <paths.h>
|
||||
@@ -55,10 +52,6 @@
|
||||
#include <sys/param.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <linux/fd.h>
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#ifdef CONFIG_FIEMAP
|
||||
#include <linux/fiemap.h>
|
||||
#endif
|
||||
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <sys/disk.h>
|
||||
@@ -136,36 +129,16 @@ typedef struct BDRVRawState {
|
||||
int use_aio;
|
||||
void *aio_ctx;
|
||||
#endif
|
||||
uint8_t *aligned_buf;
|
||||
unsigned aligned_buf_size;
|
||||
#ifdef CONFIG_XFS
|
||||
bool is_xfs : 1;
|
||||
#endif
|
||||
} BDRVRawState;
|
||||
|
||||
typedef struct BDRVRawReopenState {
|
||||
int fd;
|
||||
int open_flags;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
int use_aio;
|
||||
#endif
|
||||
} BDRVRawReopenState;
|
||||
|
||||
static int fd_open(BlockDriverState *bs);
|
||||
static int64_t raw_getlength(BlockDriverState *bs);
|
||||
|
||||
typedef struct RawPosixAIOData {
|
||||
BlockDriverState *bs;
|
||||
int aio_fildes;
|
||||
union {
|
||||
struct iovec *aio_iov;
|
||||
void *aio_ioctl_buf;
|
||||
};
|
||||
int aio_niov;
|
||||
size_t aio_nbytes;
|
||||
#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
|
||||
off_t aio_offset;
|
||||
int aio_type;
|
||||
} RawPosixAIOData;
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
static int cdrom_reopen(BlockDriverState *bs);
|
||||
#endif
|
||||
@@ -208,57 +181,6 @@ static int raw_normalize_devicepath(const char **filename)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void raw_parse_flags(int bdrv_flags, int *open_flags)
|
||||
{
|
||||
assert(open_flags != NULL);
|
||||
|
||||
*open_flags |= O_BINARY;
|
||||
*open_flags &= ~O_ACCMODE;
|
||||
if (bdrv_flags & BDRV_O_RDWR) {
|
||||
*open_flags |= O_RDWR;
|
||||
} else {
|
||||
*open_flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
/* Use O_DSYNC for write-through caching, no flags for write-back caching,
|
||||
* and O_DIRECT for no caching. */
|
||||
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
||||
*open_flags |= O_DIRECT;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
static int raw_set_aio(void **aio_ctx, int *use_aio, int bdrv_flags)
|
||||
{
|
||||
int ret = -1;
|
||||
assert(aio_ctx != NULL);
|
||||
assert(use_aio != NULL);
|
||||
/*
|
||||
* Currently Linux do AIO only for files opened with O_DIRECT
|
||||
* specified so check NOCACHE flag too
|
||||
*/
|
||||
if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) ==
|
||||
(BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) {
|
||||
|
||||
/* if non-NULL, laio_init() has already been run */
|
||||
if (*aio_ctx == NULL) {
|
||||
*aio_ctx = laio_init();
|
||||
if (!*aio_ctx) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
*use_aio = 1;
|
||||
} else {
|
||||
*use_aio = 0;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int raw_open_common(BlockDriverState *bs, const char *filename,
|
||||
int bdrv_flags, int open_flags)
|
||||
{
|
||||
@@ -270,8 +192,20 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
|
||||
return ret;
|
||||
}
|
||||
|
||||
s->open_flags = open_flags;
|
||||
raw_parse_flags(bdrv_flags, &s->open_flags);
|
||||
s->open_flags = open_flags | O_BINARY;
|
||||
s->open_flags &= ~O_ACCMODE;
|
||||
if (bdrv_flags & BDRV_O_RDWR) {
|
||||
s->open_flags |= O_RDWR;
|
||||
} else {
|
||||
s->open_flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
/* Use O_DSYNC for write-through caching, no flags for write-back caching,
|
||||
* and O_DIRECT for no caching. */
|
||||
if ((bdrv_flags & BDRV_O_NOCACHE))
|
||||
s->open_flags |= O_DIRECT;
|
||||
if (!(bdrv_flags & BDRV_O_CACHE_WB))
|
||||
s->open_flags |= O_DSYNC;
|
||||
|
||||
s->fd = -1;
|
||||
fd = qemu_open(filename, s->open_flags, 0644);
|
||||
@@ -282,13 +216,45 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
|
||||
return ret;
|
||||
}
|
||||
s->fd = fd;
|
||||
s->aligned_buf = NULL;
|
||||
|
||||
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
||||
/*
|
||||
* Allocate a buffer for read/modify/write cycles. Chose the size
|
||||
* pessimistically as we don't know the block size yet.
|
||||
*/
|
||||
s->aligned_buf_size = 32 * MAX_BLOCKSIZE;
|
||||
s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size);
|
||||
if (s->aligned_buf == NULL) {
|
||||
goto out_close;
|
||||
}
|
||||
}
|
||||
|
||||
/* We're falling back to POSIX AIO in some cases so init always */
|
||||
if (paio_init() < 0) {
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) {
|
||||
qemu_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
/*
|
||||
* Currently Linux do AIO only for files opened with O_DIRECT
|
||||
* specified so check NOCACHE flag too
|
||||
*/
|
||||
if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) ==
|
||||
(BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) {
|
||||
|
||||
s->aio_ctx = laio_init();
|
||||
if (!s->aio_ctx) {
|
||||
goto out_free_buf;
|
||||
}
|
||||
s->use_aio = 1;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
s->use_aio = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
if (platform_test_xfs_fd(s->fd)) {
|
||||
@@ -297,6 +263,12 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_buf:
|
||||
qemu_vfree(s->aligned_buf);
|
||||
out_close:
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
@@ -307,113 +279,6 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return raw_open_common(bs, filename, flags, 0);
|
||||
}
|
||||
|
||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
BDRVRawState *s;
|
||||
BDRVRawReopenState *raw_s;
|
||||
int ret = 0;
|
||||
|
||||
assert(state != NULL);
|
||||
assert(state->bs != NULL);
|
||||
|
||||
s = state->bs->opaque;
|
||||
|
||||
state->opaque = g_malloc0(sizeof(BDRVRawReopenState));
|
||||
raw_s = state->opaque;
|
||||
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
raw_s->use_aio = s->use_aio;
|
||||
|
||||
/* we can use s->aio_ctx instead of a copy, because the use_aio flag is
|
||||
* valid in the 'false' condition even if aio_ctx is set, and raw_set_aio()
|
||||
* won't override aio_ctx if aio_ctx is non-NULL */
|
||||
if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->type == FTYPE_FD || s->type == FTYPE_CD) {
|
||||
raw_s->open_flags |= O_NONBLOCK;
|
||||
}
|
||||
|
||||
raw_parse_flags(state->flags, &raw_s->open_flags);
|
||||
|
||||
raw_s->fd = -1;
|
||||
|
||||
int fcntl_flags = O_APPEND | O_ASYNC | O_NONBLOCK;
|
||||
#ifdef O_NOATIME
|
||||
fcntl_flags |= O_NOATIME;
|
||||
#endif
|
||||
|
||||
if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
|
||||
/* dup the original fd */
|
||||
/* TODO: use qemu fcntl wrapper */
|
||||
#ifdef F_DUPFD_CLOEXEC
|
||||
raw_s->fd = fcntl(s->fd, F_DUPFD_CLOEXEC, 0);
|
||||
#else
|
||||
raw_s->fd = dup(s->fd);
|
||||
if (raw_s->fd != -1) {
|
||||
qemu_set_cloexec(raw_s->fd);
|
||||
}
|
||||
#endif
|
||||
if (raw_s->fd >= 0) {
|
||||
ret = fcntl_setfl(raw_s->fd, raw_s->open_flags);
|
||||
if (ret) {
|
||||
qemu_close(raw_s->fd);
|
||||
raw_s->fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
|
||||
if (raw_s->fd == -1) {
|
||||
assert(!(raw_s->open_flags & O_CREAT));
|
||||
raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags);
|
||||
if (raw_s->fd == -1) {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void raw_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
BDRVRawReopenState *raw_s = state->opaque;
|
||||
BDRVRawState *s = state->bs->opaque;
|
||||
|
||||
s->open_flags = raw_s->open_flags;
|
||||
|
||||
qemu_close(s->fd);
|
||||
s->fd = raw_s->fd;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
s->use_aio = raw_s->use_aio;
|
||||
#endif
|
||||
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void raw_reopen_abort(BDRVReopenState *state)
|
||||
{
|
||||
BDRVRawReopenState *raw_s = state->opaque;
|
||||
|
||||
/* nothing to do if NULL, we didn't get far enough */
|
||||
if (raw_s == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (raw_s->fd >= 0) {
|
||||
qemu_close(raw_s->fd);
|
||||
raw_s->fd = -1;
|
||||
}
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* XXX: use host sector size if necessary with:
|
||||
#ifdef DIOCGSECTORSIZE
|
||||
{
|
||||
@@ -447,275 +312,6 @@ static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf);
|
||||
if (ret == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_flush(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qemu_fdatasync(aiocb->aio_fildes);
|
||||
if (ret == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PREADV
|
||||
|
||||
static bool preadv_present = true;
|
||||
|
||||
static ssize_t
|
||||
qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
||||
{
|
||||
return preadv(fd, iov, nr_iov, offset);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
||||
{
|
||||
return pwritev(fd, iov, nr_iov, offset);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static bool preadv_present = false;
|
||||
|
||||
static ssize_t
|
||||
qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
if (aiocb->aio_type & QEMU_AIO_WRITE)
|
||||
len = qemu_pwritev(aiocb->aio_fildes,
|
||||
aiocb->aio_iov,
|
||||
aiocb->aio_niov,
|
||||
aiocb->aio_offset);
|
||||
else
|
||||
len = qemu_preadv(aiocb->aio_fildes,
|
||||
aiocb->aio_iov,
|
||||
aiocb->aio_niov,
|
||||
aiocb->aio_offset);
|
||||
} while (len == -1 && errno == EINTR);
|
||||
|
||||
if (len == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/writes the data to/from a given linear buffer.
|
||||
*
|
||||
* Returns the number of bytes handles or -errno in case of an error. Short
|
||||
* reads are only returned if the end of the file is reached.
|
||||
*/
|
||||
static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf)
|
||||
{
|
||||
ssize_t offset = 0;
|
||||
ssize_t len;
|
||||
|
||||
while (offset < aiocb->aio_nbytes) {
|
||||
if (aiocb->aio_type & QEMU_AIO_WRITE) {
|
||||
len = pwrite(aiocb->aio_fildes,
|
||||
(const char *)buf + offset,
|
||||
aiocb->aio_nbytes - offset,
|
||||
aiocb->aio_offset + offset);
|
||||
} else {
|
||||
len = pread(aiocb->aio_fildes,
|
||||
buf + offset,
|
||||
aiocb->aio_nbytes - offset,
|
||||
aiocb->aio_offset + offset);
|
||||
}
|
||||
if (len == -1 && errno == EINTR) {
|
||||
continue;
|
||||
} else if (len == -1) {
|
||||
offset = -errno;
|
||||
break;
|
||||
} else if (len == 0) {
|
||||
break;
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb)
|
||||
{
|
||||
ssize_t nbytes;
|
||||
char *buf;
|
||||
|
||||
if (!(aiocb->aio_type & QEMU_AIO_MISALIGNED)) {
|
||||
/*
|
||||
* If there is just a single buffer, and it is properly aligned
|
||||
* we can just use plain pread/pwrite without any problems.
|
||||
*/
|
||||
if (aiocb->aio_niov == 1) {
|
||||
return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base);
|
||||
}
|
||||
/*
|
||||
* We have more than one iovec, and all are properly aligned.
|
||||
*
|
||||
* Try preadv/pwritev first and fall back to linearizing the
|
||||
* buffer if it's not supported.
|
||||
*/
|
||||
if (preadv_present) {
|
||||
nbytes = handle_aiocb_rw_vector(aiocb);
|
||||
if (nbytes == aiocb->aio_nbytes ||
|
||||
(nbytes < 0 && nbytes != -ENOSYS)) {
|
||||
return nbytes;
|
||||
}
|
||||
preadv_present = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX(hch): short read/write. no easy way to handle the reminder
|
||||
* using these interfaces. For now retry using plain
|
||||
* pread/pwrite?
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we have to do it the hard way, copy all segments into
|
||||
* a single aligned buffer.
|
||||
*/
|
||||
buf = qemu_blockalign(aiocb->bs, aiocb->aio_nbytes);
|
||||
if (aiocb->aio_type & QEMU_AIO_WRITE) {
|
||||
char *p = buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < aiocb->aio_niov; ++i) {
|
||||
memcpy(p, aiocb->aio_iov[i].iov_base, aiocb->aio_iov[i].iov_len);
|
||||
p += aiocb->aio_iov[i].iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
nbytes = handle_aiocb_rw_linear(aiocb, buf);
|
||||
if (!(aiocb->aio_type & QEMU_AIO_WRITE)) {
|
||||
char *p = buf;
|
||||
size_t count = aiocb->aio_nbytes, copy;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < aiocb->aio_niov && count; ++i) {
|
||||
copy = count;
|
||||
if (copy > aiocb->aio_iov[i].iov_len) {
|
||||
copy = aiocb->aio_iov[i].iov_len;
|
||||
}
|
||||
memcpy(aiocb->aio_iov[i].iov_base, p, copy);
|
||||
p += copy;
|
||||
count -= copy;
|
||||
}
|
||||
}
|
||||
qemu_vfree(buf);
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static int aio_worker(void *arg)
|
||||
{
|
||||
RawPosixAIOData *aiocb = arg;
|
||||
ssize_t ret = 0;
|
||||
|
||||
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
|
||||
case QEMU_AIO_READ:
|
||||
ret = handle_aiocb_rw(aiocb);
|
||||
if (ret >= 0 && ret < aiocb->aio_nbytes && aiocb->bs->growable) {
|
||||
iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret,
|
||||
0, aiocb->aio_nbytes - ret);
|
||||
|
||||
ret = aiocb->aio_nbytes;
|
||||
}
|
||||
if (ret == aiocb->aio_nbytes) {
|
||||
ret = 0;
|
||||
} else if (ret >= 0 && ret < aiocb->aio_nbytes) {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case QEMU_AIO_WRITE:
|
||||
ret = handle_aiocb_rw(aiocb);
|
||||
if (ret == aiocb->aio_nbytes) {
|
||||
ret = 0;
|
||||
} else if (ret >= 0 && ret < aiocb->aio_nbytes) {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case QEMU_AIO_FLUSH:
|
||||
ret = handle_aiocb_flush(aiocb);
|
||||
break;
|
||||
case QEMU_AIO_IOCTL:
|
||||
ret = handle_aiocb_ioctl(aiocb);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
g_slice_free(RawPosixAIOData, aiocb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
|
||||
acb->bs = bs;
|
||||
acb->aio_type = type;
|
||||
acb->aio_fildes = fd;
|
||||
|
||||
if (qiov) {
|
||||
acb->aio_iov = qiov->iov;
|
||||
acb->aio_niov = qiov->niov;
|
||||
}
|
||||
acb->aio_nbytes = nb_sectors * 512;
|
||||
acb->aio_offset = sector_num * 512;
|
||||
|
||||
trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
|
||||
acb->bs = bs;
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = fd;
|
||||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
@@ -730,7 +326,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
|
||||
* boundary. Check if this is the case or tell the low-level
|
||||
* driver that it needs to copy the buffer.
|
||||
*/
|
||||
if ((bs->open_flags & BDRV_O_NOCACHE)) {
|
||||
if (s->aligned_buf) {
|
||||
if (!qiov_is_aligned(bs, qiov)) {
|
||||
type |= QEMU_AIO_MISALIGNED;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
@@ -776,8 +372,10 @@ static void raw_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->fd >= 0) {
|
||||
qemu_close(s->fd);
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
if (s->aligned_buf != NULL)
|
||||
qemu_vfree(s->aligned_buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,121 +568,21 @@ static int raw_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
} else {
|
||||
if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
|
||||
result = -errno;
|
||||
}
|
||||
if (qemu_close(fd) != 0) {
|
||||
if (close(fd) != 0) {
|
||||
result = -errno;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true iff the specified sector is present in the disk image. Drivers
|
||||
* not implementing the functionality are assumed to not support backing files,
|
||||
* hence all their sectors are reported as allocated.
|
||||
*
|
||||
* If 'sector_num' is beyond the end of the disk image the return value is 0
|
||||
* and 'pnum' is set to 0.
|
||||
*
|
||||
* '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.
|
||||
*
|
||||
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
||||
* beyond the end of the disk image it will be clamped.
|
||||
*/
|
||||
static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
off_t start, data, hole;
|
||||
int ret;
|
||||
|
||||
ret = fd_open(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
start = sector_num * BDRV_SECTOR_SIZE;
|
||||
|
||||
#ifdef CONFIG_FIEMAP
|
||||
|
||||
BDRVRawState *s = bs->opaque;
|
||||
struct {
|
||||
struct fiemap fm;
|
||||
struct fiemap_extent fe;
|
||||
} f;
|
||||
|
||||
f.fm.fm_start = start;
|
||||
f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE;
|
||||
f.fm.fm_flags = 0;
|
||||
f.fm.fm_extent_count = 1;
|
||||
f.fm.fm_reserved = 0;
|
||||
if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
|
||||
/* Assume everything is allocated. */
|
||||
*pnum = nb_sectors;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (f.fm.fm_mapped_extents == 0) {
|
||||
/* No extents found, data is beyond f.fm.fm_start + f.fm.fm_length.
|
||||
* f.fm.fm_start + f.fm.fm_length must be clamped to the file size!
|
||||
*/
|
||||
off_t length = lseek(s->fd, 0, SEEK_END);
|
||||
hole = f.fm.fm_start;
|
||||
data = MIN(f.fm.fm_start + f.fm.fm_length, length);
|
||||
} else {
|
||||
data = f.fe.fe_logical;
|
||||
hole = f.fe.fe_logical + f.fe.fe_length;
|
||||
}
|
||||
|
||||
#elif defined SEEK_HOLE && defined SEEK_DATA
|
||||
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
hole = lseek(s->fd, start, SEEK_HOLE);
|
||||
if (hole == -1) {
|
||||
/* -ENXIO indicates that sector_num was past the end of the file.
|
||||
* There is a virtual hole there. */
|
||||
assert(errno != -ENXIO);
|
||||
|
||||
/* Most likely EINVAL. Assume everything is allocated. */
|
||||
*pnum = nb_sectors;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (hole > start) {
|
||||
data = start;
|
||||
} else {
|
||||
/* On a hole. We need another syscall to find its end. */
|
||||
data = lseek(s->fd, start, SEEK_DATA);
|
||||
if (data == -1) {
|
||||
data = lseek(s->fd, 0, SEEK_END);
|
||||
}
|
||||
}
|
||||
#else
|
||||
*pnum = nb_sectors;
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
if (data <= start) {
|
||||
/* On a data extent, compute sectors to the end of the extent. */
|
||||
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
|
||||
return 1;
|
||||
} else {
|
||||
/* On a hole, compute sectors to the beginning of the next extent. */
|
||||
*pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
@@ -1133,13 +631,9 @@ static BlockDriver bdrv_file = {
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
.bdrv_probe = NULL, /* no probe for protocols */
|
||||
.bdrv_file_open = raw_open,
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_create = raw_create,
|
||||
.bdrv_co_discard = raw_co_discard,
|
||||
.bdrv_co_is_allocated = raw_co_is_allocated,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
@@ -1247,11 +741,11 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
if ( bsdPath[ 0 ] != '\0' ) {
|
||||
strcat(bsdPath,"s0");
|
||||
/* some CDs don't have a partition 0 */
|
||||
fd = qemu_open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
bsdPath[strlen(bsdPath)-1] = '1';
|
||||
} else {
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
}
|
||||
filename = bsdPath;
|
||||
}
|
||||
@@ -1290,7 +784,7 @@ static int fd_open(BlockDriverState *bs)
|
||||
last_media_present = (s->fd >= 0);
|
||||
if (s->fd >= 0 &&
|
||||
(get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
|
||||
qemu_close(s->fd);
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
#ifdef DEBUG_FLOPPY
|
||||
printf("Floppy closed\n");
|
||||
@@ -1304,7 +798,7 @@ static int fd_open(BlockDriverState *bs)
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK);
|
||||
s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK);
|
||||
if (s->fd < 0) {
|
||||
s->fd_error_time = get_clock();
|
||||
s->fd_got_error = 1;
|
||||
@@ -1378,7 +872,7 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_BINARY);
|
||||
fd = open(filename, O_WRONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -1389,7 +883,7 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options)
|
||||
else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE)
|
||||
ret = -ENOSPC;
|
||||
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1405,9 +899,6 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_probe_device = hdev_probe_device,
|
||||
.bdrv_file_open = hdev_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_create = hdev_create,
|
||||
.create_options = raw_create_options,
|
||||
.bdrv_has_zero_init = hdev_has_zero_init,
|
||||
@@ -1442,7 +933,7 @@ static int floppy_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return ret;
|
||||
|
||||
/* close fd so that we can reopen it as needed */
|
||||
qemu_close(s->fd);
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
s->fd_media_changed = 1;
|
||||
|
||||
@@ -1456,12 +947,10 @@ static int floppy_probe_device(const char *filename)
|
||||
struct floppy_struct fdparam;
|
||||
struct stat st;
|
||||
|
||||
if (strstart(filename, "/dev/fd", NULL) &&
|
||||
!strstart(filename, "/dev/fdset/", NULL)) {
|
||||
if (strstart(filename, "/dev/fd", NULL))
|
||||
prio = 50;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_RDONLY | O_NONBLOCK);
|
||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -1476,7 +965,7 @@ static int floppy_probe_device(const char *filename)
|
||||
prio = 100;
|
||||
|
||||
outc:
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
out:
|
||||
return prio;
|
||||
}
|
||||
@@ -1511,14 +1000,14 @@ static void floppy_eject(BlockDriverState *bs, bool eject_flag)
|
||||
int fd;
|
||||
|
||||
if (s->fd >= 0) {
|
||||
qemu_close(s->fd);
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
fd = qemu_open(bs->filename, s->open_flags | O_NONBLOCK);
|
||||
fd = open(bs->filename, s->open_flags | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
if (ioctl(fd, FDEJECT, 0) < 0)
|
||||
perror("FDEJECT");
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1529,9 +1018,6 @@ static BlockDriver bdrv_host_floppy = {
|
||||
.bdrv_probe_device = floppy_probe_device,
|
||||
.bdrv_file_open = floppy_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_create = hdev_create,
|
||||
.create_options = raw_create_options,
|
||||
.bdrv_has_zero_init = hdev_has_zero_init,
|
||||
@@ -1567,7 +1053,7 @@ static int cdrom_probe_device(const char *filename)
|
||||
int prio = 0;
|
||||
struct stat st;
|
||||
|
||||
fd = qemu_open(filename, O_RDONLY | O_NONBLOCK);
|
||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -1582,7 +1068,7 @@ static int cdrom_probe_device(const char *filename)
|
||||
prio = 100;
|
||||
|
||||
outc:
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
out:
|
||||
return prio;
|
||||
}
|
||||
@@ -1631,9 +1117,6 @@ static BlockDriver bdrv_host_cdrom = {
|
||||
.bdrv_probe_device = cdrom_probe_device,
|
||||
.bdrv_file_open = cdrom_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_create = hdev_create,
|
||||
.create_options = raw_create_options,
|
||||
.bdrv_has_zero_init = hdev_has_zero_init,
|
||||
@@ -1693,8 +1176,8 @@ static int cdrom_reopen(BlockDriverState *bs)
|
||||
* FreeBSD seems to not notice sometimes...
|
||||
*/
|
||||
if (s->fd >= 0)
|
||||
qemu_close(s->fd);
|
||||
fd = qemu_open(bs->filename, s->open_flags, 0644);
|
||||
close(s->fd);
|
||||
fd = open(bs->filename, s->open_flags, 0644);
|
||||
if (fd < 0) {
|
||||
s->fd = -1;
|
||||
return -EIO;
|
||||
@@ -1753,9 +1236,6 @@ static BlockDriver bdrv_host_cdrom = {
|
||||
.bdrv_probe_device = cdrom_probe_device,
|
||||
.bdrv_file_open = cdrom_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_create = hdev_create,
|
||||
.create_options = raw_create_options,
|
||||
.bdrv_has_zero_init = hdev_has_zero_init,
|
||||
|
||||
+72
-193
@@ -25,10 +25,6 @@
|
||||
#include "qemu-timer.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "raw-aio.h"
|
||||
#include "trace.h"
|
||||
#include "thread-pool.h"
|
||||
#include "iov.h"
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
@@ -36,130 +32,12 @@
|
||||
#define FTYPE_CD 1
|
||||
#define FTYPE_HARDDISK 2
|
||||
|
||||
static QEMUWin32AIOState *aio;
|
||||
|
||||
typedef struct RawWin32AIOData {
|
||||
BlockDriverState *bs;
|
||||
HANDLE hfile;
|
||||
struct iovec *aio_iov;
|
||||
int aio_niov;
|
||||
size_t aio_nbytes;
|
||||
off64_t aio_offset;
|
||||
int aio_type;
|
||||
} RawWin32AIOData;
|
||||
|
||||
typedef struct BDRVRawState {
|
||||
HANDLE hfile;
|
||||
int type;
|
||||
char drive_path[16]; /* format: "d:\" */
|
||||
QEMUWin32AIOState *aio;
|
||||
} BDRVRawState;
|
||||
|
||||
/*
|
||||
* Read/writes the data to/from a given linear buffer.
|
||||
*
|
||||
* Returns the number of bytes handles or -errno in case of an error. Short
|
||||
* reads are only returned if the end of the file is reached.
|
||||
*/
|
||||
static size_t handle_aiocb_rw(RawWin32AIOData *aiocb)
|
||||
{
|
||||
size_t offset = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < aiocb->aio_niov; i++) {
|
||||
OVERLAPPED ov;
|
||||
DWORD ret, ret_count, len;
|
||||
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
ov.Offset = (aiocb->aio_offset + offset);
|
||||
ov.OffsetHigh = (aiocb->aio_offset + offset) >> 32;
|
||||
len = aiocb->aio_iov[i].iov_len;
|
||||
if (aiocb->aio_type & QEMU_AIO_WRITE) {
|
||||
ret = WriteFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
|
||||
len, &ret_count, &ov);
|
||||
} else {
|
||||
ret = ReadFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
|
||||
len, &ret_count, &ov);
|
||||
}
|
||||
if (!ret) {
|
||||
ret_count = 0;
|
||||
}
|
||||
if (ret_count != len) {
|
||||
break;
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int aio_worker(void *arg)
|
||||
{
|
||||
RawWin32AIOData *aiocb = arg;
|
||||
ssize_t ret = 0;
|
||||
size_t count;
|
||||
|
||||
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
|
||||
case QEMU_AIO_READ:
|
||||
count = handle_aiocb_rw(aiocb);
|
||||
if (count < aiocb->aio_nbytes && aiocb->bs->growable) {
|
||||
/* A short read means that we have reached EOF. Pad the buffer
|
||||
* with zeros for bytes after EOF. */
|
||||
iov_memset(aiocb->aio_iov, aiocb->aio_niov, count,
|
||||
0, aiocb->aio_nbytes - count);
|
||||
|
||||
count = aiocb->aio_nbytes;
|
||||
}
|
||||
if (count == aiocb->aio_nbytes) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case QEMU_AIO_WRITE:
|
||||
count = handle_aiocb_rw(aiocb);
|
||||
if (count == aiocb->aio_nbytes) {
|
||||
count = 0;
|
||||
} else {
|
||||
count = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case QEMU_AIO_FLUSH:
|
||||
if (!FlushFileBuffers(aiocb->hfile)) {
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
g_slice_free(RawWin32AIOData, aiocb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawWin32AIOData *acb = g_slice_new(RawWin32AIOData);
|
||||
|
||||
acb->bs = bs;
|
||||
acb->hfile = hfile;
|
||||
acb->aio_type = type;
|
||||
|
||||
if (qiov) {
|
||||
acb->aio_iov = qiov->iov;
|
||||
acb->aio_niov = qiov->niov;
|
||||
}
|
||||
acb->aio_nbytes = nb_sectors * 512;
|
||||
acb->aio_offset = sector_num * 512;
|
||||
|
||||
trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
int qemu_ftruncate64(int fd, int64_t length)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
@@ -199,26 +77,6 @@ static int set_sparse(int fd)
|
||||
NULL, 0, NULL, 0, &returned, NULL);
|
||||
}
|
||||
|
||||
static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
|
||||
{
|
||||
assert(access_flags != NULL);
|
||||
assert(overlapped != NULL);
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
*access_flags = GENERIC_READ | GENERIC_WRITE;
|
||||
} else {
|
||||
*access_flags = GENERIC_READ;
|
||||
}
|
||||
|
||||
*overlapped = FILE_ATTRIBUTE_NORMAL;
|
||||
if (flags & BDRV_O_NATIVE_AIO) {
|
||||
*overlapped |= FILE_FLAG_OVERLAPPED;
|
||||
}
|
||||
if (flags & BDRV_O_NOCACHE) {
|
||||
*overlapped |= FILE_FLAG_NO_BUFFERING;
|
||||
}
|
||||
}
|
||||
|
||||
static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
@@ -227,15 +85,17 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
s->type = FTYPE_FILE;
|
||||
|
||||
raw_parse_flags(flags, &access_flags, &overlapped);
|
||||
|
||||
if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) {
|
||||
aio = win32_aio_init();
|
||||
if (aio == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
access_flags = GENERIC_READ | GENERIC_WRITE;
|
||||
} else {
|
||||
access_flags = GENERIC_READ;
|
||||
}
|
||||
|
||||
overlapped = FILE_ATTRIBUTE_NORMAL;
|
||||
if (flags & BDRV_O_NOCACHE)
|
||||
overlapped |= FILE_FLAG_NO_BUFFERING;
|
||||
if (!(flags & BDRV_O_CACHE_WB))
|
||||
overlapped |= FILE_FLAG_WRITE_THROUGH;
|
||||
s->hfile = CreateFile(filename, access_flags,
|
||||
FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, overlapped, NULL);
|
||||
@@ -244,53 +104,64 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
if (err == ERROR_ACCESS_DENIED)
|
||||
return -EACCES;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (flags & BDRV_O_NATIVE_AIO) {
|
||||
int ret = win32_aio_attach(aio, s->hfile);
|
||||
if (ret < 0) {
|
||||
CloseHandle(s->hfile);
|
||||
return ret;
|
||||
}
|
||||
s->aio = aio;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
static int raw_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||
nb_sectors, cb, opaque, QEMU_AIO_READ);
|
||||
} else {
|
||||
return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
|
||||
cb, opaque, QEMU_AIO_READ);
|
||||
}
|
||||
OVERLAPPED ov;
|
||||
DWORD ret_count;
|
||||
int ret;
|
||||
int64_t offset = sector_num * 512;
|
||||
int count = nb_sectors * 512;
|
||||
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
ov.Offset = offset;
|
||||
ov.OffsetHigh = offset >> 32;
|
||||
ret = ReadFile(s->hfile, buf, count, &ret_count, &ov);
|
||||
if (!ret)
|
||||
return ret_count;
|
||||
if (ret_count == count)
|
||||
ret_count = 0;
|
||||
return ret_count;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
static int raw_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||
nb_sectors, cb, opaque, QEMU_AIO_WRITE);
|
||||
} else {
|
||||
return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
|
||||
cb, opaque, QEMU_AIO_WRITE);
|
||||
}
|
||||
OVERLAPPED ov;
|
||||
DWORD ret_count;
|
||||
int ret;
|
||||
int64_t offset = sector_num * 512;
|
||||
int count = nb_sectors * 512;
|
||||
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
ov.Offset = offset;
|
||||
ov.OffsetHigh = offset >> 32;
|
||||
ret = WriteFile(s->hfile, buf, count, &ret_count, &ov);
|
||||
if (!ret)
|
||||
return ret_count;
|
||||
if (ret_count == count)
|
||||
ret_count = 0;
|
||||
return ret_count;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
static int raw_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
return paio_submit(bs, s->hfile, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
|
||||
int ret;
|
||||
|
||||
ret = FlushFileBuffers(s->hfile);
|
||||
if (ret == 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void raw_close(BlockDriverState *bs)
|
||||
@@ -384,13 +255,13 @@ static int raw_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0)
|
||||
return -EIO;
|
||||
set_sparse(fd);
|
||||
ftruncate(fd, total_size * 512);
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -411,9 +282,9 @@ static BlockDriver bdrv_file = {
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_create = raw_create,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_read = raw_read,
|
||||
.bdrv_write = raw_write,
|
||||
.bdrv_co_flush_to_disk = raw_flush,
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
@@ -503,10 +374,18 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
}
|
||||
s->type = find_device_type(bs, filename);
|
||||
|
||||
raw_parse_flags(flags, &access_flags, &overlapped);
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
access_flags = GENERIC_READ | GENERIC_WRITE;
|
||||
} else {
|
||||
access_flags = GENERIC_READ;
|
||||
}
|
||||
create_flags = OPEN_EXISTING;
|
||||
|
||||
overlapped = FILE_ATTRIBUTE_NORMAL;
|
||||
if (flags & BDRV_O_NOCACHE)
|
||||
overlapped |= FILE_FLAG_NO_BUFFERING;
|
||||
if (!(flags & BDRV_O_CACHE_WB))
|
||||
overlapped |= FILE_FLAG_WRITE_THROUGH;
|
||||
s->hfile = CreateFile(filename, access_flags,
|
||||
FILE_SHARE_READ, NULL,
|
||||
create_flags, overlapped, NULL);
|
||||
@@ -534,9 +413,9 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_has_zero_init = hdev_has_zero_init,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_read = raw_read,
|
||||
.bdrv_write = raw_write,
|
||||
.bdrv_co_flush_to_disk = raw_flush,
|
||||
|
||||
.bdrv_getlength = raw_getlength,
|
||||
.bdrv_get_allocated_file_size
|
||||
|
||||
-20
@@ -9,25 +9,15 @@ static int raw_open(BlockDriverState *bs, int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We have nothing to do for raw reopen, stubs just return
|
||||
* success */
|
||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
|
||||
@@ -35,13 +25,6 @@ static void raw_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
return bdrv_co_is_allocated(bs->file, sector_num, nb_sectors, pnum);
|
||||
}
|
||||
|
||||
static int64_t raw_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file);
|
||||
@@ -123,11 +106,8 @@ static BlockDriver bdrv_raw = {
|
||||
.bdrv_open = raw_open,
|
||||
.bdrv_close = raw_close,
|
||||
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
|
||||
.bdrv_co_readv = raw_co_readv,
|
||||
.bdrv_co_writev = raw_co_writev,
|
||||
.bdrv_co_is_allocated = raw_co_is_allocated,
|
||||
.bdrv_co_discard = raw_co_discard,
|
||||
|
||||
.bdrv_probe = raw_probe,
|
||||
|
||||
+6
-19
@@ -69,7 +69,7 @@ typedef enum {
|
||||
typedef struct RBDAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int64_t ret;
|
||||
int ret;
|
||||
QEMUIOVector *qiov;
|
||||
char *bounce;
|
||||
RBDAIOCmd cmd;
|
||||
@@ -86,7 +86,7 @@ typedef struct RADOSCB {
|
||||
int done;
|
||||
int64_t size;
|
||||
char *buf;
|
||||
int64_t ret;
|
||||
int ret;
|
||||
} RADOSCB;
|
||||
|
||||
#define RBD_FD_READ 0
|
||||
@@ -476,19 +476,6 @@ static int qemu_rbd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
s->snap = g_strdup(snap_buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fallback to more conservative semantics if setting cache
|
||||
* options fails. Ignore errors from setting rbd_cache because the
|
||||
* only possible error is that the option does not exist, and
|
||||
* librbd defaults to no caching. If write through caching cannot
|
||||
* be set up, fall back to no caching.
|
||||
*/
|
||||
if (flags & BDRV_O_NOCACHE) {
|
||||
rados_conf_set(s->cluster, "rbd_cache", "false");
|
||||
} else {
|
||||
rados_conf_set(s->cluster, "rbd_cache", "true");
|
||||
}
|
||||
|
||||
if (strstr(conf, "conf=") == NULL) {
|
||||
/* try default location, but ignore failure */
|
||||
rados_conf_read_file(s->cluster, NULL);
|
||||
@@ -570,7 +557,7 @@ static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
acb->cancelled = 1;
|
||||
}
|
||||
|
||||
static const AIOCBInfo rbd_aiocb_info = {
|
||||
static AIOPool rbd_aio_pool = {
|
||||
.aiocb_size = sizeof(RBDAIOCB),
|
||||
.cancel = qemu_rbd_aio_cancel,
|
||||
};
|
||||
@@ -633,7 +620,7 @@ static void rbd_aio_bh_cb(void *opaque)
|
||||
RBDAIOCB *acb = opaque;
|
||||
|
||||
if (acb->cmd == RBD_AIO_READ) {
|
||||
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
|
||||
qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
|
||||
}
|
||||
qemu_vfree(acb->bounce);
|
||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
@@ -672,7 +659,7 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
|
||||
acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque);
|
||||
acb->cmd = cmd;
|
||||
acb->qiov = qiov;
|
||||
if (cmd == RBD_AIO_DISCARD) {
|
||||
@@ -687,7 +674,7 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
acb->bh = NULL;
|
||||
|
||||
if (cmd == RBD_AIO_WRITE) {
|
||||
qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size);
|
||||
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
|
||||
}
|
||||
|
||||
buf = acb->bounce;
|
||||
|
||||
+183
-205
@@ -201,12 +201,12 @@ static inline uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval)
|
||||
return hval;
|
||||
}
|
||||
|
||||
static inline bool is_data_obj_writable(SheepdogInode *inode, unsigned int idx)
|
||||
static inline int is_data_obj_writable(SheepdogInode *inode, unsigned int idx)
|
||||
{
|
||||
return inode->vdi_id == inode->data_vdi_id[idx];
|
||||
}
|
||||
|
||||
static inline bool is_data_obj(uint64_t oid)
|
||||
static inline int is_data_obj(uint64_t oid)
|
||||
{
|
||||
return !(VDI_BIT & oid);
|
||||
}
|
||||
@@ -231,7 +231,7 @@ static inline uint64_t vid_to_data_oid(uint32_t vid, uint32_t idx)
|
||||
return ((uint64_t)vid << VDI_SPACE_SHIFT) | idx;
|
||||
}
|
||||
|
||||
static inline bool is_snapshot(struct SheepdogInode *inode)
|
||||
static inline int is_snapshot(struct SheepdogInode *inode)
|
||||
{
|
||||
return !!inode->snap_ctime;
|
||||
}
|
||||
@@ -259,7 +259,8 @@ typedef struct AIOReq {
|
||||
uint8_t flags;
|
||||
uint32_t id;
|
||||
|
||||
QLIST_ENTRY(AIOReq) aio_siblings;
|
||||
QLIST_ENTRY(AIOReq) outstanding_aio_siblings;
|
||||
QLIST_ENTRY(AIOReq) aioreq_siblings;
|
||||
} AIOReq;
|
||||
|
||||
enum AIOCBState {
|
||||
@@ -281,8 +282,9 @@ struct SheepdogAIOCB {
|
||||
Coroutine *coroutine;
|
||||
void (*aio_done_func)(SheepdogAIOCB *);
|
||||
|
||||
bool canceled;
|
||||
int nr_pending;
|
||||
int canceled;
|
||||
|
||||
QLIST_HEAD(aioreq_head, AIOReq) aioreq_head;
|
||||
};
|
||||
|
||||
typedef struct BDRVSheepdogState {
|
||||
@@ -292,8 +294,8 @@ typedef struct BDRVSheepdogState {
|
||||
uint32_t max_dirty_data_idx;
|
||||
|
||||
char name[SD_MAX_VDI_LEN];
|
||||
bool is_snapshot;
|
||||
bool cache_enabled;
|
||||
int is_snapshot;
|
||||
uint8_t cache_enabled;
|
||||
|
||||
char *addr;
|
||||
char *port;
|
||||
@@ -305,8 +307,7 @@ typedef struct BDRVSheepdogState {
|
||||
Coroutine *co_recv;
|
||||
|
||||
uint32_t aioreq_seq_num;
|
||||
QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head;
|
||||
QLIST_HEAD(pending_aio_head, AIOReq) pending_aio_head;
|
||||
QLIST_HEAD(outstanding_aio_head, AIOReq) outstanding_aio_head;
|
||||
} BDRVSheepdogState;
|
||||
|
||||
static const char * sd_strerror(int err)
|
||||
@@ -357,7 +358,7 @@ static const char * sd_strerror(int err)
|
||||
* Sheepdog I/O handling:
|
||||
*
|
||||
* 1. In sd_co_rw_vector, we send the I/O requests to the server and
|
||||
* link the requests to the inflight_list in the
|
||||
* link the requests to the outstanding_list in the
|
||||
* BDRVSheepdogState. The function exits without waiting for
|
||||
* receiving the response.
|
||||
*
|
||||
@@ -385,18 +386,21 @@ static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
|
||||
aio_req->flags = flags;
|
||||
aio_req->id = s->aioreq_seq_num++;
|
||||
|
||||
acb->nr_pending++;
|
||||
QLIST_INSERT_HEAD(&s->outstanding_aio_head, aio_req,
|
||||
outstanding_aio_siblings);
|
||||
QLIST_INSERT_HEAD(&acb->aioreq_head, aio_req, aioreq_siblings);
|
||||
|
||||
return aio_req;
|
||||
}
|
||||
|
||||
static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
|
||||
static inline int free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
|
||||
{
|
||||
SheepdogAIOCB *acb = aio_req->aiocb;
|
||||
|
||||
QLIST_REMOVE(aio_req, aio_siblings);
|
||||
QLIST_REMOVE(aio_req, outstanding_aio_siblings);
|
||||
QLIST_REMOVE(aio_req, aioreq_siblings);
|
||||
g_free(aio_req);
|
||||
|
||||
acb->nr_pending--;
|
||||
return !QLIST_EMPTY(&acb->aioreq_head);
|
||||
}
|
||||
|
||||
static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
|
||||
@@ -417,10 +421,10 @@ static void sd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
*/
|
||||
acb->ret = -EIO;
|
||||
qemu_coroutine_enter(acb->coroutine, NULL);
|
||||
acb->canceled = true;
|
||||
acb->canceled = 1;
|
||||
}
|
||||
|
||||
static const AIOCBInfo sd_aiocb_info = {
|
||||
static AIOPool sd_aio_pool = {
|
||||
.aiocb_size = sizeof(SheepdogAIOCB),
|
||||
.cancel = sd_aio_cancel,
|
||||
};
|
||||
@@ -431,7 +435,7 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
{
|
||||
SheepdogAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&sd_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&sd_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->qiov = qiov;
|
||||
|
||||
@@ -439,10 +443,10 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
acb->nb_sectors = nb_sectors;
|
||||
|
||||
acb->aio_done_func = NULL;
|
||||
acb->canceled = false;
|
||||
acb->canceled = 0;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->ret = 0;
|
||||
acb->nr_pending = 0;
|
||||
QLIST_INIT(&acb->aioreq_head);
|
||||
return acb;
|
||||
}
|
||||
|
||||
@@ -499,8 +503,28 @@ success:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
unsigned int *wlen)
|
||||
static int send_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
unsigned int *wlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qemu_send_full(sockfd, hdr, sizeof(*hdr), 0);
|
||||
if (ret < sizeof(*hdr)) {
|
||||
error_report("failed to send a req, %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = qemu_send_full(sockfd, data, *wlen, 0);
|
||||
if (ret < *wlen) {
|
||||
error_report("failed to send a req, %s", strerror(errno));
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
unsigned int *wlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -517,37 +541,46 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void restart_co_req(void *opaque)
|
||||
static int do_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
unsigned int *wlen, unsigned int *rlen)
|
||||
{
|
||||
Coroutine *co = opaque;
|
||||
int ret;
|
||||
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
socket_set_block(sockfd);
|
||||
ret = send_req(sockfd, hdr, data, wlen);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_recv_full(sockfd, hdr, sizeof(*hdr), 0);
|
||||
if (ret < sizeof(*hdr)) {
|
||||
error_report("failed to get a rsp, %s", strerror(errno));
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*rlen > hdr->data_length) {
|
||||
*rlen = hdr->data_length;
|
||||
}
|
||||
|
||||
if (*rlen) {
|
||||
ret = qemu_recv_full(sockfd, data, *rlen, 0);
|
||||
if (ret < *rlen) {
|
||||
error_report("failed to get the data, %s", strerror(errno));
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
socket_set_nonblock(sockfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct SheepdogReqCo {
|
||||
int sockfd;
|
||||
SheepdogReq *hdr;
|
||||
void *data;
|
||||
unsigned int *wlen;
|
||||
unsigned int *rlen;
|
||||
int ret;
|
||||
bool finished;
|
||||
} SheepdogReqCo;
|
||||
|
||||
static coroutine_fn void do_co_req(void *opaque)
|
||||
static int do_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
unsigned int *wlen, unsigned int *rlen)
|
||||
{
|
||||
int ret;
|
||||
Coroutine *co;
|
||||
SheepdogReqCo *srco = opaque;
|
||||
int sockfd = srco->sockfd;
|
||||
SheepdogReq *hdr = srco->hdr;
|
||||
void *data = srco->data;
|
||||
unsigned int *wlen = srco->wlen;
|
||||
unsigned int *rlen = srco->rlen;
|
||||
|
||||
co = qemu_coroutine_self();
|
||||
qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, NULL, co);
|
||||
|
||||
socket_set_block(sockfd);
|
||||
ret = send_co_req(sockfd, hdr, data, wlen);
|
||||
@@ -555,8 +588,6 @@ static coroutine_fn void do_co_req(void *opaque)
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, NULL, co);
|
||||
|
||||
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
|
||||
if (ret < sizeof(*hdr)) {
|
||||
error_report("failed to get a rsp, %s", strerror(errno));
|
||||
@@ -578,79 +609,40 @@ static coroutine_fn void do_co_req(void *opaque)
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
qemu_aio_set_fd_handler(sockfd, NULL, NULL, NULL, NULL);
|
||||
socket_set_nonblock(sockfd);
|
||||
|
||||
srco->ret = ret;
|
||||
srco->finished = true;
|
||||
}
|
||||
|
||||
static int do_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
unsigned int *wlen, unsigned int *rlen)
|
||||
{
|
||||
Coroutine *co;
|
||||
SheepdogReqCo srco = {
|
||||
.sockfd = sockfd,
|
||||
.hdr = hdr,
|
||||
.data = data,
|
||||
.wlen = wlen,
|
||||
.rlen = rlen,
|
||||
.ret = 0,
|
||||
.finished = false,
|
||||
};
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
do_co_req(&srco);
|
||||
} else {
|
||||
co = qemu_coroutine_create(do_co_req);
|
||||
qemu_coroutine_enter(co, &srco);
|
||||
while (!srco.finished) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
return srco.ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
struct iovec *iov, int niov, bool create,
|
||||
struct iovec *iov, int niov, int create,
|
||||
enum AIOCBState aiocb_type);
|
||||
|
||||
|
||||
static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid)
|
||||
{
|
||||
AIOReq *aio_req;
|
||||
|
||||
QLIST_FOREACH(aio_req, &s->pending_aio_head, aio_siblings) {
|
||||
if (aio_req->oid == oid) {
|
||||
return aio_req;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function searchs pending requests to the object `oid', and
|
||||
* sends them.
|
||||
*/
|
||||
static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid)
|
||||
static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid, uint32_t id)
|
||||
{
|
||||
AIOReq *aio_req;
|
||||
AIOReq *aio_req, *next;
|
||||
SheepdogAIOCB *acb;
|
||||
int ret;
|
||||
|
||||
while ((aio_req = find_pending_req(s, oid)) != NULL) {
|
||||
QLIST_FOREACH_SAFE(aio_req, &s->outstanding_aio_head,
|
||||
outstanding_aio_siblings, next) {
|
||||
if (id == aio_req->id) {
|
||||
continue;
|
||||
}
|
||||
if (aio_req->oid != oid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
acb = aio_req->aiocb;
|
||||
/* move aio_req from pending list to inflight one */
|
||||
QLIST_REMOVE(aio_req, aio_siblings);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
ret = add_aio_request(s, aio_req, acb->qiov->iov,
|
||||
acb->qiov->niov, false, acb->aiocb_type);
|
||||
acb->qiov->niov, 0, acb->aiocb_type);
|
||||
if (ret < 0) {
|
||||
error_report("add_aio_request is failed");
|
||||
free_aio_req(s, aio_req);
|
||||
if (!acb->nr_pending) {
|
||||
if (QLIST_EMPTY(&acb->aioreq_head)) {
|
||||
sd_finish_aiocb(acb);
|
||||
}
|
||||
}
|
||||
@@ -671,9 +663,10 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
int ret;
|
||||
AIOReq *aio_req = NULL;
|
||||
SheepdogAIOCB *acb;
|
||||
int rest;
|
||||
unsigned long idx;
|
||||
|
||||
if (QLIST_EMPTY(&s->inflight_aio_head)) {
|
||||
if (QLIST_EMPTY(&s->outstanding_aio_head)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -684,8 +677,8 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* find the right aio_req from the inflight aio list */
|
||||
QLIST_FOREACH(aio_req, &s->inflight_aio_head, aio_siblings) {
|
||||
/* find the right aio_req from the outstanding_aio list */
|
||||
QLIST_FOREACH(aio_req, &s->outstanding_aio_head, outstanding_aio_siblings) {
|
||||
if (aio_req->id == rsp.id) {
|
||||
break;
|
||||
}
|
||||
@@ -723,12 +716,12 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
* create requests are not allowed, so we search the
|
||||
* pending requests here.
|
||||
*/
|
||||
send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx));
|
||||
send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx), rsp.id);
|
||||
}
|
||||
break;
|
||||
case AIOCB_READ_UDATA:
|
||||
ret = qemu_co_recvv(fd, acb->qiov->iov, acb->qiov->niov,
|
||||
aio_req->iov_offset, rsp.data_length);
|
||||
ret = qemu_co_recvv(fd, acb->qiov->iov, rsp.data_length,
|
||||
aio_req->iov_offset);
|
||||
if (ret < 0) {
|
||||
error_report("failed to get the data, %s", strerror(errno));
|
||||
goto out;
|
||||
@@ -741,8 +734,8 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
error_report("%s", sd_strerror(rsp.result));
|
||||
}
|
||||
|
||||
free_aio_req(s, aio_req);
|
||||
if (!acb->nr_pending) {
|
||||
rest = free_aio_req(s, aio_req);
|
||||
if (!rest) {
|
||||
/*
|
||||
* We've finished all requests which belong to the AIOCB, so
|
||||
* we can switch back to sd_co_readv/writev now.
|
||||
@@ -775,8 +768,7 @@ static int aio_flush_request(void *opaque)
|
||||
{
|
||||
BDRVSheepdogState *s = opaque;
|
||||
|
||||
return !QLIST_EMPTY(&s->inflight_aio_head) ||
|
||||
!QLIST_EMPTY(&s->pending_aio_head);
|
||||
return !QLIST_EMPTY(&s->outstanding_aio_head);
|
||||
}
|
||||
|
||||
static int set_nodelay(int fd)
|
||||
@@ -866,14 +858,14 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
|
||||
s->port = 0;
|
||||
}
|
||||
|
||||
pstrcpy(vdi, SD_MAX_VDI_LEN, p);
|
||||
strncpy(vdi, p, SD_MAX_VDI_LEN);
|
||||
|
||||
p = strchr(vdi, ':');
|
||||
if (p) {
|
||||
*p++ = '\0';
|
||||
*snapid = strtoul(p, NULL, 10);
|
||||
if (*snapid == 0) {
|
||||
pstrcpy(tag, SD_MAX_VDI_TAG_LEN, p);
|
||||
strncpy(tag, p, SD_MAX_VDI_TAG_LEN);
|
||||
}
|
||||
} else {
|
||||
*snapid = CURRENT_VDI_ID; /* search current vdi */
|
||||
@@ -900,10 +892,7 @@ static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* This pair of strncpy calls ensures that the buffer is zero-filled,
|
||||
* which is desirable since we'll soon be sending those bytes, and
|
||||
* don't want the send_req to read uninitialized data.
|
||||
*/
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strncpy(buf, filename, SD_MAX_VDI_LEN);
|
||||
strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN);
|
||||
|
||||
@@ -943,7 +932,7 @@ out:
|
||||
}
|
||||
|
||||
static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
struct iovec *iov, int niov, bool create,
|
||||
struct iovec *iov, int niov, int create,
|
||||
enum AIOCBState aiocb_type)
|
||||
{
|
||||
int nr_copies = s->inode.nr_copies;
|
||||
@@ -1004,7 +993,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
}
|
||||
|
||||
if (wlen) {
|
||||
ret = qemu_co_sendv(s->fd, iov, niov, aio_req->iov_offset, wlen);
|
||||
ret = qemu_co_sendv(s->fd, iov, wlen, aio_req->iov_offset);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
error_report("failed to send a data, %s", strerror(errno));
|
||||
@@ -1022,7 +1011,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
|
||||
static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
|
||||
unsigned int datalen, uint64_t offset,
|
||||
bool write, bool create, bool cache)
|
||||
int write, int create, uint8_t cache)
|
||||
{
|
||||
SheepdogObjReq hdr;
|
||||
SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr;
|
||||
@@ -1071,18 +1060,18 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
|
||||
}
|
||||
|
||||
static int read_object(int fd, char *buf, uint64_t oid, int copies,
|
||||
unsigned int datalen, uint64_t offset, bool cache)
|
||||
unsigned int datalen, uint64_t offset, uint8_t cache)
|
||||
{
|
||||
return read_write_object(fd, buf, oid, copies, datalen, offset, false,
|
||||
false, cache);
|
||||
return read_write_object(fd, buf, oid, copies, datalen, offset, 0, 0,
|
||||
cache);
|
||||
}
|
||||
|
||||
static int write_object(int fd, char *buf, uint64_t oid, int copies,
|
||||
unsigned int datalen, uint64_t offset, bool create,
|
||||
bool cache)
|
||||
unsigned int datalen, uint64_t offset, int create,
|
||||
uint8_t cache)
|
||||
{
|
||||
return read_write_object(fd, buf, oid, copies, datalen, offset, true,
|
||||
create, cache);
|
||||
return read_write_object(fd, buf, oid, copies, datalen, offset, 1, create,
|
||||
cache);
|
||||
}
|
||||
|
||||
static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
@@ -1096,8 +1085,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
strstart(filename, "sheepdog:", (const char **)&filename);
|
||||
|
||||
QLIST_INIT(&s->inflight_aio_head);
|
||||
QLIST_INIT(&s->pending_aio_head);
|
||||
QLIST_INIT(&s->outstanding_aio_head);
|
||||
s->fd = -1;
|
||||
|
||||
memset(vdi, 0, sizeof(vdi));
|
||||
@@ -1117,17 +1105,19 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->cache_enabled = true;
|
||||
s->flush_fd = connect_to_sdog(s->addr, s->port);
|
||||
if (s->flush_fd < 0) {
|
||||
error_report("failed to connect");
|
||||
ret = s->flush_fd;
|
||||
goto out;
|
||||
if (flags & BDRV_O_CACHE_WB) {
|
||||
s->cache_enabled = 1;
|
||||
s->flush_fd = connect_to_sdog(s->addr, s->port);
|
||||
if (s->flush_fd < 0) {
|
||||
error_report("failed to connect");
|
||||
ret = s->flush_fd;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (snapid || tag[0] != '\0') {
|
||||
dprintf("%" PRIx32 " snapshot inode was open.\n", vid);
|
||||
s->is_snapshot = true;
|
||||
s->is_snapshot = 1;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
@@ -1152,7 +1142,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
s->max_dirty_data_idx = 0;
|
||||
|
||||
bs->total_sectors = s->inode.vdi_size / SECTOR_SIZE;
|
||||
pstrcpy(s->name, sizeof(s->name), vdi);
|
||||
strncpy(s->name, vdi, sizeof(s->name));
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
g_free(buf);
|
||||
return 0;
|
||||
@@ -1180,11 +1170,8 @@ static int do_sd_create(char *filename, int64_t vdi_size,
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* FIXME: would it be better to fail (e.g., return -EIO) when filename
|
||||
* does not fit in buf? For now, just truncate and avoid buffer overrun.
|
||||
*/
|
||||
memset(buf, 0, sizeof(buf));
|
||||
pstrcpy(buf, sizeof(buf), filename);
|
||||
strncpy(buf, filename, SD_MAX_VDI_LEN);
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.opcode = SD_OP_NEW_VDI;
|
||||
@@ -1270,7 +1257,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
|
||||
BDRVSheepdogState *s;
|
||||
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
|
||||
uint32_t snapid;
|
||||
bool prealloc = false;
|
||||
int prealloc = 0;
|
||||
const char *vdiname;
|
||||
|
||||
s = g_malloc0(sizeof(BDRVSheepdogState));
|
||||
@@ -1292,9 +1279,9 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
|
||||
backing_file = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
|
||||
if (!options->value.s || !strcmp(options->value.s, "off")) {
|
||||
prealloc = false;
|
||||
prealloc = 0;
|
||||
} else if (!strcmp(options->value.s, "full")) {
|
||||
prealloc = true;
|
||||
prealloc = 1;
|
||||
} else {
|
||||
error_report("Invalid preallocation mode: '%s'",
|
||||
options->value.s);
|
||||
@@ -1422,7 +1409,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
||||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||
s->inode.vdi_size = offset;
|
||||
ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
s->inode.nr_copies, datalen, 0, false, s->cache_enabled);
|
||||
s->inode.nr_copies, datalen, 0, 0, s->cache_enabled);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -1460,8 +1447,7 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
|
||||
iov.iov_len = sizeof(s->inode);
|
||||
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
data_len, offset, 0, 0, offset);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
ret = add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA);
|
||||
ret = add_aio_request(s, aio_req, &iov, 1, 0, AIOCB_WRITE_UDATA);
|
||||
if (ret) {
|
||||
free_aio_req(s, aio_req);
|
||||
acb->ret = -EIO;
|
||||
@@ -1515,7 +1501,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
|
||||
|
||||
memcpy(&s->inode, buf, sizeof(s->inode));
|
||||
|
||||
s->is_snapshot = false;
|
||||
s->is_snapshot = 0;
|
||||
ret = 0;
|
||||
dprintf("%" PRIx32 " was newly created.\n", s->inode.vdi_id);
|
||||
|
||||
@@ -1529,7 +1515,7 @@ out:
|
||||
* Send I/O requests to the server.
|
||||
*
|
||||
* This function sends requests to the server, links the requests to
|
||||
* the inflight_list in BDRVSheepdogState, and exits without
|
||||
* the outstanding_list in BDRVSheepdogState, and exits without
|
||||
* waiting the response. The responses are received in the
|
||||
* `aio_read_response' function which is called from the main loop as
|
||||
* a fd handler.
|
||||
@@ -1561,55 +1547,46 @@ static int coroutine_fn sd_co_rw_vector(void *p)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we don't free the aiocb before we are done with all requests.
|
||||
* This additional reference is dropped at the end of this function.
|
||||
*/
|
||||
acb->nr_pending++;
|
||||
|
||||
while (done != total) {
|
||||
uint8_t flags = 0;
|
||||
uint64_t old_oid = 0;
|
||||
bool create = false;
|
||||
int create = 0;
|
||||
|
||||
oid = vid_to_data_oid(inode->data_vdi_id[idx], idx);
|
||||
|
||||
len = MIN(total - done, SD_DATA_OBJ_SIZE - offset);
|
||||
|
||||
switch (acb->aiocb_type) {
|
||||
case AIOCB_READ_UDATA:
|
||||
if (!inode->data_vdi_id[idx]) {
|
||||
qemu_iovec_memset(acb->qiov, done, 0, len);
|
||||
if (!inode->data_vdi_id[idx]) {
|
||||
if (acb->aiocb_type == AIOCB_READ_UDATA) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case AIOCB_WRITE_UDATA:
|
||||
if (!inode->data_vdi_id[idx]) {
|
||||
create = true;
|
||||
} else if (!is_data_obj_writable(inode, idx)) {
|
||||
/* Copy-On-Write */
|
||||
create = true;
|
||||
old_oid = oid;
|
||||
flags = SD_FLAG_CMD_COW;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
create = 1;
|
||||
} else if (acb->aiocb_type == AIOCB_WRITE_UDATA
|
||||
&& !is_data_obj_writable(inode, idx)) {
|
||||
/* Copy-On-Write */
|
||||
create = 1;
|
||||
old_oid = oid;
|
||||
flags = SD_FLAG_CMD_COW;
|
||||
}
|
||||
|
||||
if (create) {
|
||||
dprintf("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n",
|
||||
inode->vdi_id, oid,
|
||||
dprintf("update ino (%" PRIu32") %" PRIu64 " %" PRIu64
|
||||
" %" PRIu64 "\n", inode->vdi_id, oid,
|
||||
vid_to_data_oid(inode->data_vdi_id[idx], idx), idx);
|
||||
oid = vid_to_data_oid(inode->vdi_id, idx);
|
||||
dprintf("new oid %" PRIx64 "\n", oid);
|
||||
dprintf("new oid %lx\n", oid);
|
||||
}
|
||||
|
||||
aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done);
|
||||
|
||||
if (create) {
|
||||
AIOReq *areq;
|
||||
QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) {
|
||||
QLIST_FOREACH(areq, &s->outstanding_aio_head,
|
||||
outstanding_aio_siblings) {
|
||||
if (areq == aio_req) {
|
||||
continue;
|
||||
}
|
||||
if (areq->oid == oid) {
|
||||
/*
|
||||
* Sheepdog cannot handle simultaneous create
|
||||
@@ -1619,14 +1596,11 @@ static int coroutine_fn sd_co_rw_vector(void *p)
|
||||
*/
|
||||
aio_req->flags = 0;
|
||||
aio_req->base_oid = 0;
|
||||
QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req,
|
||||
aio_siblings);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
|
||||
create, acb->aiocb_type);
|
||||
if (ret < 0) {
|
||||
@@ -1641,7 +1615,7 @@ static int coroutine_fn sd_co_rw_vector(void *p)
|
||||
done += len;
|
||||
}
|
||||
out:
|
||||
if (!--acb->nr_pending) {
|
||||
if (QLIST_EMPTY(&acb->aioreq_head)) {
|
||||
return acb->ret;
|
||||
}
|
||||
return 1;
|
||||
@@ -1654,6 +1628,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int ret;
|
||||
|
||||
if (bs->growable && sector_num + nb_sectors > bs->total_sectors) {
|
||||
/* TODO: shouldn't block here */
|
||||
ret = sd_truncate(bs, (sector_num + nb_sectors) * SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -1680,12 +1655,20 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
SheepdogAIOCB *acb;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, NULL, NULL);
|
||||
acb->aiocb_type = AIOCB_READ_UDATA;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
/*
|
||||
* TODO: we can do better; we don't need to initialize
|
||||
* blindly.
|
||||
*/
|
||||
for (i = 0; i < qiov->niov; i++) {
|
||||
memset(qiov->iov[i].iov_base, 0, qiov->iov[i].iov_len);
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
qemu_aio_release(acb);
|
||||
@@ -1713,7 +1696,7 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
|
||||
hdr.opcode = SD_OP_FLUSH_VDI;
|
||||
hdr.oid = vid_to_vdi_oid(inode->vdi_id);
|
||||
|
||||
ret = do_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen);
|
||||
ret = do_co_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen);
|
||||
if (ret) {
|
||||
error_report("failed to send a request to the sheep");
|
||||
return ret;
|
||||
@@ -1722,7 +1705,7 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
|
||||
if (rsp->result == SD_RES_INVALID_PARMS) {
|
||||
dprintf("disable write cache since the server doesn't support it\n");
|
||||
|
||||
s->cache_enabled = false;
|
||||
s->cache_enabled = 0;
|
||||
closesocket(s->flush_fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -1743,7 +1726,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
SheepdogInode *inode;
|
||||
unsigned int datalen;
|
||||
|
||||
dprintf("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " "
|
||||
dprintf("sn_info: name %s id_str %s s: name %s vm_state_size %d "
|
||||
"is_snapshot %d\n", sn_info->name, sn_info->id_str,
|
||||
s->name, sn_info->vm_state_size, s->is_snapshot);
|
||||
|
||||
@@ -1758,9 +1741,6 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
||||
s->inode.vm_state_size = sn_info->vm_state_size;
|
||||
s->inode.vm_clock_nsec = sn_info->vm_clock_nsec;
|
||||
/* It appears that inode.tag does not require a NUL terminator,
|
||||
* which means this use of strncpy is ok.
|
||||
*/
|
||||
strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
|
||||
/* we don't need to update entire object */
|
||||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||
@@ -1773,7 +1753,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
}
|
||||
|
||||
ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
s->inode.nr_copies, datalen, 0, false, s->cache_enabled);
|
||||
s->inode.nr_copies, datalen, 0, 0, s->cache_enabled);
|
||||
if (ret < 0) {
|
||||
error_report("failed to write snapshot's inode.");
|
||||
goto cleanup;
|
||||
@@ -1820,13 +1800,13 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
|
||||
memcpy(old_s, s, sizeof(BDRVSheepdogState));
|
||||
|
||||
pstrcpy(vdi, sizeof(vdi), s->name);
|
||||
memset(vdi, 0, sizeof(vdi));
|
||||
strncpy(vdi, s->name, sizeof(vdi));
|
||||
|
||||
memset(tag, 0, sizeof(tag));
|
||||
snapid = strtoul(snapshot_id, NULL, 10);
|
||||
if (snapid) {
|
||||
tag[0] = 0;
|
||||
} else {
|
||||
pstrcpy(tag, sizeof(tag), s->name);
|
||||
if (!snapid) {
|
||||
strncpy(tag, s->name, sizeof(tag));
|
||||
}
|
||||
|
||||
ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1);
|
||||
@@ -1860,7 +1840,7 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->is_snapshot = true;
|
||||
s->is_snapshot = 1;
|
||||
|
||||
g_free(buf);
|
||||
g_free(old_s);
|
||||
@@ -1955,9 +1935,8 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
|
||||
snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), "%u",
|
||||
inode.snap_id);
|
||||
pstrcpy(sn_tab[found].name,
|
||||
MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)),
|
||||
inode.tag);
|
||||
strncpy(sn_tab[found].name, inode.tag,
|
||||
MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)));
|
||||
found++;
|
||||
}
|
||||
}
|
||||
@@ -1978,8 +1957,8 @@ out:
|
||||
static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
||||
int64_t pos, int size, int load)
|
||||
{
|
||||
bool create;
|
||||
int fd, ret = 0, remaining = size;
|
||||
int fd, create;
|
||||
int ret = 0, remaining = size;
|
||||
unsigned int data_len;
|
||||
uint64_t vmstate_oid;
|
||||
uint32_t vdi_index;
|
||||
@@ -1994,7 +1973,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
||||
vdi_index = pos / SD_DATA_OBJ_SIZE;
|
||||
offset = pos % SD_DATA_OBJ_SIZE;
|
||||
|
||||
data_len = MIN(remaining, SD_DATA_OBJ_SIZE - offset);
|
||||
data_len = MIN(remaining, SD_DATA_OBJ_SIZE);
|
||||
|
||||
vmstate_oid = vid_to_vmstate_oid(s->inode.vdi_id, vdi_index);
|
||||
|
||||
@@ -2015,7 +1994,6 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
||||
}
|
||||
|
||||
pos += data_len;
|
||||
data += data_len;
|
||||
remaining -= data_len;
|
||||
}
|
||||
ret = size;
|
||||
|
||||
+96
-52
@@ -13,8 +13,6 @@
|
||||
|
||||
#include "trace.h"
|
||||
#include "block_int.h"
|
||||
#include "blockjob.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
|
||||
enum {
|
||||
/*
|
||||
@@ -27,11 +25,38 @@ enum {
|
||||
|
||||
#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;
|
||||
BlockdevOnError on_error;
|
||||
char backing_file_id[1024];
|
||||
} StreamBlockJob;
|
||||
|
||||
@@ -73,20 +98,80 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
||||
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 error = 0;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
void *buf;
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
block_job_completed(&s->common, s->common.len);
|
||||
block_job_complete(&s->common, s->common.len);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,7 +189,6 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
uint64_t delay_ns = 0;
|
||||
bool copy;
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
@@ -115,26 +199,10 @@ wait:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = bdrv_co_is_allocated(bs, sector_num,
|
||||
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
|
||||
if (ret == 1) {
|
||||
/* Allocated in the top, no need to copy. */
|
||||
copy = false;
|
||||
} else {
|
||||
/* Copy if allocated in the intermediate images. Limit to the
|
||||
* known-unallocated area [sector_num, sector_num+n). */
|
||||
ret = bdrv_co_is_allocated_above(bs->backing_hd, base,
|
||||
sector_num, n, &n);
|
||||
|
||||
/* Finish early if end of backing file has been reached */
|
||||
if (ret == 0 && n == 0) {
|
||||
n = end - sector_num;
|
||||
}
|
||||
|
||||
copy = (ret == 1);
|
||||
}
|
||||
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 && copy) {
|
||||
if (ret == 0) {
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
if (delay_ns > 0) {
|
||||
@@ -144,19 +212,7 @@ wait:
|
||||
ret = stream_populate(bs, sector_num, n, buf);
|
||||
}
|
||||
if (ret < 0) {
|
||||
BlockErrorAction action =
|
||||
block_job_error_action(&s->common, s->common.bs, s->on_error,
|
||||
true, -ret);
|
||||
if (action == BDRV_ACTION_STOP) {
|
||||
n = 0;
|
||||
continue;
|
||||
}
|
||||
if (error == 0) {
|
||||
error = ret;
|
||||
}
|
||||
if (action == BDRV_ACTION_REPORT) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
@@ -168,9 +224,6 @@ wait:
|
||||
bdrv_disable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
/* Do not remove the backing file if an error was there but ignored. */
|
||||
ret = error;
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
@@ -184,7 +237,7 @@ wait:
|
||||
}
|
||||
|
||||
qemu_vfree(buf);
|
||||
block_job_completed(&s->common, ret);
|
||||
block_job_complete(&s->common, ret);
|
||||
}
|
||||
|
||||
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
@@ -195,7 +248,7 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
static BlockJobType stream_job_type = {
|
||||
@@ -206,19 +259,11 @@ static BlockJobType stream_job_type = {
|
||||
|
||||
void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
const char *base_id, int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s;
|
||||
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "on-error");
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
@@ -229,7 +274,6 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
|
||||
}
|
||||
|
||||
s->on_error = on_error;
|
||||
s->common.co = qemu_coroutine_create(stream_run);
|
||||
trace_stream_start(bs, base, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
|
||||
+23
-32
@@ -60,6 +60,9 @@
|
||||
/* TODO: move uuid emulation to some central place in QEMU. */
|
||||
#include "sysemu.h" /* UUID_FMT */
|
||||
typedef unsigned char uuid_t[16];
|
||||
void uuid_generate(uuid_t out);
|
||||
int uuid_is_null(const uuid_t uu);
|
||||
void uuid_unparse(const uuid_t uu, char *out);
|
||||
#endif
|
||||
|
||||
/* Code configuration options. */
|
||||
@@ -121,18 +124,18 @@ typedef unsigned char uuid_t[16];
|
||||
#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
|
||||
|
||||
#if !defined(CONFIG_UUID)
|
||||
static inline void uuid_generate(uuid_t out)
|
||||
void uuid_generate(uuid_t out)
|
||||
{
|
||||
memset(out, 0, sizeof(uuid_t));
|
||||
}
|
||||
|
||||
static inline int uuid_is_null(const uuid_t uu)
|
||||
int uuid_is_null(const uuid_t uu)
|
||||
{
|
||||
uuid_t null_uuid = { 0 };
|
||||
return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
|
||||
}
|
||||
|
||||
static inline void uuid_unparse(const uuid_t uu, char *out)
|
||||
void uuid_unparse(const uuid_t uu, char *out)
|
||||
{
|
||||
snprintf(out, 37, UUID_FMT,
|
||||
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
|
||||
@@ -274,8 +277,7 @@ static void vdi_header_print(VdiHeader *header)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
|
||||
{
|
||||
/* TODO: additional checks possible. */
|
||||
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
|
||||
@@ -284,10 +286,6 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
uint32_t *bmap;
|
||||
logout("\n");
|
||||
|
||||
if (fix) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bmap = g_malloc(s->header.blocks_in_image * sizeof(uint32_t));
|
||||
memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t));
|
||||
|
||||
@@ -451,12 +449,6 @@ static int vdi_open(BlockDriverState *bs, int flags)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vdi_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
@@ -631,6 +623,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
|
||||
VdiHeader header;
|
||||
size_t i;
|
||||
size_t bmap_size;
|
||||
uint32_t *bmap;
|
||||
|
||||
logout("\n");
|
||||
|
||||
@@ -655,9 +648,8 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
@@ -695,21 +687,21 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
|
||||
result = -errno;
|
||||
}
|
||||
|
||||
bmap = NULL;
|
||||
if (bmap_size > 0) {
|
||||
uint32_t *bmap = g_malloc0(bmap_size);
|
||||
for (i = 0; i < blocks; i++) {
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
bmap[i] = i;
|
||||
} else {
|
||||
bmap[i] = VDI_UNALLOCATED;
|
||||
}
|
||||
}
|
||||
if (write(fd, bmap, bmap_size) < 0) {
|
||||
result = -errno;
|
||||
}
|
||||
g_free(bmap);
|
||||
bmap = (uint32_t *)g_malloc0(bmap_size);
|
||||
}
|
||||
|
||||
for (i = 0; i < blocks; i++) {
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
bmap[i] = i;
|
||||
} else {
|
||||
bmap[i] = VDI_UNALLOCATED;
|
||||
}
|
||||
}
|
||||
if (write(fd, bmap, bmap_size) < 0) {
|
||||
result = -errno;
|
||||
}
|
||||
g_free(bmap);
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) {
|
||||
result = -errno;
|
||||
@@ -764,7 +756,6 @@ static BlockDriver bdrv_vdi = {
|
||||
.bdrv_probe = vdi_probe,
|
||||
.bdrv_open = vdi_open,
|
||||
.bdrv_close = vdi_close,
|
||||
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
||||
.bdrv_create = vdi_create,
|
||||
.bdrv_co_is_allocated = vdi_co_is_allocated,
|
||||
.bdrv_make_empty = vdi_make_empty,
|
||||
|
||||
+18
-55
@@ -300,40 +300,6 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Queue extents, if any, for reopen() */
|
||||
static int vmdk_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
BDRVVmdkState *s;
|
||||
int ret = -1;
|
||||
int i;
|
||||
VmdkExtent *e;
|
||||
|
||||
assert(state != NULL);
|
||||
assert(state->bs != NULL);
|
||||
|
||||
if (queue == NULL) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"No reopen queue for VMDK extents");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s = state->bs->opaque;
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
e = &s->extents[i];
|
||||
if (e->file != state->bs->file) {
|
||||
bdrv_reopen_queue(queue, e->file, state->flags);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_parent_open(BlockDriverState *bs)
|
||||
{
|
||||
char *p_name;
|
||||
@@ -1092,7 +1058,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
uint64_t n, index_in_cluster;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
VmdkExtent *extent = NULL;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
@@ -1104,9 +1069,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
ret = get_cluster_offset(
|
||||
bs, extent, NULL,
|
||||
sector_num << 9, 0, &cluster_offset);
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = sector_num % extent->cluster_sectors;
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@@ -1157,7 +1120,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
VmdkExtent *extent = NULL;
|
||||
int n, ret;
|
||||
int64_t index_in_cluster;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
uint64_t cluster_offset;
|
||||
VmdkMetaData m_data;
|
||||
|
||||
@@ -1200,9 +1162,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
if (ret) {
|
||||
return -EINVAL;
|
||||
}
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = sector_num % extent->cluster_sectors;
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@@ -1257,9 +1217,10 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
VMDK4Header header;
|
||||
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
|
||||
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
fd = open(
|
||||
filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
@@ -1354,7 +1315,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1414,7 +1375,8 @@ static int relative_path(char *dest, int dest_size,
|
||||
return -1;
|
||||
}
|
||||
if (path_is_absolute(target)) {
|
||||
pstrcpy(dest, dest_size, target);
|
||||
dest[dest_size - 1] = '\0';
|
||||
strncpy(dest, target, dest_size - 1);
|
||||
return 0;
|
||||
}
|
||||
while (base[i] == target[i]) {
|
||||
@@ -1578,13 +1540,15 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
||||
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
||||
total_size / (int64_t)(63 * 16 * 512));
|
||||
if (split || flat) {
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
fd = open(
|
||||
filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
} else {
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
fd = open(
|
||||
filename,
|
||||
O_WRONLY | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
}
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
@@ -1601,7 +1565,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
ret = 0;
|
||||
exit:
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1685,7 +1649,6 @@ static BlockDriver bdrv_vmdk = {
|
||||
.instance_size = sizeof(BDRVVmdkState),
|
||||
.bdrv_probe = vmdk_probe,
|
||||
.bdrv_open = vmdk_open,
|
||||
.bdrv_reopen_prepare = vmdk_reopen_prepare,
|
||||
.bdrv_read = vmdk_co_read,
|
||||
.bdrv_write = vmdk_co_write,
|
||||
.bdrv_close = vmdk_close,
|
||||
|
||||
+2
-9
@@ -265,12 +265,6 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vpc_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the absolute byte offset of the given sector in the image file.
|
||||
* If the sector is not allocated, -1 is returned instead.
|
||||
@@ -684,7 +678,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
|
||||
/* Create the file */
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
@@ -750,7 +744,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
}
|
||||
|
||||
fail:
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -789,7 +783,6 @@ static BlockDriver bdrv_vpc = {
|
||||
.bdrv_probe = vpc_probe,
|
||||
.bdrv_open = vpc_open,
|
||||
.bdrv_close = vpc_close,
|
||||
.bdrv_reopen_prepare = vpc_reopen_prepare,
|
||||
.bdrv_create = vpc_create,
|
||||
|
||||
.bdrv_read = vpc_co_read,
|
||||
|
||||
+34
-40
@@ -359,12 +359,11 @@ typedef struct BDRVVVFATState {
|
||||
* if the position is outside the specified geometry, fill maximum value for CHS
|
||||
* and return 1 to signal overflow.
|
||||
*/
|
||||
static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs)
|
||||
{
|
||||
static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
|
||||
int head,sector;
|
||||
sector = spos % secs; spos /= secs;
|
||||
head = spos % heads; spos /= heads;
|
||||
if (spos >= cyls) {
|
||||
sector = spos % (bs->secs); spos/= bs->secs;
|
||||
head = spos % (bs->heads); spos/= bs->heads;
|
||||
if(spos >= bs->cyls){
|
||||
/* Overflow,
|
||||
it happens if 32bit sector positions are used, while CHS is only 24bit.
|
||||
Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
|
||||
@@ -379,7 +378,7 @@ static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
|
||||
static void init_mbr(BDRVVVFATState* s)
|
||||
{
|
||||
/* TODO: if the files mbr.img and bootsect.img exist, use them */
|
||||
mbr_t* real_mbr=(mbr_t*)s->first_sectors;
|
||||
@@ -394,15 +393,12 @@ static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
|
||||
partition->attributes=0x80; /* bootable */
|
||||
|
||||
/* LBA is used when partition is outside the CHS geometry */
|
||||
lba = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1,
|
||||
cyls, heads, secs);
|
||||
lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1,
|
||||
cyls, heads, secs);
|
||||
lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
|
||||
lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count);
|
||||
|
||||
/*LBA partitions are identified only by start/length_sector_long not by CHS*/
|
||||
partition->start_sector_long = cpu_to_le32(s->first_sectors_number - 1);
|
||||
partition->length_sector_long = cpu_to_le32(s->bs->total_sectors
|
||||
- s->first_sectors_number + 1);
|
||||
partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
|
||||
partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
|
||||
|
||||
/* FAT12/FAT16/FAT32 */
|
||||
/* DOS uses different types when partition is LBA,
|
||||
@@ -834,7 +830,7 @@ static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
|
||||
}
|
||||
|
||||
static int init_directories(BDRVVVFATState* s,
|
||||
const char *dirname, int heads, int secs)
|
||||
const char* dirname)
|
||||
{
|
||||
bootsector_t* bootsector;
|
||||
mapping_t* mapping;
|
||||
@@ -961,8 +957,8 @@ static int init_directories(BDRVVVFATState* s,
|
||||
bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0); /* media descriptor (f8=hd, f0=3.5 fd)*/
|
||||
s->fat.pointer[0] = bootsector->media_type;
|
||||
bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
|
||||
bootsector->sectors_per_track = cpu_to_le16(secs);
|
||||
bootsector->number_of_heads = cpu_to_le16(heads);
|
||||
bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
|
||||
bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
|
||||
bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
|
||||
bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
|
||||
|
||||
@@ -995,7 +991,7 @@ static void vvfat_rebind(BlockDriverState *bs)
|
||||
static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
int i, cyls, heads, secs;
|
||||
int i;
|
||||
|
||||
#ifdef DEBUG
|
||||
vvv = s;
|
||||
@@ -1037,28 +1033,24 @@ DLOG(if (stderr == NULL) {
|
||||
/* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */
|
||||
if (!s->fat_type) {
|
||||
s->fat_type = 12;
|
||||
secs = 36;
|
||||
bs->secs = 36;
|
||||
s->sectors_per_cluster=2;
|
||||
} else {
|
||||
secs = s->fat_type == 12 ? 18 : 36;
|
||||
bs->secs=(s->fat_type == 12 ? 18 : 36);
|
||||
s->sectors_per_cluster=1;
|
||||
}
|
||||
s->first_sectors_number = 1;
|
||||
cyls = 80;
|
||||
heads = 2;
|
||||
bs->cyls=80; bs->heads=2;
|
||||
} else {
|
||||
/* 32MB or 504MB disk*/
|
||||
if (!s->fat_type) {
|
||||
s->fat_type = 16;
|
||||
}
|
||||
cyls = s->fat_type == 12 ? 64 : 1024;
|
||||
heads = 16;
|
||||
secs = 63;
|
||||
bs->cyls=(s->fat_type == 12 ? 64 : 1024);
|
||||
bs->heads=16; bs->secs=63;
|
||||
}
|
||||
fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
|
||||
dirname, cyls, heads, secs);
|
||||
|
||||
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
|
||||
s->sector_count=bs->cyls*bs->heads*bs->secs-(s->first_sectors_number-1);
|
||||
|
||||
if (strstr(dirname, ":rw:")) {
|
||||
if (enable_write_target(s))
|
||||
@@ -1074,16 +1066,18 @@ DLOG(if (stderr == NULL) {
|
||||
else
|
||||
dirname += i+1;
|
||||
|
||||
bs->total_sectors = cyls * heads * secs;
|
||||
bs->total_sectors=bs->cyls*bs->heads*bs->secs;
|
||||
|
||||
if (init_directories(s, dirname, heads, secs)) {
|
||||
if(init_directories(s, dirname))
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
|
||||
|
||||
if (s->first_sectors_number == 0x40) {
|
||||
init_mbr(s, cyls, heads, secs);
|
||||
if(s->first_sectors_number==0x40)
|
||||
init_mbr(s);
|
||||
else {
|
||||
/* MS-DOS does not like to know about CHS (?). */
|
||||
bs->heads = bs->cyls = bs->secs = 0;
|
||||
}
|
||||
|
||||
// assert(is_consistent(s));
|
||||
@@ -1105,7 +1099,7 @@ static inline void vvfat_close_current_file(BDRVVVFATState *s)
|
||||
if(s->current_mapping) {
|
||||
s->current_mapping = NULL;
|
||||
if (s->current_fd) {
|
||||
qemu_close(s->current_fd);
|
||||
close(s->current_fd);
|
||||
s->current_fd = 0;
|
||||
}
|
||||
}
|
||||
@@ -1162,7 +1156,7 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping)
|
||||
if(!s->current_mapping ||
|
||||
strcmp(s->current_mapping->path,mapping->path)) {
|
||||
/* open file */
|
||||
int fd = qemu_open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if(fd<0)
|
||||
return -1;
|
||||
vvfat_close_current_file(s);
|
||||
@@ -2221,7 +2215,7 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
for (i = s->cluster_size; i < offset; i += s->cluster_size)
|
||||
c = modified_fat_get(s, c);
|
||||
|
||||
fd = qemu_open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
|
||||
fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
|
||||
strerror(errno), errno);
|
||||
@@ -2230,7 +2224,7 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
}
|
||||
if (offset > 0) {
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return -3;
|
||||
}
|
||||
@@ -2251,13 +2245,13 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
(uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
|
||||
|
||||
if (ret < 0) {
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (write(fd, cluster, rest_size) < 0) {
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return -2;
|
||||
}
|
||||
@@ -2268,11 +2262,11 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
|
||||
if (ftruncate(fd, size)) {
|
||||
perror("ftruncate()");
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
return -4;
|
||||
}
|
||||
qemu_close(fd);
|
||||
close(fd);
|
||||
g_free(cluster);
|
||||
|
||||
return commit_mappings(s, first_cluster, dir_index);
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Block driver for RAW files (win32)
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-aio.h"
|
||||
#include "raw-aio.h"
|
||||
#include "event_notifier.h"
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#define FTYPE_FILE 0
|
||||
#define FTYPE_CD 1
|
||||
#define FTYPE_HARDDISK 2
|
||||
|
||||
struct QEMUWin32AIOState {
|
||||
HANDLE hIOCP;
|
||||
EventNotifier e;
|
||||
int count;
|
||||
};
|
||||
|
||||
typedef struct QEMUWin32AIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
struct QEMUWin32AIOState *ctx;
|
||||
int nbytes;
|
||||
OVERLAPPED ov;
|
||||
QEMUIOVector *qiov;
|
||||
void *buf;
|
||||
bool is_read;
|
||||
bool is_linear;
|
||||
} QEMUWin32AIOCB;
|
||||
|
||||
/*
|
||||
* Completes an AIO request (calls the callback and frees the ACB).
|
||||
*/
|
||||
static void win32_aio_process_completion(QEMUWin32AIOState *s,
|
||||
QEMUWin32AIOCB *waiocb, DWORD count)
|
||||
{
|
||||
int ret;
|
||||
s->count--;
|
||||
|
||||
if (waiocb->ov.Internal != 0) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = 0;
|
||||
if (count < waiocb->nbytes) {
|
||||
/* Short reads mean EOF, pad with zeros. */
|
||||
if (waiocb->is_read) {
|
||||
qemu_iovec_memset(waiocb->qiov, count, 0,
|
||||
waiocb->qiov->size - count);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!waiocb->is_linear) {
|
||||
if (ret == 0 && waiocb->is_read) {
|
||||
QEMUIOVector *qiov = waiocb->qiov;
|
||||
char *p = waiocb->buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov; ++i) {
|
||||
memcpy(qiov->iov[i].iov_base, p, qiov->iov[i].iov_len);
|
||||
p += qiov->iov[i].iov_len;
|
||||
}
|
||||
}
|
||||
qemu_vfree(waiocb->buf);
|
||||
}
|
||||
|
||||
|
||||
waiocb->common.cb(waiocb->common.opaque, ret);
|
||||
qemu_aio_release(waiocb);
|
||||
}
|
||||
|
||||
static void win32_aio_completion_cb(EventNotifier *e)
|
||||
{
|
||||
QEMUWin32AIOState *s = container_of(e, QEMUWin32AIOState, e);
|
||||
DWORD count;
|
||||
ULONG_PTR key;
|
||||
OVERLAPPED *ov;
|
||||
|
||||
event_notifier_test_and_clear(&s->e);
|
||||
while (GetQueuedCompletionStatus(s->hIOCP, &count, &key, &ov, 0)) {
|
||||
QEMUWin32AIOCB *waiocb = container_of(ov, QEMUWin32AIOCB, ov);
|
||||
|
||||
win32_aio_process_completion(s, waiocb, count);
|
||||
}
|
||||
}
|
||||
|
||||
static int win32_aio_flush_cb(EventNotifier *e)
|
||||
{
|
||||
QEMUWin32AIOState *s = container_of(e, QEMUWin32AIOState, e);
|
||||
|
||||
return (s->count > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void win32_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QEMUWin32AIOCB *waiocb = (QEMUWin32AIOCB *)blockacb;
|
||||
|
||||
/*
|
||||
* CancelIoEx is only supported in Vista and newer. For now, just
|
||||
* wait for completion.
|
||||
*/
|
||||
while (!HasOverlappedIoCompleted(&waiocb->ov)) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo win32_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEMUWin32AIOCB),
|
||||
.cancel = win32_aio_cancel,
|
||||
};
|
||||
|
||||
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
QEMUWin32AIOState *aio, HANDLE hfile,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
struct QEMUWin32AIOCB *waiocb;
|
||||
uint64_t offset = sector_num * 512;
|
||||
DWORD rc;
|
||||
|
||||
waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque);
|
||||
waiocb->nbytes = nb_sectors * 512;
|
||||
waiocb->qiov = qiov;
|
||||
waiocb->is_read = (type == QEMU_AIO_READ);
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
waiocb->buf = qemu_blockalign(bs, qiov->size);
|
||||
if (type & QEMU_AIO_WRITE) {
|
||||
char *p = waiocb->buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov; ++i) {
|
||||
memcpy(p, qiov->iov[i].iov_base, qiov->iov[i].iov_len);
|
||||
p += qiov->iov[i].iov_len;
|
||||
}
|
||||
}
|
||||
waiocb->is_linear = false;
|
||||
} else {
|
||||
waiocb->buf = qiov->iov[0].iov_base;
|
||||
waiocb->is_linear = true;
|
||||
}
|
||||
|
||||
memset(&waiocb->ov, 0, sizeof(waiocb->ov));
|
||||
waiocb->ov.Offset = (DWORD)offset;
|
||||
waiocb->ov.OffsetHigh = (DWORD)(offset >> 32);
|
||||
waiocb->ov.hEvent = event_notifier_get_handle(&aio->e);
|
||||
|
||||
aio->count++;
|
||||
|
||||
if (type & QEMU_AIO_READ) {
|
||||
rc = ReadFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov);
|
||||
} else {
|
||||
rc = WriteFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov);
|
||||
}
|
||||
if(rc == 0 && GetLastError() != ERROR_IO_PENDING) {
|
||||
goto out_dec_count;
|
||||
}
|
||||
return &waiocb->common;
|
||||
|
||||
out_dec_count:
|
||||
aio->count--;
|
||||
qemu_aio_release(waiocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile)
|
||||
{
|
||||
if (CreateIoCompletionPort(hfile, aio->hIOCP, (ULONG_PTR) 0, 0) == NULL) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
QEMUWin32AIOState *win32_aio_init(void)
|
||||
{
|
||||
QEMUWin32AIOState *s;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
if (event_notifier_init(&s->e, false) < 0) {
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
s->hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||
if (s->hIOCP == NULL) {
|
||||
goto out_close_efd;
|
||||
}
|
||||
|
||||
qemu_aio_set_event_notifier(&s->e, win32_aio_completion_cb,
|
||||
win32_aio_flush_cb);
|
||||
|
||||
return s;
|
||||
|
||||
out_close_efd:
|
||||
event_notifier_cleanup(&s->e);
|
||||
out_free_state:
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
+170
-74
@@ -30,12 +30,9 @@
|
||||
#include "qemu-coroutine.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "qapi-types.h"
|
||||
#include "qerror.h"
|
||||
#include "monitor.h"
|
||||
|
||||
#define BLOCK_FLAG_ENCRYPT 1
|
||||
#define BLOCK_FLAG_COMPAT6 4
|
||||
#define BLOCK_FLAG_LAZY_REFCOUNTS 8
|
||||
#define BLOCK_FLAG_ENCRYPT 1
|
||||
#define BLOCK_FLAG_COMPAT6 4
|
||||
|
||||
#define BLOCK_IO_LIMIT_READ 0
|
||||
#define BLOCK_IO_LIMIT_WRITE 1
|
||||
@@ -44,17 +41,16 @@
|
||||
#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"
|
||||
#define BLOCK_OPT_BACKING_FILE "backing_file"
|
||||
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
|
||||
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
|
||||
#define BLOCK_OPT_TABLE_SIZE "table_size"
|
||||
#define BLOCK_OPT_PREALLOC "preallocation"
|
||||
#define BLOCK_OPT_SUBFMT "subformat"
|
||||
#define BLOCK_OPT_COMPAT_LEVEL "compat"
|
||||
#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts"
|
||||
#define BLOCK_OPT_SIZE "size"
|
||||
#define BLOCK_OPT_ENCRYPT "encryption"
|
||||
#define BLOCK_OPT_COMPAT6 "compat6"
|
||||
#define BLOCK_OPT_BACKING_FILE "backing_file"
|
||||
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
|
||||
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
|
||||
#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;
|
||||
|
||||
@@ -68,18 +64,78 @@ typedef struct BlockIOBaseValue {
|
||||
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;
|
||||
};
|
||||
|
||||
struct BlockDriver {
|
||||
const char *format_name;
|
||||
int instance_size;
|
||||
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
|
||||
int (*bdrv_probe_device)(const char *filename);
|
||||
|
||||
/* For handling image reopen for split or non-split files */
|
||||
int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp);
|
||||
void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state);
|
||||
void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
|
||||
|
||||
int (*bdrv_open)(BlockDriverState *bs, int flags);
|
||||
int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags);
|
||||
int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -185,8 +241,7 @@ struct BlockDriver {
|
||||
* Returns 0 for completed check, -errno for internal errors.
|
||||
* The check results are stored in result.
|
||||
*/
|
||||
int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix);
|
||||
int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result);
|
||||
|
||||
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
|
||||
|
||||
@@ -209,6 +264,7 @@ struct BlockDriverState {
|
||||
int64_t total_sectors; /* if we are reading a disk image, give its
|
||||
size in sectors */
|
||||
int read_only; /* if true, the media is read only */
|
||||
int keep_read_only; /* if true, the media was requested to stay read only */
|
||||
int open_flags; /* flags used to open the file, re-used for re-open */
|
||||
int encrypted; /* if true, the media is encrypted */
|
||||
int valid_key; /* if true, a valid encryption key has been set */
|
||||
@@ -233,8 +289,6 @@ struct BlockDriverState {
|
||||
BlockDriverState *backing_hd;
|
||||
BlockDriverState *file;
|
||||
|
||||
NotifierList close_notifiers;
|
||||
|
||||
/* number of in-flight copy-on-read requests */
|
||||
unsigned int copy_on_read_in_flight;
|
||||
|
||||
@@ -265,7 +319,8 @@ struct BlockDriverState {
|
||||
|
||||
/* NOTE: the following infos are only hints for real hardware
|
||||
drivers. They are not used by the block driver */
|
||||
BlockdevOnError on_read_error, on_write_error;
|
||||
int cyls, heads, secs, translation;
|
||||
BlockErrorAction on_read_error, on_write_error;
|
||||
bool iostatus_enabled;
|
||||
BlockDeviceIoStatus iostatus;
|
||||
char device_name[32];
|
||||
@@ -278,7 +333,6 @@ struct BlockDriverState {
|
||||
|
||||
/* long-running background operation */
|
||||
BlockJob *job;
|
||||
|
||||
};
|
||||
|
||||
int get_tmp_filename(char *filename, int size);
|
||||
@@ -289,9 +343,92 @@ void bdrv_set_io_limits(BlockDriverState *bs,
|
||||
#ifdef _WIN32
|
||||
int is_windows_drive(const char *filename);
|
||||
#endif
|
||||
void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
|
||||
enum MonitorEvent ev,
|
||||
BlockErrorAction action, bool is_read);
|
||||
|
||||
/**
|
||||
* 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:
|
||||
@@ -301,7 +438,6 @@ void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
|
||||
* @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.
|
||||
* @on_error: The action to take upon error.
|
||||
* @cb: Completion function for the job.
|
||||
* @opaque: Opaque pointer value passed to @cb.
|
||||
* @errp: Error object.
|
||||
@@ -313,47 +449,7 @@ void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
|
||||
* @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, BlockdevOnError on_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
/**
|
||||
* commit_start:
|
||||
* @bs: Top Block device
|
||||
* @base: Block device that will be written into, and become the new top
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
* @on_error: The action to take upon error.
|
||||
* @cb: Completion function for the job.
|
||||
* @opaque: Opaque pointer value passed to @cb.
|
||||
* @errp: Error object.
|
||||
*
|
||||
*/
|
||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
/*
|
||||
* mirror_start:
|
||||
* @bs: Block device to operate on.
|
||||
* @target: Block device to write to.
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
* @mode: Whether to collapse all images in the chain to the target.
|
||||
* @on_source_error: The action to take upon error reading from the source.
|
||||
* @on_target_error: The action to take upon error writing to the target.
|
||||
* @cb: Completion function for the job.
|
||||
* @opaque: Opaque pointer value passed to @cb.
|
||||
* @errp: Error object.
|
||||
*
|
||||
* Start a mirroring operation on @bs. Clusters that are allocated
|
||||
* in @bs will be written to @bs until the job is cancelled or
|
||||
* manually completed. At the end of a successful mirroring job,
|
||||
* @bs will be switched to read from @target.
|
||||
*/
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, MirrorSyncMode mode,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
const char *base_id, int64_t speed,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Serving QEMU block devices via NBD
|
||||
*
|
||||
* Copyright (c) 2012 Red Hat, Inc.
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "blockdev.h"
|
||||
#include "hw/block-common.h"
|
||||
#include "monitor.h"
|
||||
#include "qerror.h"
|
||||
#include "sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
#include "nbd.h"
|
||||
#include "qemu_socket.h"
|
||||
|
||||
static int server_fd = -1;
|
||||
|
||||
static void nbd_accept(void *opaque)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
|
||||
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
|
||||
if (fd >= 0) {
|
||||
nbd_client_new(NULL, fd, nbd_client_put);
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
|
||||
{
|
||||
if (server_fd != -1) {
|
||||
error_setg(errp, "NBD server already running");
|
||||
return;
|
||||
}
|
||||
|
||||
server_fd = socket_listen(addr, errp);
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hook into the BlockDriverState notifiers to close the export when
|
||||
* the file is closed.
|
||||
*/
|
||||
typedef struct NBDCloseNotifier {
|
||||
Notifier n;
|
||||
NBDExport *exp;
|
||||
QTAILQ_ENTRY(NBDCloseNotifier) next;
|
||||
} NBDCloseNotifier;
|
||||
|
||||
static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers =
|
||||
QTAILQ_HEAD_INITIALIZER(close_notifiers);
|
||||
|
||||
static void nbd_close_notifier(Notifier *n, void *data)
|
||||
{
|
||||
NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n);
|
||||
|
||||
notifier_remove(&cn->n);
|
||||
QTAILQ_REMOVE(&close_notifiers, cn, next);
|
||||
|
||||
nbd_export_close(cn->exp);
|
||||
nbd_export_put(cn->exp);
|
||||
g_free(cn);
|
||||
}
|
||||
|
||||
static void nbd_server_put_ref(NBDExport *exp)
|
||||
{
|
||||
BlockDriverState *bs = nbd_export_get_blockdev(exp);
|
||||
drive_put_ref(drive_get_by_blockdev(bs));
|
||||
}
|
||||
|
||||
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
NBDExport *exp;
|
||||
NBDCloseNotifier *n;
|
||||
|
||||
if (server_fd == -1) {
|
||||
error_setg(errp, "NBD server not running");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nbd_export_find(device)) {
|
||||
error_setg(errp, "NBD server already exporting device '%s'", device);
|
||||
return;
|
||||
}
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_writable) {
|
||||
writable = false;
|
||||
}
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
writable = false;
|
||||
}
|
||||
|
||||
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
|
||||
nbd_server_put_ref);
|
||||
|
||||
nbd_export_set_name(exp, device);
|
||||
drive_get_ref(drive_get_by_blockdev(bs));
|
||||
|
||||
n = g_malloc0(sizeof(NBDCloseNotifier));
|
||||
n->n.notify = nbd_close_notifier;
|
||||
n->exp = exp;
|
||||
bdrv_add_close_notifier(bs, &n->n);
|
||||
QTAILQ_INSERT_TAIL(&close_notifiers, n, next);
|
||||
}
|
||||
|
||||
void qmp_nbd_server_stop(Error **errp)
|
||||
{
|
||||
while (!QTAILQ_EMPTY(&close_notifiers)) {
|
||||
NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers);
|
||||
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
|
||||
}
|
||||
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
}
|
||||
}
|
||||
+72
-300
@@ -7,9 +7,8 @@
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "block.h"
|
||||
#include "blockdev.h"
|
||||
#include "hw/block-common.h"
|
||||
#include "blockjob.h"
|
||||
#include "monitor.h"
|
||||
#include "qerror.h"
|
||||
#include "qemu-option.h"
|
||||
@@ -238,16 +237,16 @@ static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
|
||||
static int parse_block_error_action(const char *buf, bool is_read)
|
||||
static int parse_block_error_action(const char *buf, int is_read)
|
||||
{
|
||||
if (!strcmp(buf, "ignore")) {
|
||||
return BLOCKDEV_ON_ERROR_IGNORE;
|
||||
return BLOCK_ERR_IGNORE;
|
||||
} else if (!is_read && !strcmp(buf, "enospc")) {
|
||||
return BLOCKDEV_ON_ERROR_ENOSPC;
|
||||
return BLOCK_ERR_STOP_ENOSPC;
|
||||
} else if (!strcmp(buf, "stop")) {
|
||||
return BLOCKDEV_ON_ERROR_STOP;
|
||||
return BLOCK_ERR_STOP_ANY;
|
||||
} else if (!strcmp(buf, "report")) {
|
||||
return BLOCKDEV_ON_ERROR_REPORT;
|
||||
return BLOCK_ERR_REPORT;
|
||||
} else {
|
||||
error_report("'%s' invalid %s error action",
|
||||
buf, is_read ? "read" : "write");
|
||||
@@ -279,6 +278,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
{
|
||||
const char *buf;
|
||||
const char *file = NULL;
|
||||
char devname[128];
|
||||
const char *serial;
|
||||
const char *mediastr = "";
|
||||
BlockInterfaceType type;
|
||||
@@ -318,6 +318,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
serial = qemu_opt_get(opts, "serial");
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "if")) != NULL) {
|
||||
pstrcpy(devname, sizeof(devname), buf);
|
||||
for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
|
||||
;
|
||||
if (type == IF_COUNT) {
|
||||
@@ -326,20 +327,21 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
}
|
||||
} else {
|
||||
type = default_to_scsi ? IF_SCSI : IF_IDE;
|
||||
pstrcpy(devname, sizeof(devname), if_name[type]);
|
||||
}
|
||||
|
||||
max_devs = if_max_devs[type];
|
||||
|
||||
if (cyls || heads || secs) {
|
||||
if (cyls < 1) {
|
||||
if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
|
||||
error_report("invalid physical cyls number");
|
||||
return NULL;
|
||||
}
|
||||
if (heads < 1) {
|
||||
if (heads < 1 || (type == IF_IDE && heads > 16)) {
|
||||
error_report("invalid physical heads number");
|
||||
return NULL;
|
||||
}
|
||||
if (secs < 1) {
|
||||
if (secs < 1 || (type == IF_IDE && secs > 63)) {
|
||||
error_report("invalid physical secs number");
|
||||
return NULL;
|
||||
}
|
||||
@@ -378,7 +380,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_flags |= BDRV_O_CACHE_WB;
|
||||
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
|
||||
if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
|
||||
error_report("invalid cache option");
|
||||
@@ -400,11 +401,11 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
#endif
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
|
||||
if (is_help_option(buf)) {
|
||||
error_printf("Supported formats:");
|
||||
bdrv_iterate_format(bdrv_format_print, NULL);
|
||||
error_printf("\n");
|
||||
return NULL;
|
||||
if (strcmp(buf, "?") == 0) {
|
||||
error_printf("Supported formats:");
|
||||
bdrv_iterate_format(bdrv_format_print, NULL);
|
||||
error_printf("\n");
|
||||
return NULL;
|
||||
}
|
||||
drv = bdrv_find_whitelisted_format(buf);
|
||||
if (!drv) {
|
||||
@@ -433,13 +434,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "boot") != NULL) {
|
||||
fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
|
||||
"ignored. Future versions will reject this parameter. Please "
|
||||
"update your scripts.\n");
|
||||
}
|
||||
|
||||
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
||||
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) {
|
||||
error_report("werror is not supported by this bus type");
|
||||
@@ -452,7 +447,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
}
|
||||
}
|
||||
|
||||
on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
on_read_error = BLOCK_ERR_REPORT;
|
||||
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
||||
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
|
||||
error_report("rerror is not supported by this bus type");
|
||||
@@ -528,25 +523,21 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
||||
if (max_devs)
|
||||
snprintf(dinfo->id, 32, "%s%i%s%i",
|
||||
if_name[type], bus_id, mediastr, unit_id);
|
||||
devname, bus_id, mediastr, unit_id);
|
||||
else
|
||||
snprintf(dinfo->id, 32, "%s%s%i",
|
||||
if_name[type], mediastr, unit_id);
|
||||
devname, mediastr, unit_id);
|
||||
}
|
||||
dinfo->bdrv = bdrv_new(dinfo->id);
|
||||
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||
dinfo->bdrv->read_only = ro;
|
||||
dinfo->devaddr = devaddr;
|
||||
dinfo->type = type;
|
||||
dinfo->bus = bus_id;
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->cyls = cyls;
|
||||
dinfo->heads = heads;
|
||||
dinfo->secs = secs;
|
||||
dinfo->trans = translation;
|
||||
dinfo->opts = opts;
|
||||
dinfo->refcount = 1;
|
||||
dinfo->serial = serial;
|
||||
if (serial) {
|
||||
pstrcpy(dinfo->serial, sizeof(dinfo->serial), serial);
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
||||
|
||||
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
||||
@@ -559,7 +550,17 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
case IF_SCSI:
|
||||
case IF_XEN:
|
||||
case IF_NONE:
|
||||
dinfo->media_cd = media == MEDIA_CDROM;
|
||||
switch(media) {
|
||||
case MEDIA_DISK:
|
||||
if (cyls != 0) {
|
||||
bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs);
|
||||
bdrv_set_translation_hint(dinfo->bdrv, translation);
|
||||
}
|
||||
break;
|
||||
case MEDIA_CDROM:
|
||||
dinfo->media_cd = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IF_SD:
|
||||
case IF_FLOPPY:
|
||||
@@ -568,7 +569,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
break;
|
||||
case IF_VIRTIO:
|
||||
/* add virtio block device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
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 {
|
||||
@@ -611,10 +612,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
|
||||
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
||||
|
||||
if (ro && copy_on_read) {
|
||||
error_report("warning: disabling copy_on_read on readonly drive");
|
||||
}
|
||||
|
||||
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
|
||||
if (ret < 0) {
|
||||
error_report("could not open disk image %s: %s",
|
||||
@@ -812,11 +809,6 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
||||
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);
|
||||
/* We don't need (or want) to use the transactional
|
||||
* bdrv_reopen_multiple() across all the entries at once, because we
|
||||
* don't want to abort all of them if one of them fails the reopen */
|
||||
bdrv_reopen(states->new_bs, states->new_bs->open_flags & ~BDRV_O_RDWR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* success */
|
||||
@@ -836,6 +828,7 @@ exit:
|
||||
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
|
||||
g_free(states);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1062,12 +1055,26 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_cb(void *opaque, int ret)
|
||||
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_job_cb(bs, bs->job, ret);
|
||||
trace_block_stream_cb(bs, bs->job, ret);
|
||||
|
||||
assert(bs->job);
|
||||
obj = qobject_from_block_job(bs->job);
|
||||
@@ -1087,18 +1094,13 @@ static void block_job_cb(void *opaque, int ret)
|
||||
}
|
||||
|
||||
void qmp_block_stream(const char *device, bool has_base,
|
||||
const char *base, bool has_speed, int64_t speed,
|
||||
bool has_on_error, BlockdevOnError on_error,
|
||||
Error **errp)
|
||||
const char *base, bool has_speed,
|
||||
int64_t speed, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *base_bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!has_on_error) {
|
||||
on_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
}
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
@@ -1114,7 +1116,7 @@ void qmp_block_stream(const char *device, bool has_base,
|
||||
}
|
||||
|
||||
stream_start(bs, base_bs, base, has_speed ? speed : 0,
|
||||
on_error, block_job_cb, bs, &local_err);
|
||||
block_stream_cb, bs, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -1128,199 +1130,6 @@ void qmp_block_stream(const char *device, bool has_base,
|
||||
trace_qmp_block_stream(bs, bs->job);
|
||||
}
|
||||
|
||||
void qmp_block_commit(const char *device,
|
||||
bool has_base, const char *base, const char *top,
|
||||
bool has_speed, int64_t speed,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *base_bs, *top_bs;
|
||||
Error *local_err = NULL;
|
||||
/* This will be part of the QMP command, if/when the
|
||||
* BlockdevOnError change for blkmirror makes it in
|
||||
*/
|
||||
BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
|
||||
/* drain all i/o before commits */
|
||||
bdrv_drain_all();
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
}
|
||||
|
||||
/* default top_bs is the active layer */
|
||||
top_bs = bs;
|
||||
|
||||
if (top) {
|
||||
if (strcmp(bs->filename, top) != 0) {
|
||||
top_bs = bdrv_find_backing_image(bs, top);
|
||||
}
|
||||
}
|
||||
|
||||
if (top_bs == NULL) {
|
||||
error_setg(errp, "Top image file %s not found", top ? top : "NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_base && base) {
|
||||
base_bs = bdrv_find_backing_image(top_bs, base);
|
||||
} else {
|
||||
base_bs = bdrv_find_base(top_bs);
|
||||
}
|
||||
|
||||
if (base_bs == NULL) {
|
||||
error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
|
||||
&local_err);
|
||||
if (local_err != NULL) {
|
||||
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));
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_on_source_error, BlockdevOnError on_source_error,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *source, *target_bs;
|
||||
BlockDriver *proto_drv;
|
||||
BlockDriver *drv = NULL;
|
||||
Error *local_err = NULL;
|
||||
int flags;
|
||||
uint64_t size;
|
||||
int ret;
|
||||
|
||||
if (!has_speed) {
|
||||
speed = 0;
|
||||
}
|
||||
if (!has_on_source_error) {
|
||||
on_source_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
}
|
||||
if (!has_on_target_error) {
|
||||
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
}
|
||||
if (!has_mode) {
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bdrv_is_inserted(bs)) {
|
||||
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_format) {
|
||||
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
||||
}
|
||||
if (format) {
|
||||
drv = bdrv_find_format(format);
|
||||
if (!drv) {
|
||||
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
flags = bs->open_flags | BDRV_O_RDWR;
|
||||
source = bs->backing_hd;
|
||||
if (!source && sync == MIRROR_SYNC_MODE_TOP) {
|
||||
sync = MIRROR_SYNC_MODE_FULL;
|
||||
}
|
||||
|
||||
proto_drv = bdrv_find_protocol(target);
|
||||
if (!proto_drv) {
|
||||
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) {
|
||||
/* create new image w/o backing file */
|
||||
assert(format && drv);
|
||||
bdrv_get_geometry(bs, &size);
|
||||
size *= 512;
|
||||
ret = bdrv_img_create(target, format,
|
||||
NULL, NULL, NULL, size, flags);
|
||||
} else {
|
||||
switch (mode) {
|
||||
case NEW_IMAGE_MODE_EXISTING:
|
||||
ret = 0;
|
||||
break;
|
||||
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
|
||||
/* create new image with backing file */
|
||||
ret = bdrv_img_create(target, format,
|
||||
source->filename,
|
||||
source->drv->format_name,
|
||||
NULL, -1, flags);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, target);
|
||||
return;
|
||||
}
|
||||
|
||||
target_bs = bdrv_new("");
|
||||
ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv);
|
||||
|
||||
if (ret < 0) {
|
||||
bdrv_delete(target_bs);
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, target);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need a backing file if we will copy parts of a cluster. */
|
||||
if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 &&
|
||||
bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) {
|
||||
ret = bdrv_open_backing_file(target_bs);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(target_bs);
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
|
||||
block_job_cb, bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
bdrv_delete(target_bs);
|
||||
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));
|
||||
}
|
||||
|
||||
static BlockJob *find_block_job(const char *device)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -1337,28 +1146,19 @@ 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_BLOCK_JOB_NOT_ACTIVE, device);
|
||||
error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
block_job_set_speed(job, speed, errp);
|
||||
}
|
||||
|
||||
void qmp_block_job_cancel(const char *device,
|
||||
bool has_force, bool force, Error **errp)
|
||||
void qmp_block_job_cancel(const char *device, Error **errp)
|
||||
{
|
||||
BlockJob *job = find_block_job(device);
|
||||
|
||||
if (!has_force) {
|
||||
force = false;
|
||||
}
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
if (job->paused && !force) {
|
||||
error_set(errp, QERR_BLOCK_JOB_PAUSED, device);
|
||||
error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1366,53 +1166,25 @@ void qmp_block_job_cancel(const char *device,
|
||||
block_job_cancel(job);
|
||||
}
|
||||
|
||||
void qmp_block_job_pause(const char *device, Error **errp)
|
||||
{
|
||||
BlockJob *job = find_block_job(device);
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_pause(job);
|
||||
block_job_pause(job);
|
||||
}
|
||||
|
||||
void qmp_block_job_resume(const char *device, Error **errp)
|
||||
{
|
||||
BlockJob *job = find_block_job(device);
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_resume(job);
|
||||
block_job_resume(job);
|
||||
}
|
||||
|
||||
void qmp_block_job_complete(const char *device, Error **errp)
|
||||
{
|
||||
BlockJob *job = find_block_job(device);
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_complete(job);
|
||||
block_job_complete(job, errp);
|
||||
}
|
||||
|
||||
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
|
||||
{
|
||||
BlockJobInfoList **prev = opaque;
|
||||
BlockJob *job = bs->job;
|
||||
|
||||
if (job) {
|
||||
BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
|
||||
elem->value = block_job_query(bs->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;
|
||||
}
|
||||
|
||||
+3
-2
@@ -17,6 +17,8 @@
|
||||
void blockdev_mark_auto_del(BlockDriverState *bs);
|
||||
void blockdev_auto_del(BlockDriverState *bs);
|
||||
|
||||
#define BLOCK_SERIAL_STRLEN 20
|
||||
|
||||
typedef enum {
|
||||
IF_DEFAULT = -1, /* for use with drive_add() only */
|
||||
IF_NONE,
|
||||
@@ -33,9 +35,8 @@ struct DriveInfo {
|
||||
int unit;
|
||||
int auto_del; /* see blockdev_mark_auto_del() */
|
||||
int media_cd;
|
||||
int cyls, heads, secs, trans;
|
||||
QemuOpts *opts;
|
||||
const char *serial;
|
||||
char serial[BLOCK_SERIAL_STRLEN + 1];
|
||||
QTAILQ_ENTRY(DriveInfo) next;
|
||||
int refcount;
|
||||
};
|
||||
|
||||
-283
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator block driver
|
||||
*
|
||||
* Copyright (c) 2011 IBM Corp.
|
||||
* Copyright (c) 2012 Red Hat, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config-host.h"
|
||||
#include "qemu-common.h"
|
||||
#include "trace.h"
|
||||
#include "monitor.h"
|
||||
#include "block.h"
|
||||
#include "blockjob.h"
|
||||
#include "block_int.h"
|
||||
#include "qjson.h"
|
||||
#include "qemu-coroutine.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu-timer.h"
|
||||
|
||||
void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
|
||||
int64_t speed, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
BlockJob *job;
|
||||
|
||||
if (bs->job || bdrv_in_use(bs)) {
|
||||
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
||||
return NULL;
|
||||
}
|
||||
bdrv_set_in_use(bs, 1);
|
||||
|
||||
job = g_malloc0(job_type->instance_size);
|
||||
job->job_type = job_type;
|
||||
job->bs = bs;
|
||||
job->cb = cb;
|
||||
job->opaque = opaque;
|
||||
job->busy = true;
|
||||
bs->job = job;
|
||||
|
||||
/* Only set speed when necessary to avoid NotSupported error */
|
||||
if (speed != 0) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
bs->job = NULL;
|
||||
g_free(job);
|
||||
bdrv_set_in_use(bs, 0);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
void block_job_completed(BlockJob *job, int ret)
|
||||
{
|
||||
BlockDriverState *bs = job->bs;
|
||||
|
||||
assert(bs->job == job);
|
||||
job->cb(job->opaque, ret);
|
||||
bs->job = NULL;
|
||||
g_free(job);
|
||||
bdrv_set_in_use(bs, 0);
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!job->job_type->set_speed) {
|
||||
error_set(errp, QERR_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
job->job_type->set_speed(job, speed, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
job->speed = speed;
|
||||
}
|
||||
|
||||
void block_job_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
if (job->paused || job->cancelled || !job->job_type->complete) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
job->job_type->complete(job, errp);
|
||||
}
|
||||
|
||||
void block_job_pause(BlockJob *job)
|
||||
{
|
||||
job->paused = true;
|
||||
}
|
||||
|
||||
bool block_job_is_paused(BlockJob *job)
|
||||
{
|
||||
return job->paused;
|
||||
}
|
||||
|
||||
void block_job_resume(BlockJob *job)
|
||||
{
|
||||
job->paused = false;
|
||||
block_job_iostatus_reset(job);
|
||||
if (job->co && !job->busy) {
|
||||
qemu_coroutine_enter(job->co, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_cancel(BlockJob *job)
|
||||
{
|
||||
job->cancelled = true;
|
||||
block_job_resume(job);
|
||||
}
|
||||
|
||||
bool block_job_is_cancelled(BlockJob *job)
|
||||
{
|
||||
return job->cancelled;
|
||||
}
|
||||
|
||||
void block_job_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
||||
if (job->job_type->iostatus_reset) {
|
||||
job->job_type->iostatus_reset(job);
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockCancelData {
|
||||
BlockJob *job;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
bool cancelled;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void block_job_cancel_cb(void *opaque, int ret)
|
||||
{
|
||||
struct BlockCancelData *data = opaque;
|
||||
|
||||
data->cancelled = block_job_is_cancelled(data->job);
|
||||
data->ret = ret;
|
||||
data->cb(data->opaque, ret);
|
||||
}
|
||||
|
||||
int block_job_cancel_sync(BlockJob *job)
|
||||
{
|
||||
struct BlockCancelData data;
|
||||
BlockDriverState *bs = job->bs;
|
||||
|
||||
assert(bs->job == job);
|
||||
|
||||
/* Set up our own callback to store the result and chain to
|
||||
* the original callback.
|
||||
*/
|
||||
data.job = job;
|
||||
data.cb = job->cb;
|
||||
data.opaque = job->opaque;
|
||||
data.ret = -EINPROGRESS;
|
||||
job->cb = block_job_cancel_cb;
|
||||
job->opaque = &data;
|
||||
block_job_cancel(job);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
|
||||
}
|
||||
|
||||
void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns)
|
||||
{
|
||||
assert(job->busy);
|
||||
|
||||
/* Check cancellation *before* setting busy = false, too! */
|
||||
if (block_job_is_cancelled(job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
job->busy = false;
|
||||
if (block_job_is_paused(job)) {
|
||||
qemu_coroutine_yield();
|
||||
} else {
|
||||
co_sleep_ns(clock, ns);
|
||||
}
|
||||
job->busy = true;
|
||||
}
|
||||
|
||||
BlockJobInfo *block_job_query(BlockJob *job)
|
||||
{
|
||||
BlockJobInfo *info = g_new0(BlockJobInfo, 1);
|
||||
info->type = g_strdup(job->job_type->job_type);
|
||||
info->device = g_strdup(bdrv_get_device_name(job->bs));
|
||||
info->len = job->len;
|
||||
info->busy = job->busy;
|
||||
info->paused = job->paused;
|
||||
info->offset = job->offset;
|
||||
info->speed = job->speed;
|
||||
info->io_status = job->iostatus;
|
||||
return info;
|
||||
}
|
||||
|
||||
static void block_job_iostatus_set_err(BlockJob *job, int error)
|
||||
{
|
||||
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||
job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE :
|
||||
BLOCK_DEVICE_IO_STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void block_job_ready(BlockJob *job)
|
||||
{
|
||||
QObject *data = qobject_from_block_job(job);
|
||||
monitor_protocol_event(QEVENT_BLOCK_JOB_READY, data);
|
||||
qobject_decref(data);
|
||||
}
|
||||
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
BlockdevOnError on_err,
|
||||
int is_read, int error)
|
||||
{
|
||||
BlockErrorAction action;
|
||||
|
||||
switch (on_err) {
|
||||
case BLOCKDEV_ON_ERROR_ENOSPC:
|
||||
action = (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT;
|
||||
break;
|
||||
case BLOCKDEV_ON_ERROR_STOP:
|
||||
action = BDRV_ACTION_STOP;
|
||||
break;
|
||||
case BLOCKDEV_ON_ERROR_REPORT:
|
||||
action = BDRV_ACTION_REPORT;
|
||||
break;
|
||||
case BLOCKDEV_ON_ERROR_IGNORE:
|
||||
action = BDRV_ACTION_IGNORE;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
bdrv_emit_qmp_error_event(job->bs, QEVENT_BLOCK_JOB_ERROR, action, is_read);
|
||||
if (action == BDRV_ACTION_STOP) {
|
||||
block_job_pause(job);
|
||||
block_job_iostatus_set_err(job, error);
|
||||
if (bs != job->bs) {
|
||||
bdrv_iostatus_set_err(bs, error);
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
-278
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Declarations for long-running block device operations
|
||||
*
|
||||
* Copyright (c) 2011 IBM Corp.
|
||||
* Copyright (c) 2012 Red Hat, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef BLOCKJOB_H
|
||||
#define BLOCKJOB_H 1
|
||||
|
||||
#include "block.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/** Optional callback for job types that need to forward I/O status reset */
|
||||
void (*iostatus_reset)(BlockJob *job);
|
||||
|
||||
/**
|
||||
* Optional callback for job types whose completion must be triggered
|
||||
* manually.
|
||||
*/
|
||||
void (*complete)(BlockJob *job, 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 true if the job is either paused, or will pause itself
|
||||
* as soon as possible (if busy == true).
|
||||
*/
|
||||
bool paused;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/** Status that is published by the query-block-jobs QMP API */
|
||||
BlockDeviceIoStatus iostatus;
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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_completed:
|
||||
* @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_completed(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_complete:
|
||||
* @job: The job to be completed.
|
||||
* @errp: Error object.
|
||||
*
|
||||
* Asynchronously complete the specified job.
|
||||
*/
|
||||
void block_job_complete(BlockJob *job, Error **errp);
|
||||
|
||||
/**
|
||||
* 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_query:
|
||||
* @job: The job to get information about.
|
||||
*
|
||||
* Return information about a job.
|
||||
*/
|
||||
BlockJobInfo *block_job_query(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_pause:
|
||||
* @job: The job to be paused.
|
||||
*
|
||||
* Asynchronously pause the specified job.
|
||||
*/
|
||||
void block_job_pause(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_resume:
|
||||
* @job: The job to be resumed.
|
||||
*
|
||||
* Resume the specified job.
|
||||
*/
|
||||
void block_job_resume(BlockJob *job);
|
||||
|
||||
/**
|
||||
* qobject_from_block_job:
|
||||
* @job: The job whose information is requested.
|
||||
*
|
||||
* Return a QDict corresponding to @job's query-block-jobs entry.
|
||||
*/
|
||||
QObject *qobject_from_block_job(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_ready:
|
||||
* @job: The job which is now ready to complete.
|
||||
*
|
||||
* Send a BLOCK_JOB_READY event for the specified job.
|
||||
*/
|
||||
void block_job_ready(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_is_paused:
|
||||
* @job: The job being queried.
|
||||
*
|
||||
* Returns whether the job is currently paused, or will pause
|
||||
* as soon as it reaches a sleeping point.
|
||||
*/
|
||||
bool block_job_is_paused(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_cancel_sync:
|
||||
* @job: The job to be canceled.
|
||||
*
|
||||
* Synchronously cancel the job. The completion callback is called
|
||||
* before the function returns. The job may actually complete
|
||||
* instead of canceling itself; the circumstances under which this
|
||||
* happens depend on the kind of job that is active.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* block_job_iostatus_reset:
|
||||
* @job: The job whose I/O status should be reset.
|
||||
*
|
||||
* Reset I/O status on @job and on BlockDriverState objects it uses,
|
||||
* other than job->bs.
|
||||
*/
|
||||
void block_job_iostatus_reset(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_error_action:
|
||||
* @job: The job to signal an error for.
|
||||
* @bs: The block device on which to set an I/O error.
|
||||
* @on_err: The error action setting.
|
||||
* @is_read: Whether the operation was a read.
|
||||
* @error: The error that was reported.
|
||||
*
|
||||
* Report an I/O error for a block job and possibly stop the VM. Return the
|
||||
* action that was selected based on @on_err and @error.
|
||||
*/
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
BlockdevOnError on_err,
|
||||
int is_read, int error);
|
||||
#endif
|
||||
@@ -1,2 +0,0 @@
|
||||
obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
|
||||
uaccess.o
|
||||
+3
-3
@@ -681,7 +681,7 @@ static void usage(void)
|
||||
"-g port wait gdb connection to port\n"
|
||||
"-L path set the elf interpreter prefix (default=%s)\n"
|
||||
"-s size set the stack size in bytes (default=%ld)\n"
|
||||
"-cpu model select CPU (-cpu help for list)\n"
|
||||
"-cpu model select CPU (-cpu ? for list)\n"
|
||||
"-drop-ld-preload drop LD_PRELOAD for target process\n"
|
||||
"-E var=value sets/modifies targets environment variable(s)\n"
|
||||
"-U var unsets targets environment variable(s)\n"
|
||||
@@ -825,7 +825,7 @@ int main(int argc, char **argv)
|
||||
qemu_uname_release = argv[optind++];
|
||||
} else if (!strcmp(r, "cpu")) {
|
||||
cpu_model = argv[optind++];
|
||||
if (is_help_option(cpu_model)) {
|
||||
if (strcmp(cpu_model, "?") == 0) {
|
||||
/* XXX: implement xxx_cpu_list for targets that still miss it */
|
||||
#if defined(cpu_list)
|
||||
cpu_list(stdout, &fprintf);
|
||||
@@ -918,7 +918,7 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
cpu_reset(ENV_GET_CPU(env));
|
||||
cpu_state_reset(env);
|
||||
#endif
|
||||
thread_env = env;
|
||||
|
||||
|
||||
+88
-64
@@ -23,7 +23,11 @@
|
||||
|
||||
typedef struct QEMUFileBuffered
|
||||
{
|
||||
MigrationState *migration_state;
|
||||
BufferedPutFunc *put_buffer;
|
||||
BufferedPutReadyFunc *put_ready;
|
||||
BufferedWaitForUnfreezeFunc *wait_for_unfreeze;
|
||||
BufferedCloseFunc *close;
|
||||
void *opaque;
|
||||
QEMUFile *file;
|
||||
int freeze_output;
|
||||
size_t bytes_xfer;
|
||||
@@ -46,60 +50,70 @@ static void buffered_append(QEMUFileBuffered *s,
|
||||
const uint8_t *buf, size_t size)
|
||||
{
|
||||
if (size > (s->buffer_capacity - s->buffer_size)) {
|
||||
void *tmp;
|
||||
|
||||
DPRINTF("increasing buffer capacity from %zu by %zu\n",
|
||||
s->buffer_capacity, size + 1024);
|
||||
|
||||
s->buffer_capacity += size + 1024;
|
||||
|
||||
s->buffer = g_realloc(s->buffer, s->buffer_capacity);
|
||||
tmp = g_realloc(s->buffer, s->buffer_capacity);
|
||||
if (tmp == NULL) {
|
||||
fprintf(stderr, "qemu file buffer expansion failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->buffer = tmp;
|
||||
}
|
||||
|
||||
memcpy(s->buffer + s->buffer_size, buf, size);
|
||||
s->buffer_size += size;
|
||||
}
|
||||
|
||||
static ssize_t buffered_flush(QEMUFileBuffered *s)
|
||||
static void buffered_flush(QEMUFileBuffered *s)
|
||||
{
|
||||
size_t offset = 0;
|
||||
ssize_t ret = 0;
|
||||
int error;
|
||||
|
||||
error = qemu_file_get_error(s->file);
|
||||
if (error != 0) {
|
||||
DPRINTF("flush when error, bailing: %s\n", strerror(-error));
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
|
||||
|
||||
while (s->bytes_xfer < s->xfer_limit && offset < s->buffer_size) {
|
||||
size_t to_send = MIN(s->buffer_size - offset, s->xfer_limit - s->bytes_xfer);
|
||||
ret = migrate_fd_put_buffer(s->migration_state, s->buffer + offset,
|
||||
to_send);
|
||||
while (offset < s->buffer_size) {
|
||||
ssize_t ret;
|
||||
|
||||
ret = s->put_buffer(s->opaque, s->buffer + offset,
|
||||
s->buffer_size - offset);
|
||||
if (ret == -EAGAIN) {
|
||||
DPRINTF("backend not ready, freezing\n");
|
||||
ret = 0;
|
||||
s->freeze_output = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
DPRINTF("error flushing data, %zd\n", ret);
|
||||
qemu_file_set_error(s->file, ret);
|
||||
break;
|
||||
} else {
|
||||
DPRINTF("flushed %zd byte(s)\n", ret);
|
||||
offset += ret;
|
||||
s->bytes_xfer += ret;
|
||||
}
|
||||
}
|
||||
|
||||
DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
|
||||
memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
|
||||
s->buffer_size -= offset;
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
ssize_t error;
|
||||
int offset = 0, error;
|
||||
ssize_t ret;
|
||||
|
||||
DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
|
||||
|
||||
@@ -112,54 +126,65 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in
|
||||
DPRINTF("unfreezing output\n");
|
||||
s->freeze_output = 0;
|
||||
|
||||
if (size > 0) {
|
||||
DPRINTF("buffering %d bytes\n", size - offset);
|
||||
buffered_append(s, buf, size);
|
||||
buffered_flush(s);
|
||||
|
||||
while (!s->freeze_output && offset < size) {
|
||||
if (s->bytes_xfer > s->xfer_limit) {
|
||||
DPRINTF("transfer limit exceeded when putting\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = s->put_buffer(s->opaque, buf + offset, size - offset);
|
||||
if (ret == -EAGAIN) {
|
||||
DPRINTF("backend not ready, freezing\n");
|
||||
s->freeze_output = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
DPRINTF("error putting\n");
|
||||
qemu_file_set_error(s->file, ret);
|
||||
offset = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("put %zd byte(s)\n", ret);
|
||||
offset += ret;
|
||||
s->bytes_xfer += ret;
|
||||
}
|
||||
|
||||
error = buffered_flush(s);
|
||||
if (error < 0) {
|
||||
DPRINTF("buffered flush error. bailing: %s\n", strerror(-error));
|
||||
return error;
|
||||
if (offset >= 0) {
|
||||
DPRINTF("buffering %d bytes\n", size - offset);
|
||||
buffered_append(s, buf + offset, size - offset);
|
||||
offset = size;
|
||||
}
|
||||
|
||||
if (pos == 0 && size == 0) {
|
||||
DPRINTF("file is ready\n");
|
||||
if (!s->freeze_output && s->bytes_xfer < s->xfer_limit) {
|
||||
if (s->bytes_xfer <= s->xfer_limit) {
|
||||
DPRINTF("notifying client\n");
|
||||
migrate_fd_put_ready(s->migration_state);
|
||||
s->put_ready(s->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int buffered_close(void *opaque)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
ssize_t ret = 0;
|
||||
int ret2;
|
||||
int ret;
|
||||
|
||||
DPRINTF("closing\n");
|
||||
|
||||
s->xfer_limit = INT_MAX;
|
||||
while (!qemu_file_get_error(s->file) && s->buffer_size) {
|
||||
ret = buffered_flush(s);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
if (s->freeze_output) {
|
||||
ret = migrate_fd_wait_for_unfreeze(s->migration_state);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffered_flush(s);
|
||||
if (s->freeze_output)
|
||||
s->wait_for_unfreeze(s->opaque);
|
||||
}
|
||||
|
||||
ret2 = migrate_fd_close(s->migration_state);
|
||||
if (ret >= 0) {
|
||||
ret = ret2;
|
||||
}
|
||||
ret = s->close(s->opaque);
|
||||
|
||||
qemu_del_timer(s->timer);
|
||||
qemu_free_timer(s->timer);
|
||||
g_free(s->buffer);
|
||||
@@ -174,13 +199,6 @@ static int buffered_close(void *opaque)
|
||||
* 1: Time to stop
|
||||
* negative: There has been an error
|
||||
*/
|
||||
static int buffered_get_fd(void *opaque)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
|
||||
return qemu_get_fd(s->file);
|
||||
}
|
||||
|
||||
static int buffered_rate_limit(void *opaque)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
@@ -238,28 +256,34 @@ static void buffered_rate_tick(void *opaque)
|
||||
|
||||
s->bytes_xfer = 0;
|
||||
|
||||
buffered_put_buffer(s, NULL, 0, 0);
|
||||
buffered_flush(s);
|
||||
|
||||
/* Add some checks around this */
|
||||
s->put_ready(s->opaque);
|
||||
}
|
||||
|
||||
static const QEMUFileOps buffered_file_ops = {
|
||||
.get_fd = buffered_get_fd,
|
||||
.put_buffer = buffered_put_buffer,
|
||||
.close = buffered_close,
|
||||
.rate_limit = buffered_rate_limit,
|
||||
.get_rate_limit = buffered_get_rate_limit,
|
||||
.set_rate_limit = buffered_set_rate_limit,
|
||||
};
|
||||
|
||||
QEMUFile *qemu_fopen_ops_buffered(MigrationState *migration_state)
|
||||
QEMUFile *qemu_fopen_ops_buffered(void *opaque,
|
||||
size_t bytes_per_sec,
|
||||
BufferedPutFunc *put_buffer,
|
||||
BufferedPutReadyFunc *put_ready,
|
||||
BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
|
||||
BufferedCloseFunc *close)
|
||||
{
|
||||
QEMUFileBuffered *s;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
|
||||
s->migration_state = migration_state;
|
||||
s->xfer_limit = migration_state->bandwidth_limit / 10;
|
||||
s->opaque = opaque;
|
||||
s->xfer_limit = bytes_per_sec / 10;
|
||||
s->put_buffer = put_buffer;
|
||||
s->put_ready = put_ready;
|
||||
s->wait_for_unfreeze = wait_for_unfreeze;
|
||||
s->close = close;
|
||||
|
||||
s->file = qemu_fopen_ops(s, &buffered_file_ops);
|
||||
s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
|
||||
buffered_close, buffered_rate_limit,
|
||||
buffered_set_rate_limit,
|
||||
buffered_get_rate_limit);
|
||||
|
||||
s->timer = qemu_new_timer_ms(rt_clock, buffered_rate_tick, s);
|
||||
|
||||
|
||||
+10
-2
@@ -15,8 +15,16 @@
|
||||
#define QEMU_BUFFERED_FILE_H
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "migration.h"
|
||||
|
||||
QEMUFile *qemu_fopen_ops_buffered(MigrationState *migration_state);
|
||||
typedef ssize_t (BufferedPutFunc)(void *opaque, const void *data, size_t size);
|
||||
typedef void (BufferedPutReadyFunc)(void *opaque);
|
||||
typedef void (BufferedWaitForUnfreezeFunc)(void *opaque);
|
||||
typedef int (BufferedCloseFunc)(void *opaque);
|
||||
|
||||
QEMUFile *qemu_fopen_ops_buffered(void *opaque, size_t xfer_limit,
|
||||
BufferedPutFunc *put_buffer,
|
||||
BufferedPutReadyFunc *put_ready,
|
||||
BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
|
||||
BufferedCloseFunc *close);
|
||||
|
||||
#endif
|
||||
@@ -44,11 +44,6 @@
|
||||
/* Use gnu_printf when supported (qemu uses standard format strings). */
|
||||
# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
|
||||
# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
|
||||
# if defined(_WIN32)
|
||||
/* Map __printf__ to __gnu_printf__ because we want standard format strings
|
||||
* even when MinGW or GLib include files use __printf__. */
|
||||
# define __printf__ __gnu_printf__
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
#define GCC_ATTR /**/
|
||||
|
||||
@@ -24,12 +24,10 @@
|
||||
#include "qemu-common.h"
|
||||
#include "console.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
//#define DEBUG_CONSOLE
|
||||
#define DEFAULT_BACKSCROLL 512
|
||||
#define MAX_CONSOLES 12
|
||||
#define CONSOLE_CURSOR_PERIOD 500
|
||||
|
||||
#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
|
||||
@@ -114,20 +112,20 @@ typedef enum {
|
||||
TEXT_CONSOLE_FIXED_SIZE
|
||||
} console_type_t;
|
||||
|
||||
struct QemuConsole {
|
||||
/* ??? This is mis-named.
|
||||
It is used for both text and graphical consoles. */
|
||||
struct TextConsole {
|
||||
int index;
|
||||
console_type_t console_type;
|
||||
DisplayState *ds;
|
||||
|
||||
/* Graphic console state. */
|
||||
vga_hw_update_ptr hw_update;
|
||||
vga_hw_invalidate_ptr hw_invalidate;
|
||||
vga_hw_screen_dump_ptr hw_screen_dump;
|
||||
vga_hw_text_update_ptr hw_text_update;
|
||||
void *hw;
|
||||
int g_width, g_height;
|
||||
|
||||
/* Text console state */
|
||||
int g_width, g_height;
|
||||
int width;
|
||||
int height;
|
||||
int total_height;
|
||||
@@ -141,8 +139,6 @@ struct QemuConsole {
|
||||
TextCell *cells;
|
||||
int text_x[2], text_y[2], cursor_invalidate;
|
||||
int echo;
|
||||
bool cursor_visible_phase;
|
||||
QEMUTimer *cursor_timer;
|
||||
|
||||
int update_x0;
|
||||
int update_y0;
|
||||
@@ -161,8 +157,8 @@ struct QemuConsole {
|
||||
};
|
||||
|
||||
static DisplayState *display_state;
|
||||
static QemuConsole *active_console;
|
||||
static QemuConsole *consoles[MAX_CONSOLES];
|
||||
static TextConsole *active_console;
|
||||
static TextConsole *consoles[MAX_CONSOLES];
|
||||
static int nb_consoles = 0;
|
||||
|
||||
void vga_hw_update(void)
|
||||
@@ -177,9 +173,9 @@ void vga_hw_invalidate(void)
|
||||
active_console->hw_invalidate(active_console->hw);
|
||||
}
|
||||
|
||||
void qmp_screendump(const char *filename, Error **errp)
|
||||
void vga_hw_screen_dump(const char *filename)
|
||||
{
|
||||
QemuConsole *previous_active_console;
|
||||
TextConsole *previous_active_console;
|
||||
bool cswitch;
|
||||
|
||||
previous_active_console = active_console;
|
||||
@@ -191,9 +187,9 @@ void qmp_screendump(const char *filename, Error **errp)
|
||||
console_select(0);
|
||||
}
|
||||
if (consoles[0] && consoles[0]->hw_screen_dump) {
|
||||
consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
|
||||
consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch);
|
||||
} else {
|
||||
error_setg(errp, "device doesn't support screendump\n");
|
||||
error_report("screen dump not implemented");
|
||||
}
|
||||
|
||||
if (cswitch) {
|
||||
@@ -521,7 +517,7 @@ static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
|
||||
}
|
||||
}
|
||||
|
||||
static void text_console_resize(QemuConsole *s)
|
||||
static void text_console_resize(TextConsole *s)
|
||||
{
|
||||
TextCell *cells, *c, *c1;
|
||||
int w1, x, y, last_width;
|
||||
@@ -553,7 +549,7 @@ static void text_console_resize(QemuConsole *s)
|
||||
s->cells = cells;
|
||||
}
|
||||
|
||||
static inline void text_update_xy(QemuConsole *s, int x, int y)
|
||||
static inline void text_update_xy(TextConsole *s, int x, int y)
|
||||
{
|
||||
s->text_x[0] = MIN(s->text_x[0], x);
|
||||
s->text_x[1] = MAX(s->text_x[1], x);
|
||||
@@ -561,7 +557,7 @@ static inline void text_update_xy(QemuConsole *s, int x, int y)
|
||||
s->text_y[1] = MAX(s->text_y[1], y);
|
||||
}
|
||||
|
||||
static void invalidate_xy(QemuConsole *s, int x, int y)
|
||||
static void invalidate_xy(TextConsole *s, int x, int y)
|
||||
{
|
||||
if (s->update_x0 > x * FONT_WIDTH)
|
||||
s->update_x0 = x * FONT_WIDTH;
|
||||
@@ -573,7 +569,7 @@ static void invalidate_xy(QemuConsole *s, int x, int y)
|
||||
s->update_y1 = (y + 1) * FONT_HEIGHT;
|
||||
}
|
||||
|
||||
static void update_xy(QemuConsole *s, int x, int y)
|
||||
static void update_xy(TextConsole *s, int x, int y)
|
||||
{
|
||||
TextCell *c;
|
||||
int y1, y2;
|
||||
@@ -597,7 +593,7 @@ static void update_xy(QemuConsole *s, int x, int y)
|
||||
}
|
||||
}
|
||||
|
||||
static void console_show_cursor(QemuConsole *s, int show)
|
||||
static void console_show_cursor(TextConsole *s, int show)
|
||||
{
|
||||
TextCell *c;
|
||||
int y, y1;
|
||||
@@ -619,7 +615,7 @@ static void console_show_cursor(QemuConsole *s, int show)
|
||||
y += s->total_height;
|
||||
if (y < s->height) {
|
||||
c = &s->cells[y1 * s->width + x];
|
||||
if (show && s->cursor_visible_phase) {
|
||||
if (show) {
|
||||
TextAttributes t_attrib = s->t_attrib_default;
|
||||
t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
|
||||
vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
|
||||
@@ -631,45 +627,42 @@ static void console_show_cursor(QemuConsole *s, int show)
|
||||
}
|
||||
}
|
||||
|
||||
static void console_refresh(QemuConsole *s)
|
||||
static void console_refresh(TextConsole *s)
|
||||
{
|
||||
TextCell *c;
|
||||
int x, y, y1;
|
||||
|
||||
if (s != active_console)
|
||||
return;
|
||||
|
||||
if (s->ds->have_text) {
|
||||
if (!ds_get_bits_per_pixel(s->ds)) {
|
||||
s->text_x[0] = 0;
|
||||
s->text_y[0] = 0;
|
||||
s->text_x[1] = s->width - 1;
|
||||
s->text_y[1] = s->height - 1;
|
||||
s->cursor_invalidate = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->ds->have_gfx) {
|
||||
vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
|
||||
color_table[0][COLOR_BLACK]);
|
||||
y1 = s->y_displayed;
|
||||
for (y = 0; y < s->height; y++) {
|
||||
c = s->cells + y1 * s->width;
|
||||
for (x = 0; x < s->width; x++) {
|
||||
vga_putcharxy(s->ds, x, y, c->ch,
|
||||
&(c->t_attrib));
|
||||
c++;
|
||||
}
|
||||
if (++y1 == s->total_height) {
|
||||
y1 = 0;
|
||||
}
|
||||
vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
|
||||
color_table[0][COLOR_BLACK]);
|
||||
y1 = s->y_displayed;
|
||||
for(y = 0; y < s->height; y++) {
|
||||
c = s->cells + y1 * s->width;
|
||||
for(x = 0; x < s->width; x++) {
|
||||
vga_putcharxy(s->ds, x, y, c->ch,
|
||||
&(c->t_attrib));
|
||||
c++;
|
||||
}
|
||||
console_show_cursor(s, 1);
|
||||
dpy_gfx_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
|
||||
if (++y1 == s->total_height)
|
||||
y1 = 0;
|
||||
}
|
||||
console_show_cursor(s, 1);
|
||||
dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
|
||||
}
|
||||
|
||||
static void console_scroll(int ydelta)
|
||||
{
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
int i, y1;
|
||||
|
||||
s = active_console;
|
||||
@@ -701,7 +694,7 @@ static void console_scroll(int ydelta)
|
||||
console_refresh(s);
|
||||
}
|
||||
|
||||
static void console_put_lf(QemuConsole *s)
|
||||
static void console_put_lf(TextConsole *s)
|
||||
{
|
||||
TextCell *c;
|
||||
int x, y1;
|
||||
@@ -752,7 +745,7 @@ static void console_put_lf(QemuConsole *s)
|
||||
* NOTE: I know this code is not very efficient (checking every color for it
|
||||
* self) but it is more readable and better maintainable.
|
||||
*/
|
||||
static void console_handle_escape(QemuConsole *s)
|
||||
static void console_handle_escape(TextConsole *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -845,7 +838,7 @@ static void console_handle_escape(QemuConsole *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void console_clear_xy(QemuConsole *s, int x, int y)
|
||||
static void console_clear_xy(TextConsole *s, int x, int y)
|
||||
{
|
||||
int y1 = (s->y_base + y) % s->total_height;
|
||||
TextCell *c = &s->cells[y1 * s->width + x];
|
||||
@@ -855,7 +848,7 @@ static void console_clear_xy(QemuConsole *s, int x, int y)
|
||||
}
|
||||
|
||||
/* set cursor, checking bounds */
|
||||
static void set_cursor(QemuConsole *s, int x, int y)
|
||||
static void set_cursor(TextConsole *s, int x, int y)
|
||||
{
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
@@ -874,7 +867,7 @@ static void set_cursor(QemuConsole *s, int x, int y)
|
||||
s->y = y;
|
||||
}
|
||||
|
||||
static void console_putchar(QemuConsole *s, int ch)
|
||||
static void console_putchar(TextConsole *s, int ch)
|
||||
{
|
||||
TextCell *c;
|
||||
int y1, i;
|
||||
@@ -941,11 +934,8 @@ static void console_putchar(QemuConsole *s, int ch)
|
||||
case TTY_STATE_CSI: /* handle escape sequence parameters */
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (s->nb_esc_params < MAX_ESC_PARAMS) {
|
||||
int *param = &s->esc_params[s->nb_esc_params];
|
||||
int digit = (ch - '0');
|
||||
|
||||
*param = (*param <= (INT_MAX - digit) / 10) ?
|
||||
*param * 10 + digit : INT_MAX;
|
||||
s->esc_params[s->nb_esc_params] =
|
||||
s->esc_params[s->nb_esc_params] * 10 + ch - '0';
|
||||
}
|
||||
} else {
|
||||
if (s->nb_esc_params < MAX_ESC_PARAMS)
|
||||
@@ -1081,7 +1071,7 @@ static void console_putchar(QemuConsole *s, int ch)
|
||||
|
||||
void console_select(unsigned int index)
|
||||
{
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
|
||||
if (index >= MAX_CONSOLES)
|
||||
return;
|
||||
@@ -1092,29 +1082,21 @@ void console_select(unsigned int index)
|
||||
s = consoles[index];
|
||||
if (s) {
|
||||
DisplayState *ds = s->ds;
|
||||
|
||||
if (active_console && active_console->cursor_timer) {
|
||||
qemu_del_timer(active_console->cursor_timer);
|
||||
}
|
||||
active_console = s;
|
||||
if (ds->have_gfx) {
|
||||
if (ds_get_bits_per_pixel(s->ds)) {
|
||||
ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
|
||||
dpy_gfx_resize(ds);
|
||||
}
|
||||
if (ds->have_text) {
|
||||
dpy_text_resize(ds, s->width, s->height);
|
||||
}
|
||||
if (s->cursor_timer) {
|
||||
qemu_mod_timer(s->cursor_timer,
|
||||
qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
|
||||
} else {
|
||||
s->ds->surface->width = s->width;
|
||||
s->ds->surface->height = s->height;
|
||||
}
|
||||
dpy_resize(s->ds);
|
||||
vga_hw_invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
QemuConsole *s = chr->opaque;
|
||||
TextConsole *s = chr->opaque;
|
||||
int i;
|
||||
|
||||
s->update_x0 = s->width * FONT_WIDTH;
|
||||
@@ -1126,17 +1108,17 @@ static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
console_putchar(s, buf[i]);
|
||||
}
|
||||
console_show_cursor(s, 1);
|
||||
if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
|
||||
dpy_gfx_update(s->ds, s->update_x0, s->update_y0,
|
||||
s->update_x1 - s->update_x0,
|
||||
s->update_y1 - s->update_y0);
|
||||
if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
|
||||
dpy_update(s->ds, s->update_x0, s->update_y0,
|
||||
s->update_x1 - s->update_x0,
|
||||
s->update_y1 - s->update_y0);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void kbd_send_chars(void *opaque)
|
||||
{
|
||||
QemuConsole *s = opaque;
|
||||
TextConsole *s = opaque;
|
||||
int len;
|
||||
uint8_t buf[16];
|
||||
|
||||
@@ -1159,7 +1141,7 @@ static void kbd_send_chars(void *opaque)
|
||||
/* called when an ascii key is pressed */
|
||||
void kbd_put_keysym(int keysym)
|
||||
{
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
uint8_t buf[16], *q;
|
||||
int c;
|
||||
|
||||
@@ -1214,7 +1196,7 @@ void kbd_put_keysym(int keysym)
|
||||
|
||||
static void text_console_invalidate(void *opaque)
|
||||
{
|
||||
QemuConsole *s = (QemuConsole *) opaque;
|
||||
TextConsole *s = (TextConsole *) opaque;
|
||||
if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
|
||||
s->g_width = ds_get_width(s->ds);
|
||||
s->g_height = ds_get_height(s->ds);
|
||||
@@ -1225,7 +1207,7 @@ static void text_console_invalidate(void *opaque)
|
||||
|
||||
static void text_console_update(void *opaque, console_ch_t *chardata)
|
||||
{
|
||||
QemuConsole *s = (QemuConsole *) opaque;
|
||||
TextConsole *s = (TextConsole *) opaque;
|
||||
int i, j, src;
|
||||
|
||||
if (s->text_x[0] <= s->text_x[1]) {
|
||||
@@ -1237,23 +1219,23 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
|
||||
(s->cells[src].t_attrib.fgcol << 12) |
|
||||
(s->cells[src].t_attrib.bgcol << 8) |
|
||||
(s->cells[src].t_attrib.bold << 21));
|
||||
dpy_text_update(s->ds, s->text_x[0], s->text_y[0],
|
||||
s->text_x[1] - s->text_x[0], i - s->text_y[0]);
|
||||
dpy_update(s->ds, s->text_x[0], s->text_y[0],
|
||||
s->text_x[1] - s->text_x[0], i - s->text_y[0]);
|
||||
s->text_x[0] = s->width;
|
||||
s->text_y[0] = s->height;
|
||||
s->text_x[1] = 0;
|
||||
s->text_y[1] = 0;
|
||||
}
|
||||
if (s->cursor_invalidate) {
|
||||
dpy_text_cursor(s->ds, s->x, s->y);
|
||||
dpy_cursor(s->ds, s->x, s->y);
|
||||
s->cursor_invalidate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static QemuConsole *get_graphic_console(DisplayState *ds)
|
||||
static TextConsole *get_graphic_console(DisplayState *ds)
|
||||
{
|
||||
int i;
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
for (i = 0; i < nb_consoles; i++) {
|
||||
s = consoles[i];
|
||||
if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
|
||||
@@ -1262,14 +1244,14 @@ static QemuConsole *get_graphic_console(DisplayState *ds)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
|
||||
static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
|
||||
{
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
int i;
|
||||
|
||||
if (nb_consoles >= MAX_CONSOLES)
|
||||
return NULL;
|
||||
s = g_malloc0(sizeof(QemuConsole));
|
||||
s = g_malloc0(sizeof(TextConsole));
|
||||
if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
|
||||
(console_type == GRAPHIC_CONSOLE))) {
|
||||
active_console = s;
|
||||
@@ -1294,31 +1276,9 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
|
||||
return s;
|
||||
}
|
||||
|
||||
static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
|
||||
int linesize, PixelFormat pf, int newflags)
|
||||
static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
|
||||
{
|
||||
surface->pf = pf;
|
||||
|
||||
qemu_pixman_image_unref(surface->image);
|
||||
surface->image = NULL;
|
||||
|
||||
surface->format = qemu_pixman_get_format(&pf);
|
||||
assert(surface->format != 0);
|
||||
surface->image = pixman_image_create_bits(surface->format,
|
||||
width, height,
|
||||
NULL, linesize);
|
||||
assert(surface->image != NULL);
|
||||
|
||||
surface->flags = newflags | QEMU_ALLOCATED_FLAG;
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
surface->flags |= QEMU_BIG_ENDIAN_FLAG;
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplaySurface *qemu_create_displaysurface(DisplayState *ds,
|
||||
int width, int height)
|
||||
{
|
||||
DisplaySurface *surface = g_new0(DisplaySurface, 1);
|
||||
DisplaySurface *surface = (DisplaySurface*) g_malloc0(sizeof(DisplaySurface));
|
||||
|
||||
int linesize = width * 4;
|
||||
qemu_alloc_display(surface, width, height, linesize,
|
||||
@@ -1326,54 +1286,75 @@ DisplaySurface *qemu_create_displaysurface(DisplayState *ds,
|
||||
return surface;
|
||||
}
|
||||
|
||||
DisplaySurface *qemu_resize_displaysurface(DisplayState *ds,
|
||||
int width, int height)
|
||||
static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
|
||||
int width, int height)
|
||||
{
|
||||
int linesize = width * 4;
|
||||
|
||||
trace_displaysurface_resize(ds, ds->surface, width, height);
|
||||
qemu_alloc_display(ds->surface, width, height, linesize,
|
||||
qemu_alloc_display(surface, width, height, linesize,
|
||||
qemu_default_pixelformat(32), 0);
|
||||
return ds->surface;
|
||||
return surface;
|
||||
}
|
||||
|
||||
DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
|
||||
int linesize, uint8_t *data)
|
||||
void qemu_alloc_display(DisplaySurface *surface, int width, int height,
|
||||
int linesize, PixelFormat pf, int newflags)
|
||||
{
|
||||
DisplaySurface *surface = g_new0(DisplaySurface, 1);
|
||||
void *data;
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->linesize = linesize;
|
||||
surface->pf = pf;
|
||||
if (surface->flags & QEMU_ALLOCATED_FLAG) {
|
||||
data = g_realloc(surface->data,
|
||||
surface->linesize * surface->height);
|
||||
} else {
|
||||
data = g_malloc(surface->linesize * surface->height);
|
||||
}
|
||||
surface->data = (uint8_t *)data;
|
||||
surface->flags = newflags | QEMU_ALLOCATED_FLAG;
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
surface->flags |= QEMU_BIG_ENDIAN_FLAG;
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
|
||||
int linesize, uint8_t *data)
|
||||
{
|
||||
DisplaySurface *surface = (DisplaySurface*) g_malloc0(sizeof(DisplaySurface));
|
||||
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->linesize = linesize;
|
||||
surface->pf = qemu_default_pixelformat(bpp);
|
||||
|
||||
surface->format = qemu_pixman_get_format(&surface->pf);
|
||||
assert(surface->format != 0);
|
||||
surface->image = pixman_image_create_bits(surface->format,
|
||||
width, height,
|
||||
(void *)data, linesize);
|
||||
assert(surface->image != NULL);
|
||||
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
surface->flags = QEMU_BIG_ENDIAN_FLAG;
|
||||
#endif
|
||||
surface->data = data;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void qemu_free_displaysurface(DisplayState *ds)
|
||||
static void defaultallocator_free_displaysurface(DisplaySurface *surface)
|
||||
{
|
||||
trace_displaysurface_free(ds, ds->surface);
|
||||
if (ds->surface == NULL) {
|
||||
if (surface == NULL)
|
||||
return;
|
||||
}
|
||||
qemu_pixman_image_unref(ds->surface->image);
|
||||
g_free(ds->surface);
|
||||
if (surface->flags & QEMU_ALLOCATED_FLAG)
|
||||
g_free(surface->data);
|
||||
g_free(surface);
|
||||
}
|
||||
|
||||
static struct DisplayAllocator default_allocator = {
|
||||
defaultallocator_create_displaysurface,
|
||||
defaultallocator_resize_displaysurface,
|
||||
defaultallocator_free_displaysurface
|
||||
};
|
||||
|
||||
static void dumb_display_init(void)
|
||||
{
|
||||
DisplayState *ds = g_malloc0(sizeof(DisplayState));
|
||||
int width = 640;
|
||||
int height = 480;
|
||||
|
||||
ds->allocator = &default_allocator;
|
||||
if (is_fixedsize_console()) {
|
||||
width = active_console->g_width;
|
||||
height = active_console->g_height;
|
||||
@@ -1403,16 +1384,29 @@ DisplayState *get_displaystate(void)
|
||||
return display_state;
|
||||
}
|
||||
|
||||
DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
|
||||
{
|
||||
if(ds->allocator == &default_allocator) {
|
||||
DisplaySurface *surf;
|
||||
surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
|
||||
defaultallocator_free_displaysurface(ds->surface);
|
||||
ds->surface = surf;
|
||||
ds->allocator = da;
|
||||
}
|
||||
return ds->allocator;
|
||||
}
|
||||
|
||||
DisplayState *graphic_console_init(vga_hw_update_ptr update,
|
||||
vga_hw_invalidate_ptr invalidate,
|
||||
vga_hw_screen_dump_ptr screen_dump,
|
||||
vga_hw_text_update_ptr text_update,
|
||||
void *opaque)
|
||||
{
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
DisplayState *ds;
|
||||
|
||||
ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
|
||||
ds->allocator = &default_allocator;
|
||||
ds->surface = qemu_create_displaysurface(ds, 640, 480);
|
||||
|
||||
s = new_console(ds, GRAPHIC_CONSOLE);
|
||||
@@ -1454,24 +1448,14 @@ void console_color_init(DisplayState *ds)
|
||||
|
||||
static void text_console_set_echo(CharDriverState *chr, bool echo)
|
||||
{
|
||||
QemuConsole *s = chr->opaque;
|
||||
TextConsole *s = chr->opaque;
|
||||
|
||||
s->echo = echo;
|
||||
}
|
||||
|
||||
static void text_console_update_cursor(void *opaque)
|
||||
{
|
||||
QemuConsole *s = opaque;
|
||||
|
||||
s->cursor_visible_phase = !s->cursor_visible_phase;
|
||||
vga_hw_invalidate();
|
||||
qemu_mod_timer(s->cursor_timer,
|
||||
qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
|
||||
}
|
||||
|
||||
static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
|
||||
{
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
static int color_inited;
|
||||
|
||||
s = chr->opaque;
|
||||
@@ -1497,9 +1481,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
|
||||
s->g_height = ds_get_height(s->ds);
|
||||
}
|
||||
|
||||
s->cursor_timer =
|
||||
qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
|
||||
|
||||
s->hw_invalidate = text_console_invalidate;
|
||||
s->hw_text_update = text_console_update;
|
||||
s->hw = s;
|
||||
@@ -1534,7 +1515,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
|
||||
CharDriverState *text_console_init(QemuOpts *opts)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
QemuConsole *s;
|
||||
TextConsole *s;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
@@ -1580,14 +1561,14 @@ void text_consoles_set_display(DisplayState *ds)
|
||||
|
||||
void qemu_console_resize(DisplayState *ds, int width, int height)
|
||||
{
|
||||
QemuConsole *s = get_graphic_console(ds);
|
||||
TextConsole *s = get_graphic_console(ds);
|
||||
if (!s) return;
|
||||
|
||||
s->g_width = width;
|
||||
s->g_height = height;
|
||||
if (is_graphic_console()) {
|
||||
ds->surface = qemu_resize_displaysurface(ds, width, height);
|
||||
dpy_gfx_resize(ds);
|
||||
dpy_resize(ds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1595,7 +1576,7 @@ void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
|
||||
int dst_x, int dst_y, int w, int h)
|
||||
{
|
||||
if (is_graphic_console()) {
|
||||
dpy_gfx_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
|
||||
dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1606,7 +1587,7 @@ PixelFormat qemu_different_endianness_pixelformat(int bpp)
|
||||
memset(&pf, 0x00, sizeof(PixelFormat));
|
||||
|
||||
pf.bits_per_pixel = bpp;
|
||||
pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
|
||||
pf.bytes_per_pixel = bpp / 8;
|
||||
pf.depth = bpp == 32 ? 24 : bpp;
|
||||
|
||||
switch (bpp) {
|
||||
@@ -1655,12 +1636,13 @@ PixelFormat qemu_default_pixelformat(int bpp)
|
||||
memset(&pf, 0x00, sizeof(PixelFormat));
|
||||
|
||||
pf.bits_per_pixel = bpp;
|
||||
pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
|
||||
pf.bytes_per_pixel = bpp / 8;
|
||||
pf.depth = bpp == 32 ? 24 : bpp;
|
||||
|
||||
switch (bpp) {
|
||||
case 15:
|
||||
pf.bits_per_pixel = 16;
|
||||
pf.bytes_per_pixel = 2;
|
||||
pf.rmask = 0x00007c00;
|
||||
pf.gmask = 0x000003E0;
|
||||
pf.bmask = 0x0000001F;
|
||||
@@ -1706,15 +1688,18 @@ PixelFormat qemu_default_pixelformat(int bpp)
|
||||
pf.rmask = 0x00FF0000;
|
||||
pf.gmask = 0x0000FF00;
|
||||
pf.bmask = 0x000000FF;
|
||||
pf.amax = 255;
|
||||
pf.rmax = 255;
|
||||
pf.gmax = 255;
|
||||
pf.bmax = 255;
|
||||
pf.ashift = 24;
|
||||
pf.rshift = 16;
|
||||
pf.gshift = 8;
|
||||
pf.bshift = 0;
|
||||
pf.rbits = 8;
|
||||
pf.gbits = 8;
|
||||
pf.bbits = 8;
|
||||
pf.abits = 8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
#define CONSOLE_H
|
||||
|
||||
#include "qemu-char.h"
|
||||
#include "qemu-pixman.h"
|
||||
#include "qdict.h"
|
||||
#include "notify.h"
|
||||
#include "monitor.h"
|
||||
#include "trace.h"
|
||||
#include "qapi-types.h"
|
||||
#include "error.h"
|
||||
|
||||
/* keyboard/mouse support */
|
||||
|
||||
@@ -108,6 +105,7 @@ void kbd_put_keysym(int keysym);
|
||||
|
||||
#define QEMU_BIG_ENDIAN_FLAG 0x01
|
||||
#define QEMU_ALLOCATED_FLAG 0x02
|
||||
#define QEMU_REALPIXELS_FLAG 0x04
|
||||
|
||||
struct PixelFormat {
|
||||
uint8_t bits_per_pixel;
|
||||
@@ -120,9 +118,11 @@ struct PixelFormat {
|
||||
};
|
||||
|
||||
struct DisplaySurface {
|
||||
pixman_format_code_t format;
|
||||
pixman_image_t *image;
|
||||
uint8_t flags;
|
||||
int width;
|
||||
int height;
|
||||
int linesize; /* bytes per line */
|
||||
uint8_t *data;
|
||||
|
||||
struct PixelFormat pf;
|
||||
};
|
||||
@@ -152,32 +152,35 @@ struct DisplayChangeListener {
|
||||
int idle;
|
||||
uint64_t gui_timer_interval;
|
||||
|
||||
void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h);
|
||||
void (*dpy_resize)(struct DisplayState *s);
|
||||
void (*dpy_setdata)(struct DisplayState *s);
|
||||
void (*dpy_refresh)(struct DisplayState *s);
|
||||
|
||||
void (*dpy_gfx_update)(struct DisplayState *s, int x, int y, int w, int h);
|
||||
void (*dpy_gfx_resize)(struct DisplayState *s);
|
||||
void (*dpy_gfx_setdata)(struct DisplayState *s);
|
||||
void (*dpy_gfx_copy)(struct DisplayState *s, int src_x, int src_y,
|
||||
int dst_x, int dst_y, int w, int h);
|
||||
|
||||
void (*dpy_copy)(struct DisplayState *s, int src_x, int src_y,
|
||||
int dst_x, int dst_y, int w, int h);
|
||||
void (*dpy_fill)(struct DisplayState *s, int x, int y,
|
||||
int w, int h, uint32_t c);
|
||||
void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
|
||||
void (*dpy_text_resize)(struct DisplayState *s, int w, int h);
|
||||
void (*dpy_text_update)(struct DisplayState *s, int x, int y, int w, int h);
|
||||
|
||||
void (*dpy_mouse_set)(struct DisplayState *s, int x, int y, int on);
|
||||
void (*dpy_cursor_define)(struct DisplayState *s, QEMUCursor *cursor);
|
||||
struct DisplayChangeListener *next;
|
||||
};
|
||||
|
||||
QLIST_ENTRY(DisplayChangeListener) next;
|
||||
struct DisplayAllocator {
|
||||
DisplaySurface* (*create_displaysurface)(int width, int height);
|
||||
DisplaySurface* (*resize_displaysurface)(DisplaySurface *surface, int width, int height);
|
||||
void (*free_displaysurface)(DisplaySurface *surface);
|
||||
};
|
||||
|
||||
struct DisplayState {
|
||||
struct DisplaySurface *surface;
|
||||
void *opaque;
|
||||
struct QEMUTimer *gui_timer;
|
||||
bool have_gfx;
|
||||
bool have_text;
|
||||
|
||||
QLIST_HEAD(, DisplayChangeListener) listeners;
|
||||
struct DisplayAllocator* allocator;
|
||||
struct DisplayChangeListener* listeners;
|
||||
|
||||
void (*mouse_set)(int x, int y, int on);
|
||||
void (*cursor_define)(QEMUCursor *cursor);
|
||||
|
||||
struct DisplayState *next;
|
||||
};
|
||||
@@ -186,14 +189,29 @@ void register_displaystate(DisplayState *ds);
|
||||
DisplayState *get_displaystate(void);
|
||||
DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
|
||||
int linesize, uint8_t *data);
|
||||
void qemu_alloc_display(DisplaySurface *surface, int width, int height,
|
||||
int linesize, PixelFormat pf, int newflags);
|
||||
PixelFormat qemu_different_endianness_pixelformat(int bpp);
|
||||
PixelFormat qemu_default_pixelformat(int bpp);
|
||||
|
||||
DisplaySurface *qemu_create_displaysurface(DisplayState *ds,
|
||||
int width, int height);
|
||||
DisplaySurface *qemu_resize_displaysurface(DisplayState *ds,
|
||||
int width, int height);
|
||||
void qemu_free_displaysurface(DisplayState *ds);
|
||||
DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da);
|
||||
|
||||
static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int width, int height)
|
||||
{
|
||||
return ds->allocator->create_displaysurface(width, height);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static inline int is_surface_bgr(DisplaySurface *surface)
|
||||
{
|
||||
@@ -205,201 +223,109 @@ static inline int is_surface_bgr(DisplaySurface *surface)
|
||||
|
||||
static inline int is_buffer_shared(DisplaySurface *surface)
|
||||
{
|
||||
return !(surface->flags & QEMU_ALLOCATED_FLAG);
|
||||
return (!(surface->flags & QEMU_ALLOCATED_FLAG) &&
|
||||
!(surface->flags & QEMU_REALPIXELS_FLAG));
|
||||
}
|
||||
|
||||
void gui_setup_refresh(DisplayState *ds);
|
||||
|
||||
static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
|
||||
{
|
||||
QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
|
||||
gui_setup_refresh(ds);
|
||||
if (dcl->dpy_gfx_resize) {
|
||||
dcl->dpy_gfx_resize(ds);
|
||||
dcl->next = ds->listeners;
|
||||
ds->listeners = dcl;
|
||||
}
|
||||
|
||||
static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
|
||||
{
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
dcl->dpy_update(s, x, y, w, h);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void unregister_displaychangelistener(DisplayState *ds,
|
||||
DisplayChangeListener *dcl)
|
||||
static inline void dpy_resize(DisplayState *s)
|
||||
{
|
||||
QLIST_REMOVE(dcl, next);
|
||||
gui_setup_refresh(ds);
|
||||
}
|
||||
|
||||
static inline void dpy_gfx_update(DisplayState *s, int x, int y, int w, int h)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_gfx_update) {
|
||||
dcl->dpy_gfx_update(s, x, y, w, h);
|
||||
}
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
dcl->dpy_resize(s);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_gfx_resize(DisplayState *s)
|
||||
static inline void dpy_setdata(DisplayState *s)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_gfx_resize) {
|
||||
dcl->dpy_gfx_resize(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_gfx_setdata(DisplayState *s)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_gfx_setdata) {
|
||||
dcl->dpy_gfx_setdata(s);
|
||||
}
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
if (dcl->dpy_setdata) dcl->dpy_setdata(s);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_refresh(DisplayState *s)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_refresh) {
|
||||
dcl->dpy_refresh(s);
|
||||
}
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
if (dcl->dpy_refresh) dcl->dpy_refresh(s);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_gfx_copy(struct DisplayState *s, int src_x, int src_y,
|
||||
int dst_x, int dst_y, int w, int h)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_gfx_copy) {
|
||||
dcl->dpy_gfx_copy(s, src_x, src_y, dst_x, dst_y, w, h);
|
||||
} else { /* TODO */
|
||||
dcl->dpy_gfx_update(s, dst_x, dst_y, w, h);
|
||||
}
|
||||
static inline void dpy_copy(struct DisplayState *s, int src_x, int src_y,
|
||||
int dst_x, int dst_y, int w, int h) {
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
if (dcl->dpy_copy)
|
||||
dcl->dpy_copy(s, src_x, src_y, dst_x, dst_y, w, h);
|
||||
else /* TODO */
|
||||
dcl->dpy_update(s, dst_x, dst_y, w, h);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_text_cursor(struct DisplayState *s, int x, int y)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_text_cursor) {
|
||||
dcl->dpy_text_cursor(s, x, y);
|
||||
}
|
||||
static inline void dpy_fill(struct DisplayState *s, int x, int y,
|
||||
int w, int h, uint32_t c) {
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
if (dcl->dpy_fill) dcl->dpy_fill(s, x, y, w, h, c);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_text_update(DisplayState *s, int x, int y, int w, int h)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_text_update) {
|
||||
dcl->dpy_text_update(s, x, y, w, h);
|
||||
}
|
||||
static inline void dpy_cursor(struct DisplayState *s, int x, int y) {
|
||||
struct DisplayChangeListener *dcl = s->listeners;
|
||||
while (dcl != NULL) {
|
||||
if (dcl->dpy_text_cursor) dcl->dpy_text_cursor(s, x, y);
|
||||
dcl = dcl->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_text_resize(DisplayState *s, int w, int h)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_text_resize) {
|
||||
dcl->dpy_text_resize(s, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_mouse_set(struct DisplayState *s, int x, int y, int on)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_mouse_set) {
|
||||
dcl->dpy_mouse_set(s, x, y, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dpy_cursor_define(struct DisplayState *s, QEMUCursor *cursor)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_cursor_define) {
|
||||
dcl->dpy_cursor_define(s, cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool dpy_cursor_define_supported(struct DisplayState *s)
|
||||
{
|
||||
struct DisplayChangeListener *dcl;
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
if (dcl->dpy_cursor_define) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int ds_get_linesize(DisplayState *ds)
|
||||
{
|
||||
return pixman_image_get_stride(ds->surface->image);
|
||||
return ds->surface->linesize;
|
||||
}
|
||||
|
||||
static inline uint8_t* ds_get_data(DisplayState *ds)
|
||||
{
|
||||
return (void *)pixman_image_get_data(ds->surface->image);
|
||||
return ds->surface->data;
|
||||
}
|
||||
|
||||
static inline int ds_get_width(DisplayState *ds)
|
||||
{
|
||||
return pixman_image_get_width(ds->surface->image);
|
||||
return ds->surface->width;
|
||||
}
|
||||
|
||||
static inline int ds_get_height(DisplayState *ds)
|
||||
{
|
||||
return pixman_image_get_height(ds->surface->image);
|
||||
return ds->surface->height;
|
||||
}
|
||||
|
||||
static inline int ds_get_bits_per_pixel(DisplayState *ds)
|
||||
{
|
||||
int bits = PIXMAN_FORMAT_BPP(ds->surface->format);
|
||||
return bits;
|
||||
return ds->surface->pf.bits_per_pixel;
|
||||
}
|
||||
|
||||
static inline int ds_get_bytes_per_pixel(DisplayState *ds)
|
||||
{
|
||||
int bits = PIXMAN_FORMAT_BPP(ds->surface->format);
|
||||
return (bits + 7) / 8;
|
||||
}
|
||||
|
||||
static inline pixman_format_code_t ds_get_format(DisplayState *ds)
|
||||
{
|
||||
return ds->surface->format;
|
||||
}
|
||||
|
||||
static inline pixman_image_t *ds_get_image(DisplayState *ds)
|
||||
{
|
||||
return ds->surface->image;
|
||||
}
|
||||
|
||||
static inline int ds_get_depth(DisplayState *ds)
|
||||
{
|
||||
return ds->surface->pf.depth;
|
||||
}
|
||||
|
||||
static inline int ds_get_rmask(DisplayState *ds)
|
||||
{
|
||||
return ds->surface->pf.rmask;
|
||||
}
|
||||
|
||||
static inline int ds_get_gmask(DisplayState *ds)
|
||||
{
|
||||
return ds->surface->pf.gmask;
|
||||
}
|
||||
|
||||
static inline int ds_get_bmask(DisplayState *ds)
|
||||
{
|
||||
return ds->surface->pf.bmask;
|
||||
return ds->surface->pf.bytes_per_pixel;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CURSES
|
||||
@@ -417,8 +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,
|
||||
Error **errp);
|
||||
typedef void (*vga_hw_screen_dump_ptr)(void *, const char *, bool cswitch);
|
||||
typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *);
|
||||
|
||||
DisplayState *graphic_console_init(vga_hw_update_ptr update,
|
||||
@@ -429,6 +354,7 @@ DisplayState *graphic_console_init(vga_hw_update_ptr update,
|
||||
|
||||
void vga_hw_update(void);
|
||||
void vga_hw_invalidate(void);
|
||||
void vga_hw_screen_dump(const char *filename);
|
||||
void vga_hw_text_update(console_ch_t *chardata);
|
||||
|
||||
int is_graphic_console(void);
|
||||
@@ -449,8 +375,10 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
|
||||
|
||||
/* vnc.c */
|
||||
void vnc_display_init(DisplayState *ds);
|
||||
void vnc_display_open(DisplayState *ds, const char *display, Error **errp);
|
||||
void vnc_display_close(DisplayState *ds);
|
||||
int vnc_display_open(DisplayState *ds, const char *display);
|
||||
void vnc_display_add_client(DisplayState *ds, int csock, int skipauth);
|
||||
int vnc_display_disable_login(DisplayState *ds);
|
||||
char *vnc_display_local_addr(DisplayState *ds);
|
||||
#ifdef CONFIG_VNC
|
||||
int vnc_display_password(DisplayState *ds, const char *password);
|
||||
@@ -469,8 +397,4 @@ static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires)
|
||||
/* curses.c */
|
||||
void curses_display_init(DisplayState *ds, int full_screen);
|
||||
|
||||
/* input.c */
|
||||
int index_from_key(const char *key);
|
||||
int index_from_keycode(int code);
|
||||
|
||||
#endif
|
||||
@@ -171,8 +171,8 @@ static Coroutine *coroutine_new(void)
|
||||
CoroutineThreadState *coTS;
|
||||
struct sigaction sa;
|
||||
struct sigaction osa;
|
||||
stack_t ss;
|
||||
stack_t oss;
|
||||
struct sigaltstack ss;
|
||||
struct sigaltstack oss;
|
||||
sigset_t sigs;
|
||||
sigset_t osigs;
|
||||
jmp_buf old_env;
|
||||
|
||||
@@ -30,10 +30,6 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-coroutine-int.h"
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
enum {
|
||||
/* Maximum free pool size prevents holding too many freed coroutines */
|
||||
POOL_MAX_SIZE = 64,
|
||||
@@ -47,11 +43,6 @@ typedef struct {
|
||||
Coroutine base;
|
||||
void *stack;
|
||||
jmp_buf env;
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
unsigned int valgrind_stack_id;
|
||||
#endif
|
||||
|
||||
} CoroutineUContext;
|
||||
|
||||
/**
|
||||
@@ -168,11 +159,6 @@ static Coroutine *coroutine_new(void)
|
||||
uc.uc_stack.ss_size = stack_size;
|
||||
uc.uc_stack.ss_flags = 0;
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
co->valgrind_stack_id =
|
||||
VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size);
|
||||
#endif
|
||||
|
||||
arg.p = co;
|
||||
|
||||
makecontext(&uc, (void (*)(void))coroutine_trampoline,
|
||||
@@ -199,20 +185,6 @@ Coroutine *qemu_coroutine_new(void)
|
||||
return co;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET
|
||||
/* Work around an unused variable in the valgrind.h macro... */
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#endif
|
||||
static inline void valgrind_stack_deregister(CoroutineUContext *co)
|
||||
{
|
||||
VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
|
||||
}
|
||||
#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET
|
||||
#pragma GCC diagnostic error "-Wunused-but-set-variable"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void qemu_coroutine_delete(Coroutine *co_)
|
||||
{
|
||||
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
|
||||
@@ -224,10 +196,6 @@ void qemu_coroutine_delete(Coroutine *co_)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
valgrind_stack_deregister(co);
|
||||
#endif
|
||||
|
||||
g_free(co->stack);
|
||||
g_free(co);
|
||||
}
|
||||
|
||||
@@ -260,34 +260,21 @@ 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)
|
||||
|
||||
#define cpu_ldub_data(env, addr) ldub_raw(addr)
|
||||
#define cpu_lduw_data(env, addr) lduw_raw(addr)
|
||||
#define cpu_ldsw_data(env, addr) ldsw_raw(addr)
|
||||
#define cpu_ldl_data(env, addr) ldl_raw(addr)
|
||||
#define cpu_ldq_data(env, addr) ldq_raw(addr)
|
||||
|
||||
#define cpu_stb_data(env, addr, data) stb_raw(addr, data)
|
||||
#define cpu_stw_data(env, addr, data) stw_raw(addr, data)
|
||||
#define cpu_stl_data(env, addr, data) stl_raw(addr, data)
|
||||
#define cpu_stq_data(env, addr, data) stq_raw(addr, data)
|
||||
|
||||
#define cpu_ldub_kernel(env, addr) ldub_raw(addr)
|
||||
#define cpu_lduw_kernel(env, addr) lduw_raw(addr)
|
||||
#define cpu_ldsw_kernel(env, addr) ldsw_raw(addr)
|
||||
#define cpu_ldl_kernel(env, addr) ldl_raw(addr)
|
||||
#define cpu_ldq_kernel(env, addr) ldq_raw(addr)
|
||||
|
||||
#define cpu_stb_kernel(env, addr, data) stb_raw(addr, data)
|
||||
#define cpu_stw_kernel(env, addr, data) stw_raw(addr, data)
|
||||
#define cpu_stl_kernel(env, addr, data) stl_raw(addr, data)
|
||||
#define cpu_stq_kernel(env, addr, data) stq_raw(addr, data)
|
||||
#endif
|
||||
|
||||
#define ldub_kernel(p) ldub_raw(p)
|
||||
#define ldsb_kernel(p) ldsb_raw(p)
|
||||
@@ -304,13 +291,6 @@ extern unsigned long reserved_va;
|
||||
#define stfl_kernel(p, v) stfl_raw(p, v)
|
||||
#define stfq_kernel(p, vt) stfq_raw(p, v)
|
||||
|
||||
#define cpu_ldub_data(env, addr) ldub_raw(addr)
|
||||
#define cpu_lduw_data(env, addr) lduw_raw(addr)
|
||||
#define cpu_ldl_data(env, addr) ldl_raw(addr)
|
||||
|
||||
#define cpu_stb_data(env, addr, data) stb_raw(addr, data)
|
||||
#define cpu_stw_data(env, addr, data) stw_raw(addr, data)
|
||||
#define cpu_stl_data(env, addr, data) stl_raw(addr, data)
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
|
||||
/* page related stuff */
|
||||
@@ -356,9 +336,6 @@ CPUArchState *cpu_copy(CPUArchState *env);
|
||||
CPUArchState *qemu_get_cpu(int cpu);
|
||||
|
||||
#define CPU_DUMP_CODE 0x00010000
|
||||
#define CPU_DUMP_FPU 0x00020000 /* dump FPU register state, not just integer */
|
||||
/* dump info about TCG QEMU's condition code optimization state */
|
||||
#define CPU_DUMP_CCOP 0x00040000
|
||||
|
||||
void cpu_dump_state(CPUArchState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
int flags);
|
||||
@@ -438,6 +415,8 @@ void cpu_reset_interrupt(CPUArchState *env, int mask);
|
||||
|
||||
void cpu_exit(CPUArchState *s);
|
||||
|
||||
bool qemu_cpu_has_work(CPUArchState *env);
|
||||
|
||||
/* Breakpoint/watchpoint flags */
|
||||
#define BP_MEM_READ 0x01
|
||||
#define BP_MEM_WRITE 0x02
|
||||
@@ -464,13 +443,40 @@ void cpu_watchpoint_remove_all(CPUArchState *env, int mask);
|
||||
#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);
|
||||
|
||||
#define CPU_LOG_TB_OUT_ASM (1 << 0)
|
||||
#define CPU_LOG_TB_IN_ASM (1 << 1)
|
||||
#define CPU_LOG_TB_OP (1 << 2)
|
||||
#define CPU_LOG_TB_OP_OPT (1 << 3)
|
||||
#define CPU_LOG_INT (1 << 4)
|
||||
#define CPU_LOG_EXEC (1 << 5)
|
||||
#define CPU_LOG_PCALL (1 << 6)
|
||||
#define CPU_LOG_IOPORT (1 << 7)
|
||||
#define CPU_LOG_TB_CPU (1 << 8)
|
||||
#define CPU_LOG_RESET (1 << 9)
|
||||
|
||||
/* define log items */
|
||||
typedef struct CPULogItem {
|
||||
int mask;
|
||||
const char *name;
|
||||
const char *help;
|
||||
} CPULogItem;
|
||||
|
||||
extern const CPULogItem cpu_log_items[];
|
||||
|
||||
void cpu_set_log(int log_flags);
|
||||
void cpu_set_log_filename(const char *filename);
|
||||
int cpu_str_to_log_mask(const char *str);
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
/* 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. */
|
||||
hwaddr cpu_get_phys_page_debug(CPUArchState *env, target_ulong addr);
|
||||
target_phys_addr_t cpu_get_phys_page_debug(CPUArchState *env, target_ulong addr);
|
||||
|
||||
/* memory API */
|
||||
|
||||
@@ -513,7 +519,6 @@ extern int mem_prealloc;
|
||||
#define TLB_MMIO (1 << 5)
|
||||
|
||||
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
||||
ram_addr_t last_ram_offset(void);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
||||
|
||||
+41
-36
@@ -3,7 +3,9 @@
|
||||
|
||||
/* CPU interfaces that are target independent. */
|
||||
|
||||
#include "hwaddr.h"
|
||||
#ifdef TARGET_PHYS_ADDR_BITS
|
||||
#include "targphys.h"
|
||||
#endif
|
||||
|
||||
#ifndef NEED_CPU_H
|
||||
#include "poison.h"
|
||||
@@ -21,7 +23,7 @@ enum device_endian {
|
||||
};
|
||||
|
||||
/* address in the RAM (different from a physical address) */
|
||||
#if defined(CONFIG_XEN_BACKEND)
|
||||
#if defined(CONFIG_XEN_BACKEND) && TARGET_PHYS_ADDR_BITS == 64
|
||||
typedef uint64_t ram_addr_t;
|
||||
# define RAM_ADDR_MAX UINT64_MAX
|
||||
# define RAM_ADDR_FMT "%" PRIx64
|
||||
@@ -33,38 +35,41 @@ typedef uintptr_t ram_addr_t;
|
||||
|
||||
/* memory API */
|
||||
|
||||
typedef void CPUWriteMemoryFunc(void *opaque, hwaddr addr, uint32_t value);
|
||||
typedef uint32_t CPUReadMemoryFunc(void *opaque, hwaddr addr);
|
||||
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 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);
|
||||
void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size);
|
||||
/* Same but slower, to use for migration, where the order of
|
||||
* RAMBlocks must not change. */
|
||||
void *qemu_safe_ram_ptr(ram_addr_t addr);
|
||||
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);
|
||||
|
||||
void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf,
|
||||
void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
|
||||
int len, int is_write);
|
||||
static inline void cpu_physical_memory_read(hwaddr addr,
|
||||
static inline void cpu_physical_memory_read(target_phys_addr_t addr,
|
||||
void *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, buf, len, 0);
|
||||
}
|
||||
static inline void cpu_physical_memory_write(hwaddr addr,
|
||||
static inline void cpu_physical_memory_write(target_phys_addr_t addr,
|
||||
const void *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, (void *)buf, len, 1);
|
||||
}
|
||||
void *cpu_physical_memory_map(hwaddr addr,
|
||||
hwaddr *plen,
|
||||
void *cpu_physical_memory_map(target_phys_addr_t addr,
|
||||
target_phys_addr_t *plen,
|
||||
int is_write);
|
||||
void cpu_physical_memory_unmap(void *buffer, hwaddr len,
|
||||
int is_write, hwaddr access_len);
|
||||
void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
|
||||
int is_write, target_phys_addr_t access_len);
|
||||
void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
|
||||
|
||||
bool cpu_physical_memory_is_io(hwaddr phys_addr);
|
||||
void cpu_unregister_map_client(void *cookie);
|
||||
|
||||
/* Coalesced MMIO regions are areas where write operations can be reordered.
|
||||
* This usually implies that write operations are side-effect free. This allows
|
||||
@@ -73,33 +78,33 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr);
|
||||
*/
|
||||
void qemu_flush_coalesced_mmio_buffer(void);
|
||||
|
||||
uint32_t ldub_phys(hwaddr addr);
|
||||
uint32_t lduw_le_phys(hwaddr addr);
|
||||
uint32_t lduw_be_phys(hwaddr addr);
|
||||
uint32_t ldl_le_phys(hwaddr addr);
|
||||
uint32_t ldl_be_phys(hwaddr addr);
|
||||
uint64_t ldq_le_phys(hwaddr addr);
|
||||
uint64_t ldq_be_phys(hwaddr addr);
|
||||
void stb_phys(hwaddr addr, uint32_t val);
|
||||
void stw_le_phys(hwaddr addr, uint32_t val);
|
||||
void stw_be_phys(hwaddr addr, uint32_t val);
|
||||
void stl_le_phys(hwaddr addr, uint32_t val);
|
||||
void stl_be_phys(hwaddr addr, uint32_t val);
|
||||
void stq_le_phys(hwaddr addr, uint64_t val);
|
||||
void stq_be_phys(hwaddr addr, uint64_t val);
|
||||
uint32_t ldub_phys(target_phys_addr_t addr);
|
||||
uint32_t lduw_le_phys(target_phys_addr_t addr);
|
||||
uint32_t lduw_be_phys(target_phys_addr_t addr);
|
||||
uint32_t ldl_le_phys(target_phys_addr_t addr);
|
||||
uint32_t ldl_be_phys(target_phys_addr_t addr);
|
||||
uint64_t ldq_le_phys(target_phys_addr_t addr);
|
||||
uint64_t ldq_be_phys(target_phys_addr_t addr);
|
||||
void stb_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stw_le_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stw_be_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stl_le_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stl_be_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stq_le_phys(target_phys_addr_t addr, uint64_t val);
|
||||
void stq_be_phys(target_phys_addr_t addr, uint64_t val);
|
||||
|
||||
#ifdef NEED_CPU_H
|
||||
uint32_t lduw_phys(hwaddr addr);
|
||||
uint32_t ldl_phys(hwaddr addr);
|
||||
uint64_t ldq_phys(hwaddr addr);
|
||||
void stl_phys_notdirty(hwaddr addr, uint32_t val);
|
||||
void stq_phys_notdirty(hwaddr addr, uint64_t val);
|
||||
void stw_phys(hwaddr addr, uint32_t val);
|
||||
void stl_phys(hwaddr addr, uint32_t val);
|
||||
void stq_phys(hwaddr addr, uint64_t val);
|
||||
uint32_t lduw_phys(target_phys_addr_t addr);
|
||||
uint32_t ldl_phys(target_phys_addr_t addr);
|
||||
uint64_t ldq_phys(target_phys_addr_t addr);
|
||||
void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
|
||||
void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
|
||||
void stw_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stl_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stq_phys(target_phys_addr_t addr, uint64_t val);
|
||||
#endif
|
||||
|
||||
void cpu_physical_memory_write_rom(hwaddr addr,
|
||||
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
|
||||
const uint8_t *buf, int len);
|
||||
|
||||
extern struct MemoryRegion io_mem_ram;
|
||||
|
||||
+19
-2
@@ -29,7 +29,7 @@
|
||||
#include <signal.h>
|
||||
#include "osdep.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "hwaddr.h"
|
||||
#include "targphys.h"
|
||||
|
||||
#ifndef TARGET_LONG_BITS
|
||||
#error TARGET_LONG_BITS must be defined before including this header
|
||||
@@ -111,7 +111,7 @@ extern int CPUTLBEntry_wrong_size[sizeof(CPUTLBEntry) == (1 << CPU_TLB_ENTRY_BIT
|
||||
#define CPU_COMMON_TLB \
|
||||
/* The meaning of the MMU modes is defined in the target code. */ \
|
||||
CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \
|
||||
hwaddr iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \
|
||||
target_phys_addr_t iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \
|
||||
target_ulong tlb_flush_addr; \
|
||||
target_ulong tlb_flush_mask;
|
||||
|
||||
@@ -151,6 +151,14 @@ 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 */ \
|
||||
@@ -201,9 +209,18 @@ typedef struct CPUWatchpoint {
|
||||
int nr_cores; /* number of cores within this CPU package */ \
|
||||
int nr_threads;/* number of threads within this CPU */ \
|
||||
int running; /* Nonzero if cpu is currently running(usermode). */ \
|
||||
int thread_id; \
|
||||
/* user data */ \
|
||||
void *opaque; \
|
||||
\
|
||||
uint32_t created; \
|
||||
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; \
|
||||
const char *cpu_model_str; \
|
||||
struct KVMState *kvm_state; \
|
||||
struct kvm_run *kvm_run; \
|
||||
|
||||
+20
-49
@@ -27,9 +27,9 @@ int tb_invalidated_flag;
|
||||
|
||||
//#define CONFIG_DEBUG_EXEC
|
||||
|
||||
bool qemu_cpu_has_work(CPUState *cpu)
|
||||
bool qemu_cpu_has_work(CPUArchState *env)
|
||||
{
|
||||
return cpu_has_work(cpu);
|
||||
return cpu_has_work(env);
|
||||
}
|
||||
|
||||
void cpu_loop_exit(CPUArchState *env)
|
||||
@@ -156,9 +156,12 @@ static inline TranslationBlock *tb_find_fast(CPUArchState *env)
|
||||
|
||||
static CPUDebugExcpHandler *debug_excp_handler;
|
||||
|
||||
void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
|
||||
CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
|
||||
{
|
||||
CPUDebugExcpHandler *old_handler = debug_excp_handler;
|
||||
|
||||
debug_excp_handler = handler;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
static void cpu_handle_debug_exception(CPUArchState *env)
|
||||
@@ -181,14 +184,13 @@ volatile sig_atomic_t exit_request;
|
||||
|
||||
int cpu_exec(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int ret, interrupt_request;
|
||||
TranslationBlock *tb;
|
||||
uint8_t *tc_ptr;
|
||||
tcg_target_ulong next_tb;
|
||||
|
||||
if (env->halted) {
|
||||
if (!cpu_has_work(cpu)) {
|
||||
if (!cpu_has_work(env)) {
|
||||
return EXCP_HALTED;
|
||||
}
|
||||
|
||||
@@ -220,7 +222,6 @@ int cpu_exec(CPUArchState *env)
|
||||
#elif defined(TARGET_LM32)
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
#elif defined(TARGET_MIPS)
|
||||
#elif defined(TARGET_OPENRISC)
|
||||
#elif defined(TARGET_SH4)
|
||||
#elif defined(TARGET_CRIS)
|
||||
#elif defined(TARGET_S390X)
|
||||
@@ -291,18 +292,16 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
#endif
|
||||
if (interrupt_request & CPU_INTERRUPT_INIT) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT,
|
||||
0);
|
||||
do_cpu_init(x86_env_get_cpu(env));
|
||||
svm_check_intercept(env, SVM_EXIT_INIT);
|
||||
do_cpu_init(env);
|
||||
env->exception_index = EXCP_HALTED;
|
||||
cpu_loop_exit(env);
|
||||
} else if (interrupt_request & CPU_INTERRUPT_SIPI) {
|
||||
do_cpu_sipi(x86_env_get_cpu(env));
|
||||
do_cpu_sipi(env);
|
||||
} else if (env->hflags2 & HF2_GIF_MASK) {
|
||||
if ((interrupt_request & CPU_INTERRUPT_SMI) &&
|
||||
!(env->hflags & HF_SMM_MASK)) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_SMI,
|
||||
0);
|
||||
svm_check_intercept(env, SVM_EXIT_SMI);
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_SMI;
|
||||
do_smm_enter(env);
|
||||
next_tb = 0;
|
||||
@@ -323,8 +322,7 @@ int cpu_exec(CPUArchState *env)
|
||||
(env->eflags & IF_MASK &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
|
||||
int intno;
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INTR,
|
||||
0);
|
||||
svm_check_intercept(env, SVM_EXIT_INTR);
|
||||
env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ);
|
||||
intno = cpu_get_pic_interrupt(env);
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing hardware INT=0x%02x\n", intno);
|
||||
@@ -338,8 +336,7 @@ int cpu_exec(CPUArchState *env)
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK)) {
|
||||
int intno;
|
||||
/* FIXME: this should respect TPR */
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR,
|
||||
0);
|
||||
svm_check_intercept(env, SVM_EXIT_VINTR);
|
||||
intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
@@ -350,7 +347,7 @@ int cpu_exec(CPUArchState *env)
|
||||
}
|
||||
#elif defined(TARGET_PPC)
|
||||
if ((interrupt_request & CPU_INTERRUPT_RESET)) {
|
||||
cpu_reset(cpu);
|
||||
cpu_state_reset(env);
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
ppc_hw_interrupt(env);
|
||||
@@ -383,23 +380,6 @@ int cpu_exec(CPUArchState *env)
|
||||
do_interrupt(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_OPENRISC)
|
||||
{
|
||||
int idx = -1;
|
||||
if ((interrupt_request & CPU_INTERRUPT_HARD)
|
||||
&& (env->sr & SR_IEE)) {
|
||||
idx = EXCP_INT;
|
||||
}
|
||||
if ((interrupt_request & CPU_INTERRUPT_TIMER)
|
||||
&& (env->sr & SR_TEE)) {
|
||||
idx = EXCP_TICK;
|
||||
}
|
||||
if (idx >= 0) {
|
||||
env->exception_index = idx;
|
||||
do_interrupt(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
#elif defined(TARGET_SPARC)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
if (cpu_interrupts_enabled(env) &&
|
||||
@@ -442,7 +422,6 @@ int cpu_exec(CPUArchState *env)
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& !(env->uncached_asr & ASR_I)) {
|
||||
env->exception_index = UC32_EXCP_INTR;
|
||||
do_interrupt(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
@@ -491,18 +470,11 @@ int cpu_exec(CPUArchState *env)
|
||||
do_interrupt(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_NMI) {
|
||||
unsigned int m_flag_archval;
|
||||
if (env->pregs[PR_VR] < 32) {
|
||||
m_flag_archval = M_FLAG_V10;
|
||||
} else {
|
||||
m_flag_archval = M_FLAG_V32;
|
||||
}
|
||||
if ((env->pregs[PR_CCS] & m_flag_archval)) {
|
||||
env->exception_index = EXCP_NMI;
|
||||
do_interrupt(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_NMI
|
||||
&& (env->pregs[PR_CCS] & M_FLAG)) {
|
||||
env->exception_index = EXCP_NMI;
|
||||
do_interrupt(env);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_M68K)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
@@ -550,7 +522,7 @@ int cpu_exec(CPUArchState *env)
|
||||
#if defined(TARGET_I386)
|
||||
env->eflags = env->eflags | cpu_cc_compute_all(env, CC_OP)
|
||||
| (DF & DF_MASK);
|
||||
log_cpu_state(env, CPU_DUMP_CCOP);
|
||||
log_cpu_state(env, X86_DUMP_CCOP);
|
||||
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
#elif defined(TARGET_M68K)
|
||||
cpu_m68k_flush_flags(env, env->cc_op);
|
||||
@@ -654,7 +626,6 @@ int cpu_exec(CPUArchState *env)
|
||||
| env->cc_dest | (env->cc_x << 4);
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
#elif defined(TARGET_MIPS)
|
||||
#elif defined(TARGET_OPENRISC)
|
||||
#elif defined(TARGET_SH4)
|
||||
#elif defined(TARGET_ALPHA)
|
||||
#elif defined(TARGET_CRIS)
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "cpus.h"
|
||||
#include "qtest.h"
|
||||
#include "main-loop.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "compatfd.h"
|
||||
@@ -62,35 +61,6 @@
|
||||
|
||||
static CPUArchState *next_cpu;
|
||||
|
||||
static bool cpu_thread_is_idle(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
if (cpu->stop || cpu->queued_work_first) {
|
||||
return false;
|
||||
}
|
||||
if (cpu->stopped || !runstate_is_running()) {
|
||||
return true;
|
||||
}
|
||||
if (!env->halted || qemu_cpu_has_work(cpu) ||
|
||||
kvm_async_interrupts_enabled()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool all_cpu_threads_idle(void)
|
||||
{
|
||||
CPUArchState *env;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (!cpu_thread_is_idle(env)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* guest cycle counter */
|
||||
|
||||
@@ -397,7 +367,11 @@ void hw_error(const char *fmt, ...)
|
||||
fprintf(stderr, "\n");
|
||||
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
fprintf(stderr, "CPU #%d:\n", env->cpu_index);
|
||||
cpu_dump_state(env, stderr, fprintf, CPU_DUMP_FPU);
|
||||
#ifdef TARGET_I386
|
||||
cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU);
|
||||
#else
|
||||
cpu_dump_state(env, stderr, fprintf, 0);
|
||||
#endif
|
||||
}
|
||||
va_end(ap);
|
||||
abort();
|
||||
@@ -430,9 +404,9 @@ void cpu_synchronize_all_post_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool cpu_is_stopped(CPUState *cpu)
|
||||
int cpu_is_stopped(CPUArchState *env)
|
||||
{
|
||||
return !runstate_is_running() || cpu->stopped;
|
||||
return !runstate_is_running() || env->stopped;
|
||||
}
|
||||
|
||||
static void do_vm_stop(RunState state)
|
||||
@@ -448,24 +422,48 @@ static void do_vm_stop(RunState state)
|
||||
}
|
||||
}
|
||||
|
||||
static bool cpu_can_run(CPUState *cpu)
|
||||
static int cpu_can_run(CPUArchState *env)
|
||||
{
|
||||
if (cpu->stop) {
|
||||
if (env->stop) {
|
||||
return 0;
|
||||
}
|
||||
if (env->stopped || !runstate_is_running()) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool cpu_thread_is_idle(CPUArchState *env)
|
||||
{
|
||||
if (env->stop || env->queued_work_first) {
|
||||
return false;
|
||||
}
|
||||
if (cpu->stopped || !runstate_is_running()) {
|
||||
if (env->stopped || !runstate_is_running()) {
|
||||
return true;
|
||||
}
|
||||
if (!env->halted || qemu_cpu_has_work(env) || kvm_irqchip_in_kernel()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_cpu_threads_idle(void)
|
||||
{
|
||||
CPUArchState *env;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (!cpu_thread_is_idle(env)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cpu_handle_guest_debug(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
gdb_set_stop_cpu(env);
|
||||
qemu_system_debug_request();
|
||||
cpu->stopped = true;
|
||||
env->stopped = 1;
|
||||
}
|
||||
|
||||
static void cpu_signal(int sig)
|
||||
@@ -613,7 +611,7 @@ static void qemu_tcg_init_cpu_signals(void)
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static QemuMutex qemu_global_mutex;
|
||||
QemuMutex qemu_global_mutex;
|
||||
static QemuCond qemu_io_proceeded_cond;
|
||||
static bool iothread_requesting_mutex;
|
||||
|
||||
@@ -640,27 +638,27 @@ void qemu_init_cpu_loop(void)
|
||||
qemu_thread_get_self(&io_thread);
|
||||
}
|
||||
|
||||
void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
void run_on_cpu(CPUArchState *env, void (*func)(void *data), void *data)
|
||||
{
|
||||
struct qemu_work_item wi;
|
||||
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
if (qemu_cpu_is_self(env)) {
|
||||
func(data);
|
||||
return;
|
||||
}
|
||||
|
||||
wi.func = func;
|
||||
wi.data = data;
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = &wi;
|
||||
if (!env->queued_work_first) {
|
||||
env->queued_work_first = &wi;
|
||||
} else {
|
||||
cpu->queued_work_last->next = &wi;
|
||||
env->queued_work_last->next = &wi;
|
||||
}
|
||||
cpu->queued_work_last = &wi;
|
||||
env->queued_work_last = &wi;
|
||||
wi.next = NULL;
|
||||
wi.done = false;
|
||||
|
||||
qemu_cpu_kick(cpu);
|
||||
qemu_cpu_kick(env);
|
||||
while (!wi.done) {
|
||||
CPUArchState *self_env = cpu_single_env;
|
||||
|
||||
@@ -669,32 +667,32 @@ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_queued_work(CPUState *cpu)
|
||||
static void flush_queued_work(CPUArchState *env)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
if (!env->queued_work_first) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ((wi = cpu->queued_work_first)) {
|
||||
cpu->queued_work_first = wi->next;
|
||||
while ((wi = env->queued_work_first)) {
|
||||
env->queued_work_first = wi->next;
|
||||
wi->func(wi->data);
|
||||
wi->done = true;
|
||||
}
|
||||
cpu->queued_work_last = NULL;
|
||||
env->queued_work_last = NULL;
|
||||
qemu_cond_broadcast(&qemu_work_cond);
|
||||
}
|
||||
|
||||
static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
static void qemu_wait_io_event_common(CPUArchState *env)
|
||||
{
|
||||
if (cpu->stop) {
|
||||
cpu->stop = false;
|
||||
cpu->stopped = true;
|
||||
if (env->stop) {
|
||||
env->stop = 0;
|
||||
env->stopped = 1;
|
||||
qemu_cond_signal(&qemu_pause_cond);
|
||||
}
|
||||
flush_queued_work(cpu);
|
||||
cpu->thread_kicked = false;
|
||||
flush_queued_work(env);
|
||||
env->thread_kicked = false;
|
||||
}
|
||||
|
||||
static void qemu_tcg_wait_io_event(void)
|
||||
@@ -713,31 +711,28 @@ static void qemu_tcg_wait_io_event(void)
|
||||
}
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
qemu_wait_io_event_common(ENV_GET_CPU(env));
|
||||
qemu_wait_io_event_common(env);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_kvm_wait_io_event(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
while (cpu_thread_is_idle(env)) {
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
qemu_cond_wait(env->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
qemu_kvm_eat_signals(env);
|
||||
qemu_wait_io_event_common(cpu);
|
||||
qemu_wait_io_event_common(env);
|
||||
}
|
||||
|
||||
static void *qemu_kvm_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUArchState *env = arg;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int r;
|
||||
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
qemu_thread_get_self(env->thread);
|
||||
env->thread_id = qemu_get_thread_id();
|
||||
cpu_single_env = env;
|
||||
|
||||
r = kvm_init_vcpu(env);
|
||||
@@ -749,11 +744,11 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
|
||||
qemu_kvm_init_cpu_signals(env);
|
||||
|
||||
/* signal CPU creation */
|
||||
cpu->created = true;
|
||||
env->created = 1;
|
||||
qemu_cond_signal(&qemu_cpu_cond);
|
||||
|
||||
while (1) {
|
||||
if (cpu_can_run(cpu)) {
|
||||
if (cpu_can_run(env)) {
|
||||
r = kvm_cpu_exec(env);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(env);
|
||||
@@ -772,19 +767,18 @@ static void *qemu_dummy_cpu_thread_fn(void *arg)
|
||||
exit(1);
|
||||
#else
|
||||
CPUArchState *env = arg;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
sigset_t waitset;
|
||||
int r;
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
qemu_thread_get_self(env->thread);
|
||||
env->thread_id = qemu_get_thread_id();
|
||||
|
||||
sigemptyset(&waitset);
|
||||
sigaddset(&waitset, SIG_IPI);
|
||||
|
||||
/* signal CPU creation */
|
||||
cpu->created = true;
|
||||
env->created = 1;
|
||||
qemu_cond_signal(&qemu_cpu_cond);
|
||||
|
||||
cpu_single_env = env;
|
||||
@@ -801,7 +795,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg)
|
||||
}
|
||||
qemu_mutex_lock_iothread();
|
||||
cpu_single_env = env;
|
||||
qemu_wait_io_event_common(cpu);
|
||||
qemu_wait_io_event_common(env);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -812,28 +806,26 @@ static void tcg_exec_all(void);
|
||||
|
||||
static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
CPUArchState *env;
|
||||
CPUArchState *env = arg;
|
||||
|
||||
qemu_tcg_init_cpu_signals();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
qemu_thread_get_self(env->thread);
|
||||
|
||||
/* signal CPU creation */
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
cpu = ENV_GET_CPU(env);
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->created = true;
|
||||
env->thread_id = qemu_get_thread_id();
|
||||
env->created = 1;
|
||||
}
|
||||
qemu_cond_signal(&qemu_cpu_cond);
|
||||
|
||||
/* wait for initial kick-off after machine start */
|
||||
while (ENV_GET_CPU(first_cpu)->stopped) {
|
||||
while (first_cpu->stopped) {
|
||||
qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex);
|
||||
|
||||
/* process any pending work */
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
qemu_wait_io_event_common(ENV_GET_CPU(env));
|
||||
qemu_wait_io_event_common(env);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,31 +840,33 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void qemu_cpu_kick_thread(CPUState *cpu)
|
||||
static void qemu_cpu_kick_thread(CPUArchState *env)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
int err;
|
||||
|
||||
err = pthread_kill(cpu->thread->thread, SIG_IPI);
|
||||
err = pthread_kill(env->thread->thread, SIG_IPI);
|
||||
if (err) {
|
||||
fprintf(stderr, "qemu:%s: %s", __func__, strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
#else /* _WIN32 */
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
SuspendThread(cpu->hThread);
|
||||
if (!qemu_cpu_is_self(env)) {
|
||||
SuspendThread(env->hThread);
|
||||
cpu_signal(0);
|
||||
ResumeThread(cpu->hThread);
|
||||
ResumeThread(env->hThread);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void qemu_cpu_kick(CPUState *cpu)
|
||||
void qemu_cpu_kick(void *_env)
|
||||
{
|
||||
qemu_cond_broadcast(cpu->halt_cond);
|
||||
if (!tcg_enabled() && !cpu->thread_kicked) {
|
||||
qemu_cpu_kick_thread(cpu);
|
||||
cpu->thread_kicked = true;
|
||||
CPUArchState *env = _env;
|
||||
|
||||
qemu_cond_broadcast(env->halt_cond);
|
||||
if (!tcg_enabled() && !env->thread_kicked) {
|
||||
qemu_cpu_kick_thread(env);
|
||||
env->thread_kicked = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,25 +874,21 @@ void qemu_cpu_kick_self(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
assert(cpu_single_env);
|
||||
CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
|
||||
|
||||
if (!cpu_single_cpu->thread_kicked) {
|
||||
qemu_cpu_kick_thread(cpu_single_cpu);
|
||||
cpu_single_cpu->thread_kicked = true;
|
||||
if (!cpu_single_env->thread_kicked) {
|
||||
qemu_cpu_kick_thread(cpu_single_env);
|
||||
cpu_single_env->thread_kicked = true;
|
||||
}
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool qemu_cpu_is_self(CPUState *cpu)
|
||||
int qemu_cpu_is_self(void *_env)
|
||||
{
|
||||
return qemu_thread_is_self(cpu->thread);
|
||||
}
|
||||
CPUArchState *env = _env;
|
||||
|
||||
static bool qemu_in_vcpu_thread(void)
|
||||
{
|
||||
return cpu_single_env && qemu_cpu_is_self(ENV_GET_CPU(cpu_single_env));
|
||||
return qemu_thread_is_self(env->thread);
|
||||
}
|
||||
|
||||
void qemu_mutex_lock_iothread(void)
|
||||
@@ -908,7 +898,7 @@ void qemu_mutex_lock_iothread(void)
|
||||
} else {
|
||||
iothread_requesting_mutex = true;
|
||||
if (qemu_mutex_trylock(&qemu_global_mutex)) {
|
||||
qemu_cpu_kick_thread(ENV_GET_CPU(first_cpu));
|
||||
qemu_cpu_kick_thread(first_cpu);
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
}
|
||||
iothread_requesting_mutex = false;
|
||||
@@ -926,8 +916,7 @@ static int all_vcpus_paused(void)
|
||||
CPUArchState *penv = first_cpu;
|
||||
|
||||
while (penv) {
|
||||
CPUState *pcpu = ENV_GET_CPU(penv);
|
||||
if (!pcpu->stopped) {
|
||||
if (!penv->stopped) {
|
||||
return 0;
|
||||
}
|
||||
penv = penv->next_cpu;
|
||||
@@ -942,19 +931,17 @@ void pause_all_vcpus(void)
|
||||
|
||||
qemu_clock_enable(vm_clock, false);
|
||||
while (penv) {
|
||||
CPUState *pcpu = ENV_GET_CPU(penv);
|
||||
pcpu->stop = true;
|
||||
qemu_cpu_kick(pcpu);
|
||||
penv->stop = 1;
|
||||
qemu_cpu_kick(penv);
|
||||
penv = penv->next_cpu;
|
||||
}
|
||||
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
if (!qemu_thread_is_self(&io_thread)) {
|
||||
cpu_stop_current();
|
||||
if (!kvm_enabled()) {
|
||||
while (penv) {
|
||||
CPUState *pcpu = ENV_GET_CPU(penv);
|
||||
pcpu->stop = 0;
|
||||
pcpu->stopped = true;
|
||||
penv->stop = 0;
|
||||
penv->stopped = 1;
|
||||
penv = penv->next_cpu;
|
||||
}
|
||||
return;
|
||||
@@ -965,7 +952,7 @@ void pause_all_vcpus(void)
|
||||
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
|
||||
penv = first_cpu;
|
||||
while (penv) {
|
||||
qemu_cpu_kick(ENV_GET_CPU(penv));
|
||||
qemu_cpu_kick(penv);
|
||||
penv = penv->next_cpu;
|
||||
}
|
||||
}
|
||||
@@ -977,61 +964,58 @@ void resume_all_vcpus(void)
|
||||
|
||||
qemu_clock_enable(vm_clock, true);
|
||||
while (penv) {
|
||||
CPUState *pcpu = ENV_GET_CPU(penv);
|
||||
pcpu->stop = false;
|
||||
pcpu->stopped = false;
|
||||
qemu_cpu_kick(pcpu);
|
||||
penv->stop = 0;
|
||||
penv->stopped = 0;
|
||||
qemu_cpu_kick(penv);
|
||||
penv = penv->next_cpu;
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||
static void qemu_tcg_init_vcpu(void *_env)
|
||||
{
|
||||
CPUArchState *env = _env;
|
||||
|
||||
/* share a single thread for all cpus with TCG */
|
||||
if (!tcg_cpu_thread) {
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
tcg_halt_cond = cpu->halt_cond;
|
||||
qemu_thread_create(cpu->thread, qemu_tcg_cpu_thread_fn, cpu,
|
||||
env->thread = g_malloc0(sizeof(QemuThread));
|
||||
env->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(env->halt_cond);
|
||||
tcg_halt_cond = env->halt_cond;
|
||||
qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
#ifdef _WIN32
|
||||
cpu->hThread = qemu_thread_get_handle(cpu->thread);
|
||||
env->hThread = qemu_thread_get_handle(env->thread);
|
||||
#endif
|
||||
while (!cpu->created) {
|
||||
while (env->created == 0) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
}
|
||||
tcg_cpu_thread = cpu->thread;
|
||||
tcg_cpu_thread = env->thread;
|
||||
} else {
|
||||
cpu->thread = tcg_cpu_thread;
|
||||
cpu->halt_cond = tcg_halt_cond;
|
||||
env->thread = tcg_cpu_thread;
|
||||
env->halt_cond = tcg_halt_cond;
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_kvm_start_vcpu(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
qemu_thread_create(cpu->thread, qemu_kvm_cpu_thread_fn, env,
|
||||
env->thread = g_malloc0(sizeof(QemuThread));
|
||||
env->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(env->halt_cond);
|
||||
qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
while (!cpu->created) {
|
||||
while (env->created == 0) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_dummy_start_vcpu(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
qemu_thread_create(cpu->thread, qemu_dummy_cpu_thread_fn, env,
|
||||
env->thread = g_malloc0(sizeof(QemuThread));
|
||||
env->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(env->halt_cond);
|
||||
qemu_thread_create(env->thread, qemu_dummy_cpu_thread_fn, env,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
while (!cpu->created) {
|
||||
while (env->created == 0) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
}
|
||||
}
|
||||
@@ -1039,15 +1023,14 @@ static void qemu_dummy_start_vcpu(CPUArchState *env)
|
||||
void qemu_init_vcpu(void *_env)
|
||||
{
|
||||
CPUArchState *env = _env;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
env->nr_cores = smp_cores;
|
||||
env->nr_threads = smp_threads;
|
||||
cpu->stopped = true;
|
||||
env->stopped = 1;
|
||||
if (kvm_enabled()) {
|
||||
qemu_kvm_start_vcpu(env);
|
||||
} else if (tcg_enabled()) {
|
||||
qemu_tcg_init_vcpu(cpu);
|
||||
qemu_tcg_init_vcpu(env);
|
||||
} else {
|
||||
qemu_dummy_start_vcpu(env);
|
||||
}
|
||||
@@ -1056,9 +1039,8 @@ void qemu_init_vcpu(void *_env)
|
||||
void cpu_stop_current(void)
|
||||
{
|
||||
if (cpu_single_env) {
|
||||
CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
|
||||
cpu_single_cpu->stop = false;
|
||||
cpu_single_cpu->stopped = true;
|
||||
cpu_single_env->stop = 0;
|
||||
cpu_single_env->stopped = 1;
|
||||
cpu_exit(cpu_single_env);
|
||||
qemu_cond_signal(&qemu_pause_cond);
|
||||
}
|
||||
@@ -1066,7 +1048,7 @@ void cpu_stop_current(void)
|
||||
|
||||
void vm_stop(RunState state)
|
||||
{
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
if (!qemu_thread_is_self(&io_thread)) {
|
||||
qemu_system_vmstop_request(state);
|
||||
/*
|
||||
* FIXME: should not return to device code in case
|
||||
@@ -1139,18 +1121,17 @@ static void tcg_exec_all(void)
|
||||
}
|
||||
for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
|
||||
CPUArchState *env = next_cpu;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
qemu_clock_enable(vm_clock,
|
||||
(env->singlestep_enabled & SSTEP_NOTIMER) == 0);
|
||||
|
||||
if (cpu_can_run(cpu)) {
|
||||
if (cpu_can_run(env)) {
|
||||
r = tcg_cpu_exec(env);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(env);
|
||||
break;
|
||||
}
|
||||
} else if (cpu->stop || cpu->stopped) {
|
||||
} else if (env->stop || env->stopped) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1164,7 +1145,7 @@ void set_numa_modes(void)
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
if (test_bit(env->cpu_index, node_cpumask[i])) {
|
||||
if (node_cpumask[i] & (1 << env->cpu_index)) {
|
||||
env->numa_node = i;
|
||||
}
|
||||
}
|
||||
@@ -1195,8 +1176,10 @@ void set_cpu_log_filename(const char *optarg)
|
||||
void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
|
||||
{
|
||||
/* XXX: implement xxx_cpu_list for targets that still miss it */
|
||||
#if defined(cpu_list)
|
||||
cpu_list(f, cpu_fprintf);
|
||||
#if defined(cpu_list_id)
|
||||
cpu_list_id(f, cpu_fprintf, optarg);
|
||||
#elif defined(cpu_list)
|
||||
cpu_list(f, cpu_fprintf); /* deprecated */
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1205,8 +1188,7 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
CpuInfoList *head = NULL, *cur_item = NULL;
|
||||
CPUArchState *env;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
CpuInfoList *info;
|
||||
|
||||
cpu_synchronize_state(env);
|
||||
@@ -1216,7 +1198,7 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
info->value->CPU = env->cpu_index;
|
||||
info->value->current = (env == first_cpu);
|
||||
info->value->halted = env->halted;
|
||||
info->value->thread_id = cpu->thread_id;
|
||||
info->value->thread_id = env->thread_id;
|
||||
#if defined(TARGET_I386)
|
||||
info->value->has_pc = true;
|
||||
info->value->pc = env->eip + env->segs[R_CS].base;
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
#include "cpu.h"
|
||||
#include "exec-all.h"
|
||||
#include "memory.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
#include "cputlb.h"
|
||||
|
||||
#include "memory-internal.h"
|
||||
#define WANT_EXEC_OBSOLETE
|
||||
#include "exec-obsolete.h"
|
||||
|
||||
//#define DEBUG_TLB
|
||||
//#define DEBUG_TLB_CHECK
|
||||
@@ -237,7 +237,7 @@ static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr,
|
||||
is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
|
||||
supplied size is only used by tlb_flush_page. */
|
||||
void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
||||
hwaddr paddr, int prot,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int mmu_idx, target_ulong size)
|
||||
{
|
||||
MemoryRegionSection *section;
|
||||
@@ -246,13 +246,13 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
||||
target_ulong code_address;
|
||||
uintptr_t addend;
|
||||
CPUTLBEntry *te;
|
||||
hwaddr iotlb;
|
||||
target_phys_addr_t iotlb;
|
||||
|
||||
assert(size >= TARGET_PAGE_SIZE);
|
||||
if (size != TARGET_PAGE_SIZE) {
|
||||
tlb_add_large_page(env, vaddr, size);
|
||||
}
|
||||
section = phys_page_find(address_space_memory.dispatch, paddr >> TARGET_PAGE_BITS);
|
||||
section = phys_page_find(paddr >> TARGET_PAGE_BITS);
|
||||
#if defined(DEBUG_TLB)
|
||||
printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
|
||||
" prot=%x idx=%d pd=0x%08lx\n",
|
||||
@@ -312,9 +312,7 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
||||
|
||||
/* NOTE: this function can trigger an exception */
|
||||
/* NOTE2: the returned address is not exactly the physical address: it
|
||||
* is actually a ram_addr_t (in system mode; the user mode emulation
|
||||
* version of this function returns a guest virtual address).
|
||||
*/
|
||||
is the offset relative to phys_ram_base */
|
||||
tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
{
|
||||
int mmu_idx, page_index, pd;
|
||||
@@ -325,7 +323,11 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
mmu_idx = cpu_mmu_index(env1);
|
||||
if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code !=
|
||||
(addr & TARGET_PAGE_MASK))) {
|
||||
#ifdef CONFIG_TCG_PASS_AREG0
|
||||
cpu_ldub_code(env1, addr);
|
||||
#else
|
||||
ldub_code(addr);
|
||||
#endif
|
||||
}
|
||||
pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK;
|
||||
mr = iotlb_to_region(pd);
|
||||
@@ -344,6 +346,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
#define MMUSUFFIX _cmmu
|
||||
#undef GETPC
|
||||
#define GETPC() ((uintptr_t)0)
|
||||
#define env cpu_single_env
|
||||
#define SOFTMMU_CODE_ACCESS
|
||||
|
||||
#define SHIFT 0
|
||||
|
||||
@@ -26,18 +26,17 @@ void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr,
|
||||
target_ulong vaddr);
|
||||
void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start,
|
||||
uintptr_t length);
|
||||
MemoryRegionSection *phys_page_find(struct AddressSpaceDispatch *d,
|
||||
hwaddr index);
|
||||
MemoryRegionSection *phys_page_find(target_phys_addr_t index);
|
||||
void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length);
|
||||
void tlb_set_dirty(CPUArchState *env, target_ulong vaddr);
|
||||
extern int tlb_flush_count;
|
||||
|
||||
/* exec.c */
|
||||
void tb_flush_jmp_cache(CPUArchState *env, target_ulong addr);
|
||||
hwaddr memory_region_section_get_iotlb(CPUArchState *env,
|
||||
target_phys_addr_t memory_region_section_get_iotlb(CPUArchState *env,
|
||||
MemoryRegionSection *section,
|
||||
target_ulong vaddr,
|
||||
hwaddr paddr,
|
||||
target_phys_addr_t paddr,
|
||||
int prot,
|
||||
target_ulong *address);
|
||||
bool memory_region_is_unassigned(MemoryRegion *mr);
|
||||
|
||||
@@ -26,14 +26,6 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "qemu_socket.h"
|
||||
#include "iov.h"
|
||||
|
||||
void strpadcpy(char *buf, int buf_size, const char *str, char pad)
|
||||
{
|
||||
int len = qemu_strnlen(str, buf_size);
|
||||
memcpy(buf, str, len);
|
||||
memset(buf + len, pad, buf_size - len);
|
||||
}
|
||||
|
||||
void pstrcpy(char *buf, int buf_size, const char *str)
|
||||
{
|
||||
@@ -115,7 +107,7 @@ time_t mktimegm(struct tm *tm)
|
||||
m += 12;
|
||||
y--;
|
||||
}
|
||||
t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 +
|
||||
t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 +
|
||||
y / 400 - 719469);
|
||||
t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
|
||||
return t;
|
||||
@@ -142,6 +134,175 @@ int qemu_fdatasync(int fd)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* io vectors */
|
||||
|
||||
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
|
||||
{
|
||||
qiov->iov = g_malloc(alloc_hint * sizeof(struct iovec));
|
||||
qiov->niov = 0;
|
||||
qiov->nalloc = alloc_hint;
|
||||
qiov->size = 0;
|
||||
}
|
||||
|
||||
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov)
|
||||
{
|
||||
int i;
|
||||
|
||||
qiov->iov = iov;
|
||||
qiov->niov = niov;
|
||||
qiov->nalloc = -1;
|
||||
qiov->size = 0;
|
||||
for (i = 0; i < niov; i++)
|
||||
qiov->size += iov[i].iov_len;
|
||||
}
|
||||
|
||||
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
|
||||
{
|
||||
assert(qiov->nalloc != -1);
|
||||
|
||||
if (qiov->niov == qiov->nalloc) {
|
||||
qiov->nalloc = 2 * qiov->nalloc + 1;
|
||||
qiov->iov = g_realloc(qiov->iov, qiov->nalloc * sizeof(struct iovec));
|
||||
}
|
||||
qiov->iov[qiov->niov].iov_base = base;
|
||||
qiov->iov[qiov->niov].iov_len = len;
|
||||
qiov->size += len;
|
||||
++qiov->niov;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies iovecs from src to the end of dst. It starts copying after skipping
|
||||
* the given number of bytes in src and copies until src is completely copied
|
||||
* or the total size of the copied iovec reaches size.The size of the last
|
||||
* copied iovec is changed in order to fit the specified total size if it isn't
|
||||
* a perfect fit already.
|
||||
*/
|
||||
void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip,
|
||||
size_t size)
|
||||
{
|
||||
int i;
|
||||
size_t done;
|
||||
void *iov_base;
|
||||
uint64_t iov_len;
|
||||
|
||||
assert(dst->nalloc != -1);
|
||||
|
||||
done = 0;
|
||||
for (i = 0; (i < src->niov) && (done != size); i++) {
|
||||
if (skip >= src->iov[i].iov_len) {
|
||||
/* Skip the whole iov */
|
||||
skip -= src->iov[i].iov_len;
|
||||
continue;
|
||||
} else {
|
||||
/* Skip only part (or nothing) of the iov */
|
||||
iov_base = (uint8_t*) src->iov[i].iov_base + skip;
|
||||
iov_len = src->iov[i].iov_len - skip;
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
if (done + iov_len > size) {
|
||||
qemu_iovec_add(dst, iov_base, size - done);
|
||||
break;
|
||||
} else {
|
||||
qemu_iovec_add(dst, iov_base, iov_len);
|
||||
}
|
||||
done += iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size)
|
||||
{
|
||||
qemu_iovec_copy(dst, src, 0, size);
|
||||
}
|
||||
|
||||
void qemu_iovec_destroy(QEMUIOVector *qiov)
|
||||
{
|
||||
assert(qiov->nalloc != -1);
|
||||
|
||||
qemu_iovec_reset(qiov);
|
||||
g_free(qiov->iov);
|
||||
qiov->nalloc = 0;
|
||||
qiov->iov = NULL;
|
||||
}
|
||||
|
||||
void qemu_iovec_reset(QEMUIOVector *qiov)
|
||||
{
|
||||
assert(qiov->nalloc != -1);
|
||||
|
||||
qiov->niov = 0;
|
||||
qiov->size = 0;
|
||||
}
|
||||
|
||||
void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov; ++i) {
|
||||
memcpy(p, qiov->iov[i].iov_base, qiov->iov[i].iov_len);
|
||||
p += qiov->iov[i].iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)buf;
|
||||
size_t copy;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov && count; ++i) {
|
||||
copy = count;
|
||||
if (copy > qiov->iov[i].iov_len)
|
||||
copy = qiov->iov[i].iov_len;
|
||||
memcpy(qiov->iov[i].iov_base, p, copy);
|
||||
p += copy;
|
||||
count -= copy;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count)
|
||||
{
|
||||
size_t n;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov && count; ++i) {
|
||||
n = MIN(count, qiov->iov[i].iov_len);
|
||||
memset(qiov->iov[i].iov_base, c, n);
|
||||
count -= n;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
|
||||
size_t skip)
|
||||
{
|
||||
int i;
|
||||
size_t done;
|
||||
void *iov_base;
|
||||
uint64_t iov_len;
|
||||
|
||||
done = 0;
|
||||
for (i = 0; (i < qiov->niov) && (done != count); i++) {
|
||||
if (skip >= qiov->iov[i].iov_len) {
|
||||
/* Skip the whole iov */
|
||||
skip -= qiov->iov[i].iov_len;
|
||||
continue;
|
||||
} else {
|
||||
/* Skip only part (or nothing) of the iov */
|
||||
iov_base = (uint8_t*) qiov->iov[i].iov_base + skip;
|
||||
iov_len = qiov->iov[i].iov_len - skip;
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
if (done + iov_len > count) {
|
||||
memset(iov_base, c, count - done);
|
||||
break;
|
||||
} else {
|
||||
memset(iov_base, c, iov_len);
|
||||
}
|
||||
done += iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if a buffer is all zeroes
|
||||
*
|
||||
@@ -280,44 +441,111 @@ int qemu_parse_fd(const char *param)
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* round down to the nearest power of 2*/
|
||||
int64_t pow2floor(int64_t value)
|
||||
{
|
||||
if (!is_power_of_2(value)) {
|
||||
value = 0x8000000000000000ULL >> clz64(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128)
|
||||
* Input is limited to 14-bit numbers
|
||||
* Send/recv data with iovec buffers
|
||||
*
|
||||
* This function send/recv data from/to the iovec buffer directly.
|
||||
* The first `offset' bytes in the iovec buffer are skipped and next
|
||||
* `len' bytes are used.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* do_sendv_recvv(sockfd, iov, len, offset, 1);
|
||||
*
|
||||
* is equal to
|
||||
*
|
||||
* char *buf = malloc(size);
|
||||
* iov_to_buf(iov, iovcnt, buf, offset, size);
|
||||
* send(sockfd, buf, size, 0);
|
||||
* free(buf);
|
||||
*/
|
||||
int uleb128_encode_small(uint8_t *out, uint32_t n)
|
||||
static int do_sendv_recvv(int sockfd, struct iovec *iov, int len, int offset,
|
||||
int do_sendv)
|
||||
{
|
||||
g_assert(n <= 0x3fff);
|
||||
if (n < 0x80) {
|
||||
*out++ = n;
|
||||
return 1;
|
||||
} else {
|
||||
*out++ = (n & 0x7f) | 0x80;
|
||||
*out++ = n >> 7;
|
||||
return 2;
|
||||
int ret, diff, iovlen;
|
||||
struct iovec *last_iov;
|
||||
|
||||
/* last_iov is inclusive, so count from one. */
|
||||
iovlen = 1;
|
||||
last_iov = iov;
|
||||
len += offset;
|
||||
|
||||
while (last_iov->iov_len < len) {
|
||||
len -= last_iov->iov_len;
|
||||
|
||||
last_iov++;
|
||||
iovlen++;
|
||||
}
|
||||
|
||||
diff = last_iov->iov_len - len;
|
||||
last_iov->iov_len -= diff;
|
||||
|
||||
while (iov->iov_len <= offset) {
|
||||
offset -= iov->iov_len;
|
||||
|
||||
iov++;
|
||||
iovlen--;
|
||||
}
|
||||
|
||||
iov->iov_base = (char *) iov->iov_base + offset;
|
||||
iov->iov_len -= offset;
|
||||
|
||||
{
|
||||
#if defined CONFIG_IOVEC && defined CONFIG_POSIX
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = iovlen;
|
||||
|
||||
do {
|
||||
if (do_sendv) {
|
||||
ret = sendmsg(sockfd, &msg, 0);
|
||||
} else {
|
||||
ret = recvmsg(sockfd, &msg, 0);
|
||||
}
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
#else
|
||||
struct iovec *p = iov;
|
||||
ret = 0;
|
||||
while (iovlen > 0) {
|
||||
int rc;
|
||||
if (do_sendv) {
|
||||
rc = send(sockfd, p->iov_base, p->iov_len, 0);
|
||||
} else {
|
||||
rc = qemu_recv(sockfd, p->iov_base, p->iov_len, 0);
|
||||
}
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (rc == 0) {
|
||||
break;
|
||||
}
|
||||
ret += rc;
|
||||
iovlen--, p++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Undo the changes above */
|
||||
iov->iov_base = (char *) iov->iov_base - offset;
|
||||
iov->iov_len += offset;
|
||||
last_iov->iov_len += diff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int uleb128_decode_small(const uint8_t *in, uint32_t *n)
|
||||
int qemu_recvv(int sockfd, struct iovec *iov, int len, int iov_offset)
|
||||
{
|
||||
if (!(*in & 0x80)) {
|
||||
*n = *in++;
|
||||
return 1;
|
||||
} else {
|
||||
*n = *in++ & 0x7f;
|
||||
/* we exceed 14 bit number */
|
||||
if (*in & 0x80) {
|
||||
return -1;
|
||||
}
|
||||
*n |= *in++ << 7;
|
||||
return 2;
|
||||
}
|
||||
return do_sendv_recvv(sockfd, iov, len, iov_offset, 0);
|
||||
}
|
||||
|
||||
int qemu_sendv(int sockfd, struct iovec *iov, int len, int iov_offset)
|
||||
{
|
||||
return do_sendv_recvv(sockfd, iov, len, iov_offset, 1);
|
||||
}
|
||||
|
||||
@@ -128,8 +128,6 @@
|
||||
#define DEF_HELPER_5(name, ret, t1, t2, t3, t4, t5) \
|
||||
DEF_HELPER_FLAGS_5(name, 0, ret, t1, t2, t3, t4, t5)
|
||||
|
||||
/* MAX_OPC_PARAM_IARGS must be set to n if last entry is DEF_HELPER_FLAGS_n. */
|
||||
|
||||
#endif /* DEF_HELPER_H */
|
||||
|
||||
#ifndef GEN_HELPER
|
||||
|
||||
@@ -22,27 +22,8 @@ CONFIG_ADS7846=y
|
||||
CONFIG_MAX111X=y
|
||||
CONFIG_SSI=y
|
||||
CONFIG_SSI_SD=y
|
||||
CONFIG_SSI_M25P80=y
|
||||
CONFIG_LAN9118=y
|
||||
CONFIG_SMC91C111=y
|
||||
CONFIG_DS1338=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
|
||||
CONFIG_ARM_TIMER=y
|
||||
CONFIG_PL011=y
|
||||
CONFIG_PL022=y
|
||||
CONFIG_PL031=y
|
||||
CONFIG_PL041=y
|
||||
CONFIG_PL050=y
|
||||
CONFIG_PL061=y
|
||||
CONFIG_PL080=y
|
||||
CONFIG_PL110=y
|
||||
CONFIG_PL181=y
|
||||
CONFIG_PL190=y
|
||||
CONFIG_PL310=y
|
||||
CONFIG_CADENCE=y
|
||||
CONFIG_XGMAC=y
|
||||
|
||||
CONFIG_VERSATILE_PCI=y
|
||||
CONFIG_VERSATILE_I2C=y
|
||||
@@ -3,7 +3,3 @@
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_XILINX_AXI=y
|
||||
CONFIG_SSI=y
|
||||
CONFIG_SSI_M25P80=y
|
||||
@@ -3,7 +3,3 @@
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_XILINX_AXI=y
|
||||
CONFIG_SSI=y
|
||||
CONFIG_SSI_M25P80=y
|
||||
@@ -1 +0,0 @@
|
||||
# Default configuration for or32-linux-user
|
||||
@@ -1,4 +0,0 @@
|
||||
# Default configuration for or32-softmmu
|
||||
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_OPENCORES_ETH=y
|
||||
Loaded 100 of 1768 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user