mirror of https://github.com/xemu-project/xemu.git
Merge commit 'b1999e8' into xbox
Conflicts: blockdev.c configure hw/pci/pci_ids.h qemu-config.c
This commit is contained in:
commit
82a4db30c9
|
@ -3,10 +3,11 @@ config-all-devices.*
|
|||
config-all-disas.*
|
||||
config-host.*
|
||||
config-target.*
|
||||
trace.h
|
||||
trace.c
|
||||
trace-dtrace.h
|
||||
trace-dtrace.dtrace
|
||||
trace/generated-tracers.h
|
||||
trace/generated-tracers.c
|
||||
trace/generated-tracers-dtrace.h
|
||||
trace/generated-tracers-dtrace.dtrace
|
||||
libcacard/trace/generated-tracers.c
|
||||
*-timestamp
|
||||
*-softmmu
|
||||
*-darwin-user
|
||||
|
@ -70,6 +71,7 @@ fsdev/virtfs-proxy-helper.pod
|
|||
*.tp
|
||||
*.vr
|
||||
*.d
|
||||
!scripts/qemu-guest-agent/fsfreeze-hook.d
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
|
@ -81,12 +83,15 @@ fsdev/virtfs-proxy-helper.pod
|
|||
patches
|
||||
pc-bios/bios-pq/status
|
||||
pc-bios/vgabios-pq/status
|
||||
pc-bios/optionrom/linuxboot.asm
|
||||
pc-bios/optionrom/linuxboot.bin
|
||||
pc-bios/optionrom/linuxboot.raw
|
||||
pc-bios/optionrom/linuxboot.img
|
||||
pc-bios/optionrom/multiboot.asm
|
||||
pc-bios/optionrom/multiboot.bin
|
||||
pc-bios/optionrom/multiboot.raw
|
||||
pc-bios/optionrom/multiboot.img
|
||||
pc-bios/optionrom/kvmvapic.asm
|
||||
pc-bios/optionrom/kvmvapic.bin
|
||||
pc-bios/optionrom/kvmvapic.raw
|
||||
pc-bios/optionrom/kvmvapic.img
|
||||
|
|
32
MAINTAINERS
32
MAINTAINERS
|
@ -98,6 +98,7 @@ S: Maintained
|
|||
F: target-ppc/
|
||||
|
||||
S390
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target-s390x/
|
||||
|
@ -132,13 +133,18 @@ Guest CPU Cores (KVM):
|
|||
----------------------
|
||||
|
||||
Overall
|
||||
M: Avi Kivity <avi@redhat.com>
|
||||
M: Gleb Natapov <gleb@redhat.com>
|
||||
M: Marcelo Tosatti <mtosatti@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
F: kvm-*
|
||||
F: */kvm.*
|
||||
|
||||
ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: target-arm/kvm.c
|
||||
|
||||
PPC
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
|
@ -150,7 +156,7 @@ S: Maintained
|
|||
F: target-s390x/kvm.c
|
||||
|
||||
X86
|
||||
M: Avi Kivity <avi@redhat.com>
|
||||
M: Gleb Natapov <gleb@redhat.com>
|
||||
M: Marcelo Tosatti <mtosatti@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
|
@ -379,7 +385,7 @@ New World
|
|||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc_newworld.c
|
||||
F: hw/ppc/mac_newworld.c
|
||||
F: hw/unin_pci.c
|
||||
F: hw/dec_pci.[hc]
|
||||
|
||||
|
@ -387,15 +393,16 @@ Old World
|
|||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc_oldworld.c
|
||||
F: hw/ppc/mac_oldworld.c
|
||||
F: hw/grackle_pci.c
|
||||
|
||||
PReP
|
||||
M: Andreas Färber <andreas.faerber@web.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc_prep.c
|
||||
F: hw/ppc/prep.c
|
||||
F: hw/prep_pci.[hc]
|
||||
F: hw/pc87312.[hc]
|
||||
|
||||
sPAPR
|
||||
M: David Gibson <david@gibson.dropbear.id.au>
|
||||
|
@ -447,6 +454,14 @@ M: Alexander Graf <agraf@suse.de>
|
|||
S: Maintained
|
||||
F: hw/s390-*.c
|
||||
|
||||
S390 Virtio-ccw
|
||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Supported
|
||||
F: hw/s390x/s390-virtio-ccw.c
|
||||
F: hw/s390x/css.[hc]
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
UniCore32 Machines
|
||||
-------------
|
||||
PKUnity-3 SoC initramfs-with-busybox
|
||||
|
@ -558,6 +573,12 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
|
|||
S: Supported
|
||||
F: hw/virtio-blk*
|
||||
|
||||
virtio-ccw
|
||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/virtio-ccw.[hc]
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
virtio-serial
|
||||
M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Supported
|
||||
|
@ -600,6 +621,7 @@ M: Andreas Färber <afaerber@suse.de>
|
|||
S: Supported
|
||||
F: qom/cpu.c
|
||||
F: include/qemu/cpu.h
|
||||
F: target-i386/cpu.c
|
||||
|
||||
Device Tree
|
||||
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
|
|
97
Makefile
97
Makefile
|
@ -31,12 +31,15 @@ ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
|||
endif
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
GENERATED_HEADERS += trace-dtrace.h
|
||||
endif
|
||||
GENERATED_HEADERS = config-host.h qemu-options.def
|
||||
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c trace.c
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-tracers.h
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
GENERATED_HEADERS += trace/generated-tracers-dtrace.h
|
||||
endif
|
||||
GENERATED_SOURCES += trace/generated-tracers.c
|
||||
|
||||
# Don't try to regenerate Makefile or configure
|
||||
# We don't generate any of them
|
||||
|
@ -63,7 +66,7 @@ endif
|
|||
|
||||
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))
|
||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
|
||||
|
||||
ifeq ($(SUBDIR_DEVICES_MAK),)
|
||||
config-all-devices.mak:
|
||||
|
@ -100,6 +103,17 @@ defconfig:
|
|||
|
||||
-include config-all-devices.mak
|
||||
-include config-all-disas.mak
|
||||
CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y)
|
||||
CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y)
|
||||
CONFIG_ALL=y
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
include $(SRC_PATH)/tests/Makefile
|
||||
endif
|
||||
ifeq ($(CONFIG_SMARTCARD_NSS),y)
|
||||
include $(SRC_PATH)/libcacard/Makefile
|
||||
endif
|
||||
|
||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
|
||||
|
||||
|
@ -109,30 +123,23 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx
|
|||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
|
||||
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||
|
||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||
|
||||
subdir-%:
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
endif
|
||||
|
||||
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,)
|
||||
|
||||
pixman/Makefile: $(SRC_PATH)/pixman/configure
|
||||
(cd pixman; CFLAGS="$(CFLAGS) -fPIC" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static)
|
||||
(cd pixman; CFLAGS="$(CFLAGS) -fPIC $(extra_cflags) $(extra_ldflags)" $(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)
|
||||
|
||||
$(filter %-user,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(user-obj-y)
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y)
|
||||
|
||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
romsubdir-%:
|
||||
|
@ -148,41 +155,25 @@ version.o: $(SRC_PATH)/version.rc config-host.h
|
|||
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
|
||||
|
||||
version-obj-$(CONFIG_WIN32) += version.o
|
||||
Makefile: $(version-obj-y)
|
||||
|
||||
######################################################################
|
||||
# Build library with stubs
|
||||
# Build libraries
|
||||
|
||||
libqemustub.a: $(stub-obj-y)
|
||||
|
||||
######################################################################
|
||||
# Support building shared library libcacard
|
||||
|
||||
.PHONY: libcacard.la install-libcacard
|
||||
libcacard.la: $(oslib-obj-y) qemu-timer-common.o $(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,)
|
||||
libqemuutil.a: $(util-obj-y)
|
||||
|
||||
######################################################################
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
|
||||
tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
|
||||
main-loop.o iohandler.o error.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 $(block-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||
|
||||
vscclient$(EXESUF): LIBS += $(libcacard_libs)
|
||||
vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) libcacard/vscclient.o libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
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): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
|
@ -193,10 +184,6 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
|
|||
|
||||
gen-out-type = $(subst .,-,$(suffix $@))
|
||||
|
||||
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 :\
|
||||
|
@ -222,26 +209,27 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
|||
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)
|
||||
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) $(oslib-obj-y) $(trace-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) libqemustub.a
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
find . -name '*.[od]' -exec rm -f {} +
|
||||
rm -f *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
find . -name '*.[oda]' -type f -exec rm -f {} +
|
||||
find . -name '*.l[oa]' -type f -exec rm -f {} +
|
||||
rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -Rf .libs
|
||||
rm -f qemu-img-cmds.h
|
||||
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 trace/generated-tracers-dtrace.dtrace*
|
||||
rm -f trace/generated-tracers-dtrace.h*
|
||||
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
|
||||
$(MAKE) -C tests/tcg clean
|
||||
for d in $(ALL_SUBDIRS) libcacard; do \
|
||||
for d in $(ALL_SUBDIRS); do \
|
||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||
rm -f $$d/qemu-options.def; \
|
||||
done
|
||||
|
@ -277,6 +265,7 @@ bepo
|
|||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
|
||||
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
|
@ -327,6 +316,9 @@ ifneq ($(BLOBS),)
|
|||
set -e; for x in $(BLOBS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||
done
|
||||
endif
|
||||
ifeq ($(CONFIG_GTK),y)
|
||||
$(MAKE) -C po $@
|
||||
endif
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
||||
set -e; for x in $(KEYMAPS); do \
|
||||
|
@ -342,7 +334,8 @@ test speed: all
|
|||
|
||||
.PHONY: TAGS
|
||||
TAGS:
|
||||
find "$(SRC_PATH)" -name '*.[hc]' -print0 | xargs -0 etags
|
||||
rm -f $@
|
||||
find "$(SRC_PATH)" -name '*.[hc]' -exec etags --append {} +
|
||||
|
||||
cscope:
|
||||
rm -f ./cscope.*
|
||||
|
|
270
Makefile.objs
270
Makefile.objs
|
@ -1,58 +1,31 @@
|
|||
#######################################################################
|
||||
# Stub library, linked in tools
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
universal-obj-y =
|
||||
universal-obj-y += qemu-log.o
|
||||
|
||||
#######################################################################
|
||||
# QObject
|
||||
qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
|
||||
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
|
||||
qobject-obj-y += qerror.o error.o qemu-error.o
|
||||
|
||||
universal-obj-y += $(qobject-obj-y)
|
||||
|
||||
#######################################################################
|
||||
# QOM
|
||||
qom-obj-y = qom/
|
||||
|
||||
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-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
|
||||
oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
|
||||
|
||||
#######################################################################
|
||||
# coroutines
|
||||
coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
||||
coroutine-obj-y += qemu-coroutine-sleep.o
|
||||
ifeq ($(CONFIG_UCONTEXT_COROUTINE),y)
|
||||
coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o
|
||||
else
|
||||
ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y)
|
||||
coroutine-obj-$(CONFIG_POSIX) += coroutine-sigaltstack.o
|
||||
else
|
||||
coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o
|
||||
endif
|
||||
endif
|
||||
coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
|
||||
util-obj-y = util/ qobject/ qapi/ trace/
|
||||
|
||||
#######################################################################
|
||||
# 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 += $(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 = async.o thread-pool.o
|
||||
block-obj-y += nbd.o block.o blockjob.o
|
||||
block-obj-y += main-loop.o iohandler.o qemu-timer.o
|
||||
block-obj-$(CONFIG_POSIX) += aio-posix.o
|
||||
block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||
block-obj-y += block/
|
||||
block-obj-y += $(qapi-obj-y) qapi-types.o qapi-visit.o
|
||||
block-obj-y += qapi-types.o qapi-visit.o
|
||||
|
||||
block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
||||
block-obj-y += qemu-coroutine-sleep.o
|
||||
ifeq ($(CONFIG_UCONTEXT_COROUTINE),y)
|
||||
block-obj-$(CONFIG_POSIX) += coroutine-ucontext.o
|
||||
else
|
||||
ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y)
|
||||
block-obj-$(CONFIG_POSIX) += coroutine-sigaltstack.o
|
||||
else
|
||||
block-obj-$(CONFIG_POSIX) += coroutine-gthread.o
|
||||
endif
|
||||
endif
|
||||
block-obj-$(CONFIG_WIN32) += coroutine-win32.o
|
||||
|
||||
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
||||
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
||||
|
@ -60,142 +33,6 @@ ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
|||
CONFIG_REALLY_VIRTFS=y
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# 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/
|
||||
common-obj-y += qom/
|
||||
common-obj-y += readline.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 += migration.o migration-tcp.o
|
||||
common-obj-y += migration.o migration-tcp.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += block-migration.o iohandler.o
|
||||
common-obj-y += bitmap.o bitops.o
|
||||
common-obj-y += page_cache.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-y += audio/
|
||||
common-obj-y += hw/
|
||||
extra-obj-y += hw/
|
||||
|
||||
common-obj-y += ui/
|
||||
common-obj-y += bt-host.o bt-vhci.o
|
||||
|
||||
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
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# libuser
|
||||
|
||||
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 += module.o
|
||||
user-obj-y += qemu-user.o
|
||||
user-obj-y += qom/
|
||||
|
||||
######################################################################
|
||||
# disassemblers
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
|
||||
universal-obj-y += disas/
|
||||
|
||||
######################################################################
|
||||
# trace
|
||||
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
TRACE_H_EXTRA_DEPS=trace-dtrace.h
|
||||
endif
|
||||
trace.h: trace.h-timestamp $(TRACE_H_EXTRA_DEPS)
|
||||
trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=h \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
< $< > $@," GEN trace.h")
|
||||
@cmp -s $@ trace.h || cp $@ trace.h
|
||||
|
||||
trace.c: trace.c-timestamp
|
||||
trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=c \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
< $< > $@," GEN trace.c")
|
||||
@cmp -s $@ trace.c || cp $@ trace.c
|
||||
|
||||
trace.o: trace.c $(GENERATED_HEADERS)
|
||||
|
||||
trace-dtrace.h: trace-dtrace.dtrace
|
||||
$(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.h")
|
||||
|
||||
# Normal practice is to name DTrace probe file with a '.d' extension
|
||||
# but that gets picked up by QEMU's Makefile as an external dependency
|
||||
# rule file. So we use '.dtrace' instead
|
||||
trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp
|
||||
trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--format=d \
|
||||
--backend=$(TRACE_BACKEND) \
|
||||
< $< > $@," GEN trace-dtrace.dtrace")
|
||||
@cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace
|
||||
|
||||
trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
|
||||
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
|
||||
|
||||
ifeq ($(LIBTOOL),)
|
||||
trace-dtrace.lo: trace-dtrace.dtrace
|
||||
@echo "missing libtool. please install and rerun configure."; exit 1
|
||||
else
|
||||
trace-dtrace.lo: trace-dtrace.dtrace
|
||||
$(call quiet-command,$(LIBTOOL) --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN trace-dtrace.o")
|
||||
endif
|
||||
|
||||
trace/simple.o: trace/simple.c $(GENERATED_HEADERS)
|
||||
|
||||
trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace.o
|
||||
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-obj-$(CONFIG_TRACE_SIMPLE) += qemu-timer-common.o
|
||||
trace-obj-$(CONFIG_TRACE_STDERR) += trace/stderr.o
|
||||
trace-obj-y += trace/control.o
|
||||
|
||||
$(trace-obj-y): $(GENERATED_HEADERS)
|
||||
|
||||
universal-obj-y += $(trace-obj-y)
|
||||
|
||||
######################################################################
|
||||
# smartcard
|
||||
|
||||
|
@ -205,24 +42,69 @@ libcacard-y += libcacard/vcard_emul_nss.o
|
|||
libcacard-y += libcacard/vcard_emul_type.o
|
||||
libcacard-y += libcacard/card_7816.o
|
||||
|
||||
######################################################################
|
||||
# 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.
|
||||
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += net/
|
||||
common-obj-y += readline.o
|
||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration.o migration-tcp.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += block-migration.o
|
||||
common-obj-y += page_cache.o xbzrle.o
|
||||
|
||||
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
||||
|
||||
common-obj-$(CONFIG_SPICE) += 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
|
||||
|
||||
common-obj-y += dma-helpers.o
|
||||
common-obj-y += vl.o
|
||||
common-obj-y += tpm/
|
||||
|
||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
||||
|
||||
common-obj-y += backends/
|
||||
|
||||
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
|
||||
|
||||
common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
|
||||
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
qapi-obj-y = qapi/
|
||||
qapi-obj-y += qapi-types.o qapi-visit.o
|
||||
|
||||
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o
|
||||
common-obj-y += qmp.o hmp.o
|
||||
endif
|
||||
|
||||
universal-obj-y += $(qapi-obj-y)
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
common-obj-y += qemu-log.o
|
||||
common-obj-y += tcg-runtime.o
|
||||
common-obj-y += hw/
|
||||
common-obj-y += qom/
|
||||
common-obj-y += disas/
|
||||
|
||||
######################################################################
|
||||
# guest agent
|
||||
|
||||
qga-obj-y = qga/ module.o qemu-tool.o
|
||||
qga-obj-$(CONFIG_POSIX) += qemu-sockets.o qemu-option.o
|
||||
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
|
||||
# by libqemuutil.a. These should be moved to a separate .json schema.
|
||||
qga-obj-y = qga/ qapi-types.o qapi-visit.o
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
|
@ -232,12 +114,8 @@ QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
|||
|
||||
nested-vars += \
|
||||
stub-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
qom-obj-y \
|
||||
qapi-obj-y \
|
||||
block-obj-y \
|
||||
user-obj-y \
|
||||
common-obj-y \
|
||||
universal-obj-y \
|
||||
extra-obj-y
|
||||
common-obj-y
|
||||
dummy := $(call unnest-vars)
|
||||
|
|
|
@ -54,7 +54,7 @@ $(QEMU_PROG).stp: $(SRC_PATH)/trace-events
|
|||
--binary=$(bindir)/$(QEMU_PROG) \
|
||||
--target-arch=$(TARGET_ARCH) \
|
||||
--target-type=$(TARGET_TYPE) \
|
||||
< $< > $@," GEN $(QEMU_PROG).stp")
|
||||
< $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp")
|
||||
else
|
||||
stap:
|
||||
endif
|
||||
|
@ -69,13 +69,12 @@ all: $(PROGS) stap
|
|||
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-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target-$(TARGET_BASE_ARCH)/
|
||||
obj-y += disas.o
|
||||
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
|
||||
|
||||
tci-dis.o: QEMU_CFLAGS += -I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/tci
|
||||
|
||||
#########################################################
|
||||
# Linux user emulator target
|
||||
|
||||
|
@ -84,7 +83,7 @@ ifdef CONFIG_LINUX_USER
|
|||
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
|
||||
|
||||
obj-y += linux-user/
|
||||
obj-y += gdbstub.o thunk.o user-exec.o $(oslib-obj-y)
|
||||
obj-y += gdbstub.o thunk.o user-exec.o
|
||||
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
|
@ -96,7 +95,7 @@ ifdef CONFIG_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 += gdbstub.o user-exec.o
|
||||
|
||||
endif #CONFIG_BSD_USER
|
||||
|
||||
|
@ -110,7 +109,9 @@ CONFIG_NO_GET_MEMORY_MAPPING = $(if $(subst n,,$(CONFIG_HAVE_GET_MEMORY_MAPPING)
|
|||
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 += qtest.o
|
||||
obj-y += hw/
|
||||
obj-$(CONFIG_FDT) += device_tree.o
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
obj-$(CONFIG_NO_KVM) += kvm-stub.o
|
||||
obj-y += memory.o savevm.o cputlb.o
|
||||
|
@ -146,22 +147,16 @@ nested-vars += obj-y
|
|||
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))
|
||||
else
|
||||
all-obj-y += $(addprefix ../, $(user-obj-y))
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
ifdef QEMU_PROGW
|
||||
# The linker builds a windows executable. Make also a console executable.
|
||||
$(QEMU_PROGW): $(all-obj-y) ../libqemustub.a
|
||||
$(QEMU_PROGW): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
$(call LINK,$^)
|
||||
$(QEMU_PROG): $(QEMU_PROGW)
|
||||
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
|
||||
else
|
||||
$(QEMU_PROG): $(all-obj-y) ../libqemustub.a
|
||||
$(QEMU_PROG): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
$(call LINK,$^)
|
||||
endif
|
||||
|
||||
|
|
37
TODO
37
TODO
|
@ -1,37 +0,0 @@
|
|||
General:
|
||||
-------
|
||||
- cycle counter for all archs
|
||||
- cpu_interrupt() win32/SMP fix
|
||||
- merge PIC spurious interrupt patch
|
||||
- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
|
||||
- config file (at least for windows/Mac OS X)
|
||||
- update doc: PCI infos.
|
||||
- basic VGA optimizations
|
||||
- better code fetch
|
||||
- do not resize vga if invalid size.
|
||||
- TLB code protection support for PPC
|
||||
- disable SMC handling for ARM/SPARC/PPC (not finished)
|
||||
- see undefined flags for BTx insn
|
||||
- keyboard output buffer filling timing emulation
|
||||
- tests for each target CPU
|
||||
- fix all remaining thread lock issues (must put TBs in a specific invalid
|
||||
state, find a solution for tb_flush()).
|
||||
|
||||
ppc specific:
|
||||
------------
|
||||
- TLB invalidate not needed if msr_pr changes
|
||||
- enable shift optimizations ?
|
||||
|
||||
linux-user specific:
|
||||
-------------------
|
||||
- remove threading support as it cannot work at this point
|
||||
- improve IPC syscalls
|
||||
- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
|
||||
issues, fix 16 bit uid issues)
|
||||
- use kernel traps for unaligned accesses on ARM ?
|
||||
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
- int15 ah=86: use better timing
|
||||
- use -msoft-float on ARM
|
133
aio-posix.c
133
aio-posix.c
|
@ -25,6 +25,7 @@ struct AioHandler
|
|||
IOHandler *io_write;
|
||||
AioFlushHandler *io_flush;
|
||||
int deleted;
|
||||
int pollfds_idx;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
@ -85,9 +86,10 @@ void aio_set_fd_handler(AioContext *ctx,
|
|||
node->io_write = io_write;
|
||||
node->io_flush = io_flush;
|
||||
node->opaque = opaque;
|
||||
node->pollfds_idx = -1;
|
||||
|
||||
node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0);
|
||||
node->pfd.events |= (io_write ? G_IO_OUT : 0);
|
||||
node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0);
|
||||
node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0);
|
||||
}
|
||||
|
||||
aio_notify(ctx);
|
||||
|
@ -110,13 +112,6 @@ bool aio_pending(AioContext *ctx)
|
|||
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;
|
||||
|
@ -129,30 +124,12 @@ bool aio_pending(AioContext *ctx)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
static bool aio_dispatch(AioContext *ctx)
|
||||
{
|
||||
static struct timeval tv0;
|
||||
AioHandler *node;
|
||||
fd_set rdfds, wrfds;
|
||||
int max_fd = -1;
|
||||
int ret;
|
||||
bool busy, progress;
|
||||
|
||||
progress = false;
|
||||
bool 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.
|
||||
*/
|
||||
|
@ -166,12 +143,15 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
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) {
|
||||
if (!node->deleted &&
|
||||
(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) {
|
||||
if (!node->deleted &&
|
||||
(revents & (G_IO_OUT | G_IO_ERR)) &&
|
||||
node->io_write) {
|
||||
node->io_write(node->opaque);
|
||||
progress = true;
|
||||
}
|
||||
|
@ -186,6 +166,30 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
AioHandler *node;
|
||||
int ret;
|
||||
bool busy, progress;
|
||||
|
||||
progress = false;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call them.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for qemu_aio_wait loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
blocking = false;
|
||||
progress = true;
|
||||
}
|
||||
|
||||
if (aio_dispatch(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
if (progress && !blocking) {
|
||||
return true;
|
||||
|
@ -193,12 +197,13 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
FD_ZERO(&rdfds);
|
||||
FD_ZERO(&wrfds);
|
||||
g_array_set_size(ctx->pollfds, 0);
|
||||
|
||||
/* fill fd sets */
|
||||
/* fill pollfds */
|
||||
busy = false;
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
node->pollfds_idx = -1;
|
||||
|
||||
/* If there aren't pending AIO operations, don't invoke callbacks.
|
||||
* Otherwise, if there are no AIO requests, qemu_aio_wait() would
|
||||
* wait indefinitely.
|
||||
|
@ -209,13 +214,13 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
}
|
||||
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);
|
||||
if (!node->deleted && node->pfd.events) {
|
||||
GPollFD pfd = {
|
||||
.fd = node->pfd.fd,
|
||||
.events = node->pfd.events,
|
||||
};
|
||||
node->pollfds_idx = ctx->pollfds->len;
|
||||
g_array_append_val(ctx->pollfds, pfd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,42 +232,24 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
}
|
||||
|
||||
/* wait until next event */
|
||||
ret = select(max_fd, &rdfds, &wrfds, NULL, blocking ? NULL : &tv0);
|
||||
ret = g_poll((GPollFD *)ctx->pollfds->data,
|
||||
ctx->pollfds->len,
|
||||
blocking ? -1 : 0);
|
||||
|
||||
/* 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);
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->pollfds_idx != -1) {
|
||||
GPollFD *pfd = &g_array_index(ctx->pollfds, GPollFD,
|
||||
node->pollfds_idx);
|
||||
node->pfd.revents = pfd->revents;
|
||||
}
|
||||
}
|
||||
if (aio_dispatch(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
|
||||
return progress;
|
||||
assert(progress || busy);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -214,5 +214,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
events[ret - WAIT_OBJECT_0] = events[--count];
|
||||
}
|
||||
|
||||
return progress;
|
||||
assert(progress || busy);
|
||||
return true;
|
||||
}
|
||||
|
|
27
arch_init.c
27
arch_init.c
|
@ -293,8 +293,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
|||
|
||||
if (!cache_is_cached(XBZRLE.cache, current_addr)) {
|
||||
if (!last_stage) {
|
||||
cache_insert(XBZRLE.cache, current_addr,
|
||||
g_memdup(current_data, TARGET_PAGE_SIZE));
|
||||
cache_insert(XBZRLE.cache, current_addr, current_data);
|
||||
}
|
||||
acct_info.xbzrle_cache_miss++;
|
||||
return -1;
|
||||
|
@ -379,6 +378,8 @@ static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Needs iothread lock! */
|
||||
|
||||
static void migration_bitmap_sync(void)
|
||||
{
|
||||
RAMBlock *block;
|
||||
|
@ -414,6 +415,7 @@ static void migration_bitmap_sync(void)
|
|||
if (end_time > start_time + 1000) {
|
||||
s->dirty_pages_rate = num_dirty_pages_period * 1000
|
||||
/ (end_time - start_time);
|
||||
s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE;
|
||||
start_time = end_time;
|
||||
num_dirty_pages_period = 0;
|
||||
}
|
||||
|
@ -567,10 +569,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
|||
bitmap_set(migration_bitmap, 0, ram_pages);
|
||||
migration_dirty_pages = ram_pages;
|
||||
|
||||
qemu_mutex_lock_ramlist();
|
||||
bytes_transferred = 0;
|
||||
reset_ram_globals();
|
||||
|
||||
if (migrate_use_xbzrle()) {
|
||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
||||
TARGET_PAGE_SIZE,
|
||||
|
@ -584,8 +582,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
|||
acct_clear();
|
||||
}
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_mutex_lock_ramlist();
|
||||
bytes_transferred = 0;
|
||||
reset_ram_globals();
|
||||
|
||||
memory_global_dirty_log_start();
|
||||
migration_bitmap_sync();
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
||||
|
||||
|
@ -642,12 +646,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
|||
i++;
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_ramlist();
|
||||
|
||||
if (ret < 0) {
|
||||
bytes_transferred += total_sent;
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_ramlist();
|
||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||
total_sent += 8;
|
||||
bytes_transferred += total_sent;
|
||||
|
@ -657,9 +662,8 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
|||
|
||||
static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||
{
|
||||
migration_bitmap_sync();
|
||||
|
||||
qemu_mutex_lock_ramlist();
|
||||
migration_bitmap_sync();
|
||||
|
||||
/* try transferring iterative blocks of memory */
|
||||
|
||||
|
@ -689,7 +693,9 @@ static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
|
|||
remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE;
|
||||
|
||||
if (remaining_size < max_size) {
|
||||
qemu_mutex_lock_iothread();
|
||||
migration_bitmap_sync();
|
||||
qemu_mutex_unlock_iothread();
|
||||
remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE;
|
||||
}
|
||||
return remaining_size;
|
||||
|
@ -851,9 +857,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
|||
|
||||
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;
|
||||
|
|
13
async.c
13
async.c
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "qemu-common.h"
|
||||
#include "block/aio.h"
|
||||
#include "block/thread-pool.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
/***********************************************************/
|
||||
|
@ -172,8 +173,10 @@ aio_ctx_finalize(GSource *source)
|
|||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
|
||||
thread_pool_free(ctx->thread_pool);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL);
|
||||
event_notifier_cleanup(&ctx->notifier);
|
||||
g_array_free(ctx->pollfds, TRUE);
|
||||
}
|
||||
|
||||
static GSourceFuncs aio_source_funcs = {
|
||||
|
@ -189,6 +192,14 @@ GSource *aio_get_g_source(AioContext *ctx)
|
|||
return &ctx->source;
|
||||
}
|
||||
|
||||
ThreadPool *aio_get_thread_pool(AioContext *ctx)
|
||||
{
|
||||
if (!ctx->thread_pool) {
|
||||
ctx->thread_pool = thread_pool_new(ctx);
|
||||
}
|
||||
return ctx->thread_pool;
|
||||
}
|
||||
|
||||
void aio_notify(AioContext *ctx)
|
||||
{
|
||||
event_notifier_set(&ctx->notifier);
|
||||
|
@ -198,6 +209,8 @@ AioContext *aio_context_new(void)
|
|||
{
|
||||
AioContext *ctx;
|
||||
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
|
||||
ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
|
||||
ctx->thread_pool = NULL;
|
||||
event_notifier_init(&ctx->notifier, false);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
(EventNotifierHandler *)
|
||||
|
|
|
@ -828,8 +828,9 @@ static int audio_attach_capture (HWVoiceOut *hw)
|
|||
QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
|
||||
QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);
|
||||
#ifdef DEBUG_CAPTURE
|
||||
asprintf (&sw->name, "for %p %d,%d,%d",
|
||||
hw, sw->info.freq, sw->info.bits, sw->info.nchannels);
|
||||
sw->name = g_strdup_printf ("for %p %d,%d,%d",
|
||||
hw, sw->info.freq, sw->info.bits,
|
||||
sw->info.nchannels);
|
||||
dolog ("Added %s active = %d\n", sw->name, sw->active);
|
||||
#endif
|
||||
if (sw->active) {
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
common-obj-y += rng.o rng-egd.o
|
||||
common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
|
||||
common-obj-y += msmouse.o
|
||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||
$(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "char/char.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "usb.h"
|
||||
#include "baum.h"
|
||||
#include "hw/usb.h"
|
||||
#include <brlapi.h>
|
||||
#include <brlapi_constants.h>
|
||||
#include <brlapi_keycodes.h>
|
||||
|
@ -562,7 +561,7 @@ static void baum_close(struct CharDriverState *chr)
|
|||
g_free(baum);
|
||||
}
|
||||
|
||||
CharDriverState *chr_baum_init(QemuOpts *opts)
|
||||
CharDriverState *chr_baum_init(void)
|
||||
{
|
||||
BaumDriverState *baum;
|
||||
CharDriverState *chr;
|
||||
|
@ -625,3 +624,10 @@ fail_handle:
|
|||
g_free(baum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver_qapi("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
|
@ -25,7 +25,6 @@
|
|||
#include "qemu-common.h"
|
||||
#include "char/char.h"
|
||||
#include "ui/console.h"
|
||||
#include "msmouse.h"
|
||||
|
||||
#define MSMOUSE_LO6(n) ((n) & 0x3f)
|
||||
#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
|
||||
|
@ -64,7 +63,7 @@ static void msmouse_chr_close (struct CharDriverState *chr)
|
|||
g_free (chr);
|
||||
}
|
||||
|
||||
CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
|
||||
CharDriverState *qemu_chr_open_msmouse(void)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
|
||||
|
@ -76,3 +75,10 @@ CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
|
|||
|
||||
return chr;
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver_qapi("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
|
@ -207,7 +207,7 @@ static void rng_egd_class_init(ObjectClass *klass, void *data)
|
|||
rbc->opened = rng_egd_opened;
|
||||
}
|
||||
|
||||
static TypeInfo rng_egd_info = {
|
||||
static const TypeInfo rng_egd_info = {
|
||||
.name = TYPE_RNG_EGD,
|
||||
.parent = TYPE_RNG_BACKEND,
|
||||
.instance_size = sizeof(RngEgd),
|
||||
|
|
|
@ -74,7 +74,7 @@ static void rng_random_opened(RngBackend *b, Error **errp)
|
|||
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"filename", "a valid filename");
|
||||
} else {
|
||||
s->fd = open(s->filename, O_RDONLY | O_NONBLOCK);
|
||||
s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK);
|
||||
|
||||
if (s->fd == -1) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, s->filename);
|
||||
|
@ -130,7 +130,7 @@ static void rng_random_finalize(Object *obj)
|
|||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
|
||||
if (s->fd != -1) {
|
||||
close(s->fd);
|
||||
qemu_close(s->fd);
|
||||
}
|
||||
|
||||
g_free(s->filename);
|
||||
|
@ -144,7 +144,7 @@ static void rng_random_class_init(ObjectClass *klass, void *data)
|
|||
rbc->opened = rng_random_opened;
|
||||
}
|
||||
|
||||
static TypeInfo rng_random_info = {
|
||||
static const TypeInfo rng_random_info = {
|
||||
.name = TYPE_RNG_RANDOM,
|
||||
.parent = TYPE_RNG_BACKEND,
|
||||
.instance_size = sizeof(RndRandom),
|
||||
|
|
|
@ -76,7 +76,7 @@ static void rng_backend_init(Object *obj)
|
|||
NULL);
|
||||
}
|
||||
|
||||
static TypeInfo rng_backend_info = {
|
||||
static const TypeInfo rng_backend_info = {
|
||||
.name = TYPE_RNG_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(RngBackend),
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
#include "sysemu/blockdev.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS)
|
||||
#define BLOCK_SIZE (1 << 20)
|
||||
#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLOCK_SIZE >> BDRV_SECTOR_BITS)
|
||||
|
||||
#define BLK_MIG_FLAG_DEVICE_BLOCK 0x01
|
||||
#define BLK_MIG_FLAG_EOS 0x02
|
||||
|
@ -42,19 +43,24 @@
|
|||
#endif
|
||||
|
||||
typedef struct BlkMigDevState {
|
||||
/* Written during setup phase. Can be read without a lock. */
|
||||
BlockDriverState *bs;
|
||||
int bulk_completed;
|
||||
int shared_base;
|
||||
int64_t total_sectors;
|
||||
QSIMPLEQ_ENTRY(BlkMigDevState) entry;
|
||||
|
||||
/* Only used by migration thread. Does not need a lock. */
|
||||
int bulk_completed;
|
||||
int64_t cur_sector;
|
||||
int64_t cur_dirty;
|
||||
int64_t completed_sectors;
|
||||
int64_t total_sectors;
|
||||
int64_t dirty;
|
||||
QSIMPLEQ_ENTRY(BlkMigDevState) entry;
|
||||
|
||||
/* Protected by block migration lock. */
|
||||
unsigned long *aio_bitmap;
|
||||
int64_t completed_sectors;
|
||||
} BlkMigDevState;
|
||||
|
||||
typedef struct BlkMigBlock {
|
||||
/* Only used by migration thread. */
|
||||
uint8_t *buf;
|
||||
BlkMigDevState *bmds;
|
||||
int64_t sector;
|
||||
|
@ -62,26 +68,49 @@ typedef struct BlkMigBlock {
|
|||
struct iovec iov;
|
||||
QEMUIOVector qiov;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
|
||||
/* Protected by block migration lock. */
|
||||
int ret;
|
||||
QSIMPLEQ_ENTRY(BlkMigBlock) entry;
|
||||
} BlkMigBlock;
|
||||
|
||||
typedef struct BlkMigState {
|
||||
/* Written during setup phase. Can be read without a lock. */
|
||||
int blk_enable;
|
||||
int shared_base;
|
||||
QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list;
|
||||
int64_t total_sector_sum;
|
||||
|
||||
/* Protected by lock. */
|
||||
QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list;
|
||||
int submitted;
|
||||
int read_done;
|
||||
|
||||
/* Only used by migration thread. Does not need a lock. */
|
||||
int transferred;
|
||||
int64_t total_sector_sum;
|
||||
int prev_progress;
|
||||
int bulk_completed;
|
||||
long double prev_time_offset;
|
||||
|
||||
/* Lock must be taken _inside_ the iothread lock. */
|
||||
QemuMutex lock;
|
||||
} BlkMigState;
|
||||
|
||||
static BlkMigState block_mig_state;
|
||||
|
||||
static void blk_mig_lock(void)
|
||||
{
|
||||
qemu_mutex_lock(&block_mig_state.lock);
|
||||
}
|
||||
|
||||
static void blk_mig_unlock(void)
|
||||
{
|
||||
qemu_mutex_unlock(&block_mig_state.lock);
|
||||
}
|
||||
|
||||
/* Must run outside of the iothread lock during the bulk phase,
|
||||
* or the VM will stall.
|
||||
*/
|
||||
|
||||
static void blk_send(QEMUFile *f, BlkMigBlock * blk)
|
||||
{
|
||||
int len;
|
||||
|
@ -108,9 +137,11 @@ uint64_t blk_mig_bytes_transferred(void)
|
|||
BlkMigDevState *bmds;
|
||||
uint64_t sum = 0;
|
||||
|
||||
blk_mig_lock();
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
sum += bmds->completed_sectors;
|
||||
}
|
||||
blk_mig_unlock();
|
||||
return sum << BDRV_SECTOR_BITS;
|
||||
}
|
||||
|
||||
|
@ -130,6 +161,9 @@ uint64_t blk_mig_bytes_total(void)
|
|||
return sum << BDRV_SECTOR_BITS;
|
||||
}
|
||||
|
||||
|
||||
/* Called with migration lock held. */
|
||||
|
||||
static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
|
||||
{
|
||||
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
|
@ -142,6 +176,8 @@ static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
|
|||
}
|
||||
}
|
||||
|
||||
/* Called with migration lock held. */
|
||||
|
||||
static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num,
|
||||
int nb_sectors, int set)
|
||||
{
|
||||
|
@ -176,23 +212,26 @@ static void alloc_aio_bitmap(BlkMigDevState *bmds)
|
|||
bmds->aio_bitmap = g_malloc0(bitmap_size);
|
||||
}
|
||||
|
||||
/* Never hold migration lock when yielding to the main loop! */
|
||||
|
||||
static void blk_mig_read_cb(void *opaque, int ret)
|
||||
{
|
||||
long double curr_time = qemu_get_clock_ns(rt_clock);
|
||||
BlkMigBlock *blk = opaque;
|
||||
|
||||
blk_mig_lock();
|
||||
blk->ret = ret;
|
||||
|
||||
block_mig_state.prev_time_offset = curr_time;
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
|
||||
bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0);
|
||||
|
||||
block_mig_state.submitted--;
|
||||
block_mig_state.read_done++;
|
||||
assert(block_mig_state.submitted >= 0);
|
||||
blk_mig_unlock();
|
||||
}
|
||||
|
||||
/* Called with no lock taken. */
|
||||
|
||||
static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
{
|
||||
int64_t total_sectors = bmds->total_sectors;
|
||||
|
@ -202,11 +241,13 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
|||
int nr_sectors;
|
||||
|
||||
if (bmds->shared_base) {
|
||||
qemu_mutex_lock_iothread();
|
||||
while (cur_sector < total_sectors &&
|
||||
!bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH,
|
||||
&nr_sectors)) {
|
||||
cur_sector += nr_sectors;
|
||||
}
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
if (cur_sector >= total_sectors) {
|
||||
|
@ -235,26 +276,29 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
|||
blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
|
||||
|
||||
if (block_mig_state.submitted == 0) {
|
||||
block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
|
||||
}
|
||||
blk_mig_lock();
|
||||
block_mig_state.submitted++;
|
||||
blk_mig_unlock();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
|
||||
nr_sectors, blk_mig_read_cb, blk);
|
||||
block_mig_state.submitted++;
|
||||
|
||||
bdrv_reset_dirty(bs, cur_sector, nr_sectors);
|
||||
bmds->cur_sector = cur_sector + nr_sectors;
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
bmds->cur_sector = cur_sector + nr_sectors;
|
||||
return (bmds->cur_sector >= total_sectors);
|
||||
}
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static void set_dirty_tracking(int enable)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
bdrv_set_dirty_tracking(bmds->bs, enable);
|
||||
bdrv_set_dirty_tracking(bmds->bs, enable ? BLOCK_SIZE : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,6 +348,8 @@ static void init_blk_migration(QEMUFile *f)
|
|||
bdrv_iterate(init_blk_migration_it, NULL);
|
||||
}
|
||||
|
||||
/* Called with no lock taken. */
|
||||
|
||||
static int blk_mig_save_bulked_block(QEMUFile *f)
|
||||
{
|
||||
int64_t completed_sector_sum = 0;
|
||||
|
@ -350,6 +396,8 @@ static void blk_mig_reset_dirty_cursor(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
int is_async)
|
||||
{
|
||||
|
@ -360,8 +408,12 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
|||
int ret = -EIO;
|
||||
|
||||
for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
|
||||
blk_mig_lock();
|
||||
if (bmds_aio_inflight(bmds, sector)) {
|
||||
blk_mig_unlock();
|
||||
bdrv_drain_all();
|
||||
} else {
|
||||
blk_mig_unlock();
|
||||
}
|
||||
if (bdrv_get_dirty(bmds->bs, sector)) {
|
||||
|
||||
|
@ -381,14 +433,13 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
|||
blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
|
||||
qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
|
||||
|
||||
if (block_mig_state.submitted == 0) {
|
||||
block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
|
||||
}
|
||||
|
||||
blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
|
||||
nr_sectors, blk_mig_read_cb, blk);
|
||||
|
||||
blk_mig_lock();
|
||||
block_mig_state.submitted++;
|
||||
bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
|
||||
blk_mig_unlock();
|
||||
} else {
|
||||
ret = bdrv_read(bmds->bs, sector, blk->buf, nr_sectors);
|
||||
if (ret < 0) {
|
||||
|
@ -416,7 +467,9 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* return value:
|
||||
/* Called with iothread lock taken.
|
||||
*
|
||||
* return value:
|
||||
* 0: too much data for max_downtime
|
||||
* 1: few enough data for max_downtime
|
||||
*/
|
||||
|
@ -435,6 +488,8 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Called with no locks taken. */
|
||||
|
||||
static int flush_blks(QEMUFile *f)
|
||||
{
|
||||
BlkMigBlock *blk;
|
||||
|
@ -444,6 +499,7 @@ static int flush_blks(QEMUFile *f)
|
|||
__FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
|
||||
block_mig_state.transferred);
|
||||
|
||||
blk_mig_lock();
|
||||
while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
|
||||
if (qemu_file_rate_limit(f)) {
|
||||
break;
|
||||
|
@ -452,9 +508,12 @@ static int flush_blks(QEMUFile *f)
|
|||
ret = blk->ret;
|
||||
break;
|
||||
}
|
||||
blk_send(f, blk);
|
||||
|
||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
|
||||
blk_mig_unlock();
|
||||
blk_send(f, blk);
|
||||
blk_mig_lock();
|
||||
|
||||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
|
||||
|
@ -462,6 +521,7 @@ static int flush_blks(QEMUFile *f)
|
|||
block_mig_state.transferred++;
|
||||
assert(block_mig_state.read_done >= 0);
|
||||
}
|
||||
blk_mig_unlock();
|
||||
|
||||
DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
|
||||
block_mig_state.submitted, block_mig_state.read_done,
|
||||
|
@ -469,6 +529,8 @@ static int flush_blks(QEMUFile *f)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static int64_t get_remaining_dirty(void)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
|
@ -478,9 +540,11 @@ static int64_t get_remaining_dirty(void)
|
|||
dirty += bdrv_get_dirty_count(bmds->bs);
|
||||
}
|
||||
|
||||
return dirty * BLOCK_SIZE;
|
||||
return dirty << BDRV_SECTOR_BITS;
|
||||
}
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static void blk_mig_cleanup(void)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
|
@ -490,6 +554,7 @@ static void blk_mig_cleanup(void)
|
|||
|
||||
set_dirty_tracking(0);
|
||||
|
||||
blk_mig_lock();
|
||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
||||
bdrv_set_in_use(bmds->bs, 0);
|
||||
|
@ -503,6 +568,7 @@ static void blk_mig_cleanup(void)
|
|||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
}
|
||||
blk_mig_unlock();
|
||||
}
|
||||
|
||||
static void block_migration_cancel(void *opaque)
|
||||
|
@ -517,73 +583,78 @@ static int block_save_setup(QEMUFile *f, void *opaque)
|
|||
DPRINTF("Enter save live setup submitted %d transferred %d\n",
|
||||
block_mig_state.submitted, block_mig_state.transferred);
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
init_blk_migration(f);
|
||||
|
||||
/* start track dirty blocks */
|
||||
set_dirty_tracking(1);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
ret = flush_blks(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_mig_reset_dirty_cursor();
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int block_save_iterate(QEMUFile *f, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
int64_t last_ftell = qemu_ftell(f);
|
||||
|
||||
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 */
|
||||
blk_mig_lock();
|
||||
while ((block_mig_state.submitted +
|
||||
block_mig_state.read_done) * BLOCK_SIZE <
|
||||
qemu_file_get_rate_limit(f)) {
|
||||
blk_mig_unlock();
|
||||
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;
|
||||
}
|
||||
ret = 0;
|
||||
} else {
|
||||
/* Always called with iothread lock taken for
|
||||
* simplicity, block_save_complete also calls it.
|
||||
*/
|
||||
qemu_mutex_lock_iothread();
|
||||
ret = blk_mig_save_dirty_block(f, 1);
|
||||
if (ret != 0) {
|
||||
/* no more dirty blocks */
|
||||
break;
|
||||
}
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
blk_mig_lock();
|
||||
if (ret != 0) {
|
||||
/* no more dirty blocks */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
blk_mig_unlock();
|
||||
|
||||
ret = flush_blks(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
return 0;
|
||||
return qemu_ftell(f) - last_ftell;
|
||||
}
|
||||
|
||||
/* Called with iothread lock taken. */
|
||||
|
||||
static int block_save_complete(QEMUFile *f, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
|
@ -593,7 +664,6 @@ static int block_save_complete(QEMUFile *f, void *opaque)
|
|||
|
||||
ret = flush_blks(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -601,16 +671,17 @@ static int block_save_complete(QEMUFile *f, void *opaque)
|
|||
|
||||
/* we know for sure that save bulk is completed and
|
||||
all async read completed */
|
||||
blk_mig_lock();
|
||||
assert(block_mig_state.submitted == 0);
|
||||
blk_mig_unlock();
|
||||
|
||||
do {
|
||||
ret = blk_mig_save_dirty_block(f, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
} while (ret == 0);
|
||||
|
||||
blk_mig_cleanup();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
/* report completion */
|
||||
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
||||
|
||||
|
@ -618,15 +689,30 @@ static int block_save_complete(QEMUFile *f, void *opaque)
|
|||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
||||
blk_mig_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
|
||||
{
|
||||
/* Estimate pending number of bytes to send */
|
||||
uint64_t pending;
|
||||
|
||||
DPRINTF("Enter save live pending %ld\n", get_remaining_dirty());
|
||||
qemu_mutex_lock_iothread();
|
||||
blk_mig_lock();
|
||||
pending = get_remaining_dirty() +
|
||||
block_mig_state.submitted * BLOCK_SIZE +
|
||||
block_mig_state.read_done * BLOCK_SIZE;
|
||||
|
||||
return get_remaining_dirty();
|
||||
/* Report at least one block pending during bulk phase */
|
||||
if (pending == 0 && !block_mig_state.bulk_completed) {
|
||||
pending = BLOCK_SIZE;
|
||||
}
|
||||
blk_mig_unlock();
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
DPRINTF("Enter save live pending %" PRIu64 "\n", pending);
|
||||
return pending;
|
||||
}
|
||||
|
||||
static int block_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
@ -694,7 +780,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
|
|||
(addr == 100) ? '\n' : '\r');
|
||||
fflush(stdout);
|
||||
} else if (!(flags & BLK_MIG_FLAG_EOS)) {
|
||||
fprintf(stderr, "Unknown flags\n");
|
||||
fprintf(stderr, "Unknown block migration flags: %#x\n", flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = qemu_file_get_error(f);
|
||||
|
@ -735,6 +821,7 @@ void blk_mig_init(void)
|
|||
{
|
||||
QSIMPLEQ_INIT(&block_mig_state.bmds_list);
|
||||
QSIMPLEQ_INIT(&block_mig_state.blk_list);
|
||||
qemu_mutex_init(&block_mig_state.lock);
|
||||
|
||||
register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers,
|
||||
&block_mig_state);
|
||||
|
|
341
block.c
341
block.c
|
@ -155,10 +155,6 @@ void bdrv_io_limits_enable(BlockDriverState *bs)
|
|||
{
|
||||
qemu_co_queue_init(&bs->throttled_reqs);
|
||||
bs->block_timer = qemu_new_timer_ns(vm_clock, bdrv_block_timer, bs);
|
||||
bs->slice_time = 5 * BLOCK_IO_SLICE_TIME;
|
||||
bs->slice_start = qemu_get_clock_ns(vm_clock);
|
||||
bs->slice_end = bs->slice_start + bs->slice_time;
|
||||
memset(&bs->io_base, 0, sizeof(bs->io_base));
|
||||
bs->io_limits_enabled = true;
|
||||
}
|
||||
|
||||
|
@ -527,7 +523,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
|
|||
int ret = 0;
|
||||
|
||||
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */
|
||||
if (bs->sg || !bdrv_is_inserted(bs)) {
|
||||
if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) {
|
||||
drv = bdrv_find_format("raw");
|
||||
if (!drv) {
|
||||
ret = -ENOENT;
|
||||
|
@ -584,6 +580,26 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set open flags for a given discard mode
|
||||
*
|
||||
* Return 0 on success, -1 if the discard mode was invalid.
|
||||
*/
|
||||
int bdrv_parse_discard_flags(const char *mode, int *flags)
|
||||
{
|
||||
*flags &= ~BDRV_O_UNMAP;
|
||||
|
||||
if (!strcmp(mode, "off") || !strcmp(mode, "ignore")) {
|
||||
/* do nothing */
|
||||
} else if (!strcmp(mode, "on") || !strcmp(mode, "unmap")) {
|
||||
*flags |= BDRV_O_UNMAP;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set open flags for a given cache mode
|
||||
*
|
||||
|
@ -649,15 +665,18 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
|||
|
||||
/*
|
||||
* Common part for opening disk images and files
|
||||
*
|
||||
* Removes all processed options from *options.
|
||||
*/
|
||||
static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
const char *filename,
|
||||
const char *filename, QDict *options,
|
||||
int flags, BlockDriver *drv)
|
||||
{
|
||||
int ret, open_flags;
|
||||
|
||||
assert(drv != NULL);
|
||||
assert(bs->file == NULL);
|
||||
assert(options == NULL || bs->options != options);
|
||||
|
||||
trace_bdrv_open_common(bs, filename, flags, drv->format_name);
|
||||
|
||||
|
@ -694,7 +713,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
|||
} else {
|
||||
assert(file != NULL);
|
||||
bs->file = file;
|
||||
ret = drv->bdrv_open(bs, open_flags);
|
||||
ret = drv->bdrv_open(bs, options, open_flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -736,7 +755,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
|
|||
}
|
||||
|
||||
bs = bdrv_new("");
|
||||
ret = bdrv_open_common(bs, NULL, filename, flags, drv);
|
||||
ret = bdrv_open_common(bs, NULL, filename, NULL, flags, drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs);
|
||||
return ret;
|
||||
|
@ -772,7 +791,8 @@ int bdrv_open_backing_file(BlockDriverState *bs)
|
|||
/* backing files always opened read-only */
|
||||
back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT);
|
||||
|
||||
ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);
|
||||
ret = bdrv_open(bs->backing_hd, backing_filename, NULL,
|
||||
back_flags, back_drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs->backing_hd);
|
||||
bs->backing_hd = NULL;
|
||||
|
@ -784,15 +804,29 @@ int bdrv_open_backing_file(BlockDriverState *bs)
|
|||
|
||||
/*
|
||||
* Opens a disk image (raw, qcow2, vmdk, ...)
|
||||
*
|
||||
* options is a QDict of options to pass to the block drivers, or NULL for an
|
||||
* empty set of options. The reference to the QDict belongs to the block layer
|
||||
* after the call (even on failure), so if the caller intends to reuse the
|
||||
* dictionary, it needs to use QINCREF() before calling bdrv_open.
|
||||
*/
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
BlockDriver *drv)
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
int flags, BlockDriver *drv)
|
||||
{
|
||||
int ret;
|
||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||
char tmp_filename[PATH_MAX + 1];
|
||||
BlockDriverState *file = NULL;
|
||||
|
||||
/* NULL means an empty set of options */
|
||||
if (options == NULL) {
|
||||
options = qdict_new();
|
||||
}
|
||||
|
||||
bs->options = options;
|
||||
options = qdict_clone_shallow(options);
|
||||
|
||||
/* For snapshot=on, create a temporary qcow2 overlay */
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
BlockDriverState *bs1;
|
||||
int64_t total_size;
|
||||
|
@ -806,10 +840,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|||
|
||||
/* if there is a backing file, use it */
|
||||
bs1 = bdrv_new("");
|
||||
ret = bdrv_open(bs1, filename, 0, drv);
|
||||
ret = bdrv_open(bs1, filename, NULL, 0, drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs1);
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
|
||||
|
||||
|
@ -820,15 +854,17 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|||
|
||||
ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Real path is meaningless for protocols */
|
||||
if (is_protocol)
|
||||
if (is_protocol) {
|
||||
snprintf(backing_filename, sizeof(backing_filename),
|
||||
"%s", filename);
|
||||
else if (!realpath(filename, backing_filename))
|
||||
return -errno;
|
||||
} else if (!realpath(filename, backing_filename)) {
|
||||
ret = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bdrv_qcow2 = bdrv_find_format("qcow2");
|
||||
options = parse_option_parameters("", bdrv_qcow2->create_options, NULL);
|
||||
|
@ -843,7 +879,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|||
ret = bdrv_create(bdrv_qcow2, tmp_filename, options);
|
||||
free_option_parameters(options);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
filename = tmp_filename;
|
||||
|
@ -858,7 +894,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|||
|
||||
ret = bdrv_file_open(&file, filename, bdrv_open_flags(bs, flags));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Find the right image format driver */
|
||||
|
@ -871,7 +907,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|||
}
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, file, filename, flags, drv);
|
||||
ret = bdrv_open_common(bs, file, filename, options, flags, drv);
|
||||
if (ret < 0) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
@ -885,11 +921,22 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|||
if ((flags & BDRV_O_NO_BACKING) == 0) {
|
||||
ret = bdrv_open_backing_file(bs);
|
||||
if (ret < 0) {
|
||||
bdrv_close(bs);
|
||||
return ret;
|
||||
goto close_and_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if any unknown options were used */
|
||||
if (qdict_size(options) != 0) {
|
||||
const QDictEntry *entry = qdict_first(options);
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by "
|
||||
"device '%s' doesn't support the option '%s'",
|
||||
drv->format_name, bs->device_name, entry->key);
|
||||
|
||||
ret = -EINVAL;
|
||||
goto close_and_fail;
|
||||
}
|
||||
QDECREF(options);
|
||||
|
||||
if (!bdrv_key_required(bs)) {
|
||||
bdrv_dev_change_media_cb(bs, true);
|
||||
}
|
||||
|
@ -908,6 +955,15 @@ unlink_and_fail:
|
|||
if (bs->is_temporary) {
|
||||
unlink(filename);
|
||||
}
|
||||
fail:
|
||||
QDECREF(bs->options);
|
||||
QDECREF(options);
|
||||
bs->options = NULL;
|
||||
return ret;
|
||||
|
||||
close_and_fail:
|
||||
bdrv_close(bs);
|
||||
QDECREF(options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1177,6 +1233,8 @@ void bdrv_close(BlockDriverState *bs)
|
|||
bs->valid_key = 0;
|
||||
bs->sg = 0;
|
||||
bs->growable = 0;
|
||||
QDECREF(bs->options);
|
||||
bs->options = NULL;
|
||||
|
||||
if (bs->file != NULL) {
|
||||
bdrv_delete(bs->file);
|
||||
|
@ -1290,7 +1348,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
|||
bs_dest->iostatus = bs_src->iostatus;
|
||||
|
||||
/* dirty bitmap */
|
||||
bs_dest->dirty_count = bs_src->dirty_count;
|
||||
bs_dest->dirty_bitmap = bs_src->dirty_bitmap;
|
||||
|
||||
/* job */
|
||||
|
@ -1625,9 +1682,11 @@ int bdrv_commit_all(void)
|
|||
BlockDriverState *bs;
|
||||
|
||||
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
||||
int ret = bdrv_commit(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (bs->drv && bs->backing_hd) {
|
||||
int ret = bdrv_commit(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -1678,10 +1737,10 @@ static void tracked_request_begin(BdrvTrackedRequest *req,
|
|||
/**
|
||||
* Round a region to cluster boundaries
|
||||
*/
|
||||
static void round_to_clusters(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t *cluster_sector_num,
|
||||
int *cluster_nb_sectors)
|
||||
void bdrv_round_to_clusters(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t *cluster_sector_num,
|
||||
int *cluster_nb_sectors)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
|
||||
|
@ -1723,8 +1782,8 @@ static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs,
|
|||
* CoR read and write operations are atomic and guest writes cannot
|
||||
* interleave between them.
|
||||
*/
|
||||
round_to_clusters(bs, sector_num, nb_sectors,
|
||||
&cluster_sector_num, &cluster_nb_sectors);
|
||||
bdrv_round_to_clusters(bs, sector_num, nb_sectors,
|
||||
&cluster_sector_num, &cluster_nb_sectors);
|
||||
|
||||
do {
|
||||
retry = false;
|
||||
|
@ -2039,36 +2098,6 @@ int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
||||
|
||||
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int dirty)
|
||||
{
|
||||
int64_t start, end;
|
||||
unsigned long val, idx, bit;
|
||||
|
||||
start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
|
||||
for (; start <= end; start++) {
|
||||
idx = start / BITS_PER_LONG;
|
||||
bit = start % BITS_PER_LONG;
|
||||
val = bs->dirty_bitmap[idx];
|
||||
if (dirty) {
|
||||
if (!(val & (1UL << bit))) {
|
||||
bs->dirty_count++;
|
||||
val |= 1UL << bit;
|
||||
}
|
||||
} else {
|
||||
if (val & (1UL << bit)) {
|
||||
bs->dirty_count--;
|
||||
val &= ~(1UL << bit);
|
||||
}
|
||||
}
|
||||
bs->dirty_bitmap[idx] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return < 0 if error. Important errors are:
|
||||
-EIO generic I/O error (may happen for all errors)
|
||||
-ENOMEDIUM No media inserted.
|
||||
|
@ -2220,8 +2249,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
|
|||
/* Cover entire cluster so no additional backing file I/O is required when
|
||||
* allocating cluster in the image file.
|
||||
*/
|
||||
round_to_clusters(bs, sector_num, nb_sectors,
|
||||
&cluster_sector_num, &cluster_nb_sectors);
|
||||
bdrv_round_to_clusters(bs, sector_num, nb_sectors,
|
||||
&cluster_sector_num, &cluster_nb_sectors);
|
||||
|
||||
trace_bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors,
|
||||
cluster_sector_num, cluster_nb_sectors);
|
||||
|
@ -2462,6 +2491,10 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
|
|||
return -EACCES;
|
||||
if (bdrv_in_use(bs))
|
||||
return -EBUSY;
|
||||
|
||||
/* There better not be any in-flight IOs when we truncate the device. */
|
||||
bdrv_drain_all();
|
||||
|
||||
ret = drv->bdrv_truncate(bs, offset);
|
||||
if (ret == 0) {
|
||||
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
||||
|
@ -2716,6 +2749,7 @@ int bdrv_has_zero_init(BlockDriverState *bs)
|
|||
|
||||
typedef struct BdrvCoIsAllocatedData {
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *base;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
int *pnum;
|
||||
|
@ -2835,7 +2869,9 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
|||
*
|
||||
* [sector_num+x, nr_sectors] allocated.
|
||||
*/
|
||||
if (n > pnum_inter) {
|
||||
if (n > pnum_inter &&
|
||||
(intermediate == top ||
|
||||
sector_num + pnum_inter < intermediate->total_sectors)) {
|
||||
n = pnum_inter;
|
||||
}
|
||||
|
||||
|
@ -2846,6 +2882,44 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Coroutine wrapper for bdrv_is_allocated_above() */
|
||||
static void coroutine_fn bdrv_is_allocated_above_co_entry(void *opaque)
|
||||
{
|
||||
BdrvCoIsAllocatedData *data = opaque;
|
||||
BlockDriverState *top = data->bs;
|
||||
BlockDriverState *base = data->base;
|
||||
|
||||
data->ret = bdrv_co_is_allocated_above(top, base, data->sector_num,
|
||||
data->nb_sectors, data->pnum);
|
||||
data->done = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronous wrapper around bdrv_co_is_allocated_above().
|
||||
*
|
||||
* See bdrv_co_is_allocated_above() for details.
|
||||
*/
|
||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
Coroutine *co;
|
||||
BdrvCoIsAllocatedData data = {
|
||||
.bs = top,
|
||||
.base = base,
|
||||
.sector_num = sector_num,
|
||||
.nb_sectors = nb_sectors,
|
||||
.pnum = pnum,
|
||||
.done = false,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(bdrv_is_allocated_above_co_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
while (!data.done) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
return data.ret;
|
||||
}
|
||||
|
||||
BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
||||
{
|
||||
BlockInfo *info = g_malloc0(sizeof(*info));
|
||||
|
@ -2867,8 +2941,9 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
|||
if (bs->dirty_bitmap) {
|
||||
info->has_dirty = true;
|
||||
info->dirty = g_malloc0(sizeof(*info->dirty));
|
||||
info->dirty->count = bdrv_get_dirty_count(bs) *
|
||||
BDRV_SECTORS_PER_DIRTY_CHUNK * BDRV_SECTOR_SIZE;
|
||||
info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE;
|
||||
info->dirty->granularity =
|
||||
((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap));
|
||||
}
|
||||
|
||||
if (bs->drv) {
|
||||
|
@ -3157,7 +3232,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||
if (bs->file) {
|
||||
drv->bdrv_close(bs);
|
||||
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
||||
open_ret = drv->bdrv_open(bs, bs->open_flags);
|
||||
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags);
|
||||
if (open_ret < 0) {
|
||||
bdrv_delete(bs->file);
|
||||
bs->drv = NULL;
|
||||
|
@ -3338,11 +3413,7 @@ char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
|||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
|
||||
{
|
||||
char buf1[128], date_buf[128], clock_buf[128];
|
||||
#ifdef _WIN32
|
||||
struct tm *ptm;
|
||||
#else
|
||||
struct tm tm;
|
||||
#endif
|
||||
time_t ti;
|
||||
int64_t secs;
|
||||
|
||||
|
@ -3352,15 +3423,9 @@ char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
|
|||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||
} else {
|
||||
ti = sn->date_sec;
|
||||
#ifdef _WIN32
|
||||
ptm = localtime(&ti);
|
||||
strftime(date_buf, sizeof(date_buf),
|
||||
"%Y-%m-%d %H:%M:%S", ptm);
|
||||
#else
|
||||
localtime_r(&ti, &tm);
|
||||
strftime(date_buf, sizeof(date_buf),
|
||||
"%Y-%m-%d %H:%M:%S", &tm);
|
||||
#endif
|
||||
secs = sn->vm_clock_nsec / 1000000000;
|
||||
snprintf(clock_buf, sizeof(clock_buf),
|
||||
"%02d:%02d:%02d.%03d",
|
||||
|
@ -4184,7 +4249,18 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||
return -EIO;
|
||||
} else if (bs->read_only) {
|
||||
return -EROFS;
|
||||
} else if (bs->drv->bdrv_co_discard) {
|
||||
}
|
||||
|
||||
if (bs->dirty_bitmap) {
|
||||
bdrv_reset_dirty(bs, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
/* Do nothing if disabled. */
|
||||
if (!(bs->open_flags & BDRV_O_UNMAP)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bs->drv->bdrv_co_discard) {
|
||||
return bs->drv->bdrv_co_discard(bs, sector_num, nb_sectors);
|
||||
} else if (bs->drv->bdrv_aio_discard) {
|
||||
BlockDriverAIOCB *acb;
|
||||
|
@ -4323,22 +4399,36 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size)
|
|||
return qemu_memalign((bs && bs->buffer_alignment) ? bs->buffer_alignment : 512, size);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
|
||||
/*
|
||||
* Check if all memory in this vector is sector aligned.
|
||||
*/
|
||||
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov; i++) {
|
||||
if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bdrv_set_dirty_tracking(BlockDriverState *bs, int granularity)
|
||||
{
|
||||
int64_t bitmap_size;
|
||||
|
||||
bs->dirty_count = 0;
|
||||
if (enable) {
|
||||
if (!bs->dirty_bitmap) {
|
||||
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
|
||||
BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG - 1;
|
||||
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
|
||||
assert((granularity & (granularity - 1)) == 0);
|
||||
|
||||
bs->dirty_bitmap = g_new0(unsigned long, bitmap_size);
|
||||
}
|
||||
if (granularity) {
|
||||
granularity >>= BDRV_SECTOR_BITS;
|
||||
assert(!bs->dirty_bitmap);
|
||||
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS);
|
||||
bs->dirty_bitmap = hbitmap_alloc(bitmap_size, ffs(granularity) - 1);
|
||||
} else {
|
||||
if (bs->dirty_bitmap) {
|
||||
g_free(bs->dirty_bitmap);
|
||||
hbitmap_free(bs->dirty_bitmap);
|
||||
bs->dirty_bitmap = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -4346,67 +4436,37 @@ void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
|
|||
|
||||
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
|
||||
{
|
||||
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
|
||||
if (bs->dirty_bitmap &&
|
||||
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
|
||||
return !!(bs->dirty_bitmap[chunk / BITS_PER_LONG] &
|
||||
(1UL << (chunk % BITS_PER_LONG)));
|
||||
if (bs->dirty_bitmap) {
|
||||
return hbitmap_get(bs->dirty_bitmap, sector);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector)
|
||||
void bdrv_dirty_iter_init(BlockDriverState *bs, HBitmapIter *hbi)
|
||||
{
|
||||
int64_t chunk;
|
||||
int bit, elem;
|
||||
|
||||
/* Avoid an infinite loop. */
|
||||
assert(bs->dirty_count > 0);
|
||||
|
||||
sector = (sector | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1;
|
||||
chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(bs->dirty_bitmap[0]) * 8 != BITS_PER_LONG);
|
||||
elem = chunk / BITS_PER_LONG;
|
||||
bit = chunk % BITS_PER_LONG;
|
||||
for (;;) {
|
||||
if (sector >= bs->total_sectors) {
|
||||
sector = 0;
|
||||
bit = elem = 0;
|
||||
}
|
||||
if (bit == 0 && bs->dirty_bitmap[elem] == 0) {
|
||||
sector += BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
|
||||
elem++;
|
||||
} else {
|
||||
if (bs->dirty_bitmap[elem] & (1UL << bit)) {
|
||||
return sector;
|
||||
}
|
||||
sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
if (++bit == BITS_PER_LONG) {
|
||||
bit = 0;
|
||||
elem++;
|
||||
}
|
||||
}
|
||||
}
|
||||
hbitmap_iter_init(hbi, bs->dirty_bitmap, 0);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors)
|
||||
{
|
||||
set_dirty_bitmap(bs, cur_sector, nr_sectors, 1);
|
||||
hbitmap_set(bs->dirty_bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors)
|
||||
{
|
||||
set_dirty_bitmap(bs, cur_sector, nr_sectors, 0);
|
||||
hbitmap_reset(bs->dirty_bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs)
|
||||
{
|
||||
return bs->dirty_count;
|
||||
if (bs->dirty_bitmap) {
|
||||
return hbitmap_count(bs->dirty_bitmap);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
||||
|
@ -4483,7 +4543,8 @@ bdrv_acct_done(BlockDriverState *bs, BlockAcctCookie *cookie)
|
|||
|
||||
void bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags, Error **errp)
|
||||
char *options, uint64_t img_size, int flags,
|
||||
Error **errp, bool quiet)
|
||||
{
|
||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||
QEMUOptionParameter *backing_fmt, *backing_file, *size;
|
||||
|
@ -4575,7 +4636,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||
|
||||
bs = bdrv_new("");
|
||||
|
||||
ret = bdrv_open(bs, backing_file->value.s, back_flags, backing_drv);
|
||||
ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags,
|
||||
backing_drv);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not open '%s'",
|
||||
backing_file->value.s);
|
||||
|
@ -4592,10 +4654,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||
}
|
||||
}
|
||||
|
||||
printf("Formatting '%s', fmt=%s ", filename, fmt);
|
||||
print_option_parameters(param);
|
||||
puts("");
|
||||
|
||||
if (!quiet) {
|
||||
printf("Formatting '%s', fmt=%s ", filename, fmt);
|
||||
print_option_parameters(param);
|
||||
puts("");
|
||||
}
|
||||
ret = bdrv_create(drv, filename, param);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOTSUP) {
|
||||
|
@ -4618,3 +4681,9 @@ out:
|
|||
bdrv_delete(bs);
|
||||
}
|
||||
}
|
||||
|
||||
AioContext *bdrv_get_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
/* Currently BlockDriverState always uses the main loop AioContext */
|
||||
return qemu_get_aio_context();
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
|
||||
/* Open the test file */
|
||||
s->test_file = bdrv_new("");
|
||||
ret = bdrv_open(s->test_file, filename, flags, NULL);
|
||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(s->test_file);
|
||||
s->test_file = NULL;
|
||||
|
|
|
@ -108,17 +108,19 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_open(BlockDriverState *bs, int flags)
|
||||
static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int i;
|
||||
struct bochs_header bochs;
|
||||
struct bochs_header_v1 header_v1;
|
||||
int ret;
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) {
|
||||
goto fail;
|
||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (strcmp(bochs.magic, HEADER_MAGIC) ||
|
||||
|
@ -126,7 +128,7 @@ static int bochs_open(BlockDriverState *bs, int flags)
|
|||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||
goto fail;
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(bochs.version) == HEADER_V1) {
|
||||
|
@ -138,9 +140,13 @@ static int bochs_open(BlockDriverState *bs, int flags)
|
|||
|
||||
s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
|
||||
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||
if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||
s->catalog_size * 4) != s->catalog_size * 4)
|
||||
goto fail;
|
||||
|
||||
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||
s->catalog_size * 4);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->catalog_size; i++)
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
|
||||
|
@ -153,8 +159,10 @@ static int bochs_open(BlockDriverState *bs, int flags)
|
|||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
|
||||
fail:
|
||||
g_free(s->catalog_bitmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
|
|
|
@ -53,31 +53,36 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_open(BlockDriverState *bs, int flags)
|
||||
static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
||||
int ret;
|
||||
|
||||
bs->read_only = 1;
|
||||
|
||||
/* read header */
|
||||
if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) {
|
||||
goto cloop_close;
|
||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
s->block_size = be32_to_cpu(s->block_size);
|
||||
|
||||
if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) {
|
||||
goto cloop_close;
|
||||
ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
s->n_blocks = be32_to_cpu(s->n_blocks);
|
||||
|
||||
/* read offsets */
|
||||
offsets_size = s->n_blocks * sizeof(uint64_t);
|
||||
s->offsets = g_malloc(offsets_size);
|
||||
if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) <
|
||||
offsets_size) {
|
||||
goto cloop_close;
|
||||
|
||||
ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(i=0;i<s->n_blocks;i++) {
|
||||
s->offsets[i] = be64_to_cpu(s->offsets[i]);
|
||||
if (i > 0) {
|
||||
|
@ -92,7 +97,8 @@ static int cloop_open(BlockDriverState *bs, int flags)
|
|||
s->compressed_block = g_malloc(max_compressed_block_size + 1);
|
||||
s->uncompressed_block = g_malloc(s->block_size);
|
||||
if (inflateInit(&s->zstream) != Z_OK) {
|
||||
goto cloop_close;
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
s->current_block = s->n_blocks;
|
||||
|
||||
|
@ -101,8 +107,11 @@ static int cloop_open(BlockDriverState *bs, int flags)
|
|||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
|
||||
cloop_close:
|
||||
return -1;
|
||||
fail:
|
||||
g_free(s->offsets);
|
||||
g_free(s->compressed_block);
|
||||
g_free(s->uncompressed_block);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int cloop_read_block(BlockDriverState *bs, int block_num)
|
||||
|
|
|
@ -65,7 +65,7 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
BlockDriverState *active = s->active;
|
||||
BlockDriverState *top = s->top;
|
||||
BlockDriverState *base = s->base;
|
||||
BlockDriverState *overlay_bs = NULL;
|
||||
BlockDriverState *overlay_bs;
|
||||
int64_t sector_num, end;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
|
@ -92,8 +92,6 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
}
|
||||
}
|
||||
|
||||
overlay_bs = bdrv_find_overlay(active, top);
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE);
|
||||
|
||||
|
@ -156,7 +154,8 @@ exit_restore_reopen:
|
|||
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)) {
|
||||
overlay_bs = bdrv_find_overlay(active, top);
|
||||
if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
||||
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, int flags)
|
||||
static int cow_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
struct cow_header_v2 cow_header;
|
||||
|
@ -73,7 +73,7 @@ static int cow_open(BlockDriverState *bs, int flags)
|
|||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
15
block/curl.c
15
block/curl.c
|
@ -34,6 +34,10 @@
|
|||
#define DPRINTF(fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
|
||||
CURLPROTO_FTP | CURLPROTO_FTPS | \
|
||||
CURLPROTO_TFTP)
|
||||
|
||||
#define CURL_NUM_STATES 8
|
||||
#define CURL_NUM_ACB 8
|
||||
#define SECTOR_SIZE 512
|
||||
|
@ -302,6 +306,17 @@ static CURLState *curl_init_state(BDRVCURLState *s)
|
|||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
||||
|
||||
/* Restrict supported protocols to avoid security issues in the more
|
||||
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
||||
* CVE-2013-0249.
|
||||
*
|
||||
* Restricting protocols is only supported from 7.19.4 upwards.
|
||||
*/
|
||||
#if LIBCURL_VERSION_NUM >= 0x071304
|
||||
curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS);
|
||||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
|
||||
#endif
|
||||
|
|
153
block/dmg.c
153
block/dmg.c
|
@ -57,29 +57,42 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static off_t read_off(BlockDriverState *bs, int64_t offset)
|
||||
static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result)
|
||||
{
|
||||
uint64_t buffer;
|
||||
if (bdrv_pread(bs->file, offset, &buffer, 8) < 8)
|
||||
return 0;
|
||||
return be64_to_cpu(buffer);
|
||||
uint64_t buffer;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &buffer, 8);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*result = be64_to_cpu(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static off_t read_uint32(BlockDriverState *bs, int64_t offset)
|
||||
static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
|
||||
{
|
||||
uint32_t buffer;
|
||||
if (bdrv_pread(bs->file, offset, &buffer, 4) < 4)
|
||||
return 0;
|
||||
return be32_to_cpu(buffer);
|
||||
uint32_t buffer;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &buffer, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*result = be32_to_cpu(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmg_open(BlockDriverState *bs, int flags)
|
||||
static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
off_t info_begin,info_end,last_in_offset,last_out_offset;
|
||||
uint32_t count;
|
||||
uint64_t info_begin,info_end,last_in_offset,last_out_offset;
|
||||
uint32_t count, tmp;
|
||||
uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
|
||||
int64_t offset;
|
||||
int ret;
|
||||
|
||||
bs->read_only = 1;
|
||||
s->n_chunks = 0;
|
||||
|
@ -88,21 +101,32 @@ static int dmg_open(BlockDriverState *bs, int flags)
|
|||
/* read offset of info blocks */
|
||||
offset = bdrv_getlength(bs->file);
|
||||
if (offset < 0) {
|
||||
ret = offset;
|
||||
goto fail;
|
||||
}
|
||||
offset -= 0x1d8;
|
||||
|
||||
info_begin = read_off(bs, offset);
|
||||
if (info_begin == 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (read_uint32(bs, info_begin) != 0x100) {
|
||||
ret = read_uint64(bs, offset, &info_begin);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
} else if (info_begin == 0) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
count = read_uint32(bs, info_begin + 4);
|
||||
if (count == 0) {
|
||||
ret = read_uint32(bs, info_begin, &tmp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
} else if (tmp != 0x100) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = read_uint32(bs, info_begin + 4, &count);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
} else if (count == 0) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
info_end = info_begin + count;
|
||||
|
@ -114,12 +138,20 @@ static int dmg_open(BlockDriverState *bs, int flags)
|
|||
while (offset < info_end) {
|
||||
uint32_t type;
|
||||
|
||||
count = read_uint32(bs, offset);
|
||||
if(count==0)
|
||||
goto fail;
|
||||
ret = read_uint32(bs, offset, &count);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
} else if (count == 0) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
offset += 4;
|
||||
|
||||
type = read_uint32(bs, offset);
|
||||
ret = read_uint32(bs, offset, &type);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (type == 0x6d697368 && count >= 244) {
|
||||
int new_size, chunk_count;
|
||||
|
||||
|
@ -134,8 +166,11 @@ static int dmg_open(BlockDriverState *bs, int flags)
|
|||
s->sectors = g_realloc(s->sectors, new_size);
|
||||
s->sectorcounts = g_realloc(s->sectorcounts, new_size);
|
||||
|
||||
for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
|
||||
s->types[i] = read_uint32(bs, offset);
|
||||
for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) {
|
||||
ret = read_uint32(bs, offset, &s->types[i]);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += 4;
|
||||
if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
|
||||
if(s->types[i]==0xffffffff) {
|
||||
|
@ -149,17 +184,31 @@ static int dmg_open(BlockDriverState *bs, int flags)
|
|||
}
|
||||
offset += 4;
|
||||
|
||||
s->sectors[i] = last_out_offset+read_off(bs, offset);
|
||||
offset += 8;
|
||||
ret = read_uint64(bs, offset, &s->sectors[i]);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
s->sectors[i] += last_out_offset;
|
||||
offset += 8;
|
||||
|
||||
s->sectorcounts[i] = read_off(bs, offset);
|
||||
offset += 8;
|
||||
ret = read_uint64(bs, offset, &s->sectorcounts[i]);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += 8;
|
||||
|
||||
s->offsets[i] = last_in_offset+read_off(bs, offset);
|
||||
offset += 8;
|
||||
ret = read_uint64(bs, offset, &s->offsets[i]);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
s->offsets[i] += last_in_offset;
|
||||
offset += 8;
|
||||
|
||||
s->lengths[i] = read_off(bs, offset);
|
||||
offset += 8;
|
||||
ret = read_uint64(bs, offset, &s->lengths[i]);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += 8;
|
||||
|
||||
if(s->lengths[i]>max_compressed_size)
|
||||
max_compressed_size = s->lengths[i];
|
||||
|
@ -173,15 +222,25 @@ static int dmg_open(BlockDriverState *bs, int flags)
|
|||
/* initialize zlib engine */
|
||||
s->compressed_chunk = g_malloc(max_compressed_size+1);
|
||||
s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk);
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto fail;
|
||||
if(inflateInit(&s->zstream) != Z_OK) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->current_chunk = s->n_chunks;
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
g_free(s->types);
|
||||
g_free(s->offsets);
|
||||
g_free(s->lengths);
|
||||
g_free(s->sectors);
|
||||
g_free(s->sectorcounts);
|
||||
g_free(s->compressed_chunk);
|
||||
g_free(s->uncompressed_chunk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||
|
@ -296,15 +355,15 @@ static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num,
|
|||
static void dmg_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
if(s->n_chunks>0) {
|
||||
free(s->types);
|
||||
free(s->offsets);
|
||||
free(s->lengths);
|
||||
free(s->sectors);
|
||||
free(s->sectorcounts);
|
||||
}
|
||||
free(s->compressed_chunk);
|
||||
free(s->uncompressed_chunk);
|
||||
|
||||
g_free(s->types);
|
||||
g_free(s->offsets);
|
||||
g_free(s->lengths);
|
||||
g_free(s->sectors);
|
||||
g_free(s->sectorcounts);
|
||||
g_free(s->compressed_chunk);
|
||||
g_free(s->uncompressed_chunk);
|
||||
|
||||
inflateEnd(&s->zstream);
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
|
|||
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,
|
||||
"volume=%s image=%s transport=%s", gconf->server, gconf->port,
|
||||
gconf->volname, gconf->image, gconf->transport);
|
||||
goto out;
|
||||
}
|
||||
|
|
624
block/iscsi.c
624
block/iscsi.c
|
@ -48,6 +48,7 @@ typedef struct IscsiLun {
|
|||
int block_size;
|
||||
uint64_t num_blocks;
|
||||
int events;
|
||||
QEMUTimer *nop_timer;
|
||||
} IscsiLun;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
|
@ -59,13 +60,20 @@ typedef struct IscsiAIOCB {
|
|||
uint8_t *buf;
|
||||
int status;
|
||||
int canceled;
|
||||
int retries;
|
||||
size_t read_size;
|
||||
size_t read_offset;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
#ifdef __linux__
|
||||
sg_io_hdr_t *ioh;
|
||||
#endif
|
||||
} IscsiAIOCB;
|
||||
|
||||
#define NOP_INTERVAL 5000
|
||||
#define MAX_NOP_FAILURES 3
|
||||
#define ISCSI_CMD_RETRIES 5
|
||||
|
||||
static void
|
||||
iscsi_bh_cb(void *p)
|
||||
{
|
||||
|
@ -73,6 +81,9 @@ iscsi_bh_cb(void *p)
|
|||
|
||||
qemu_bh_delete(acb->bh);
|
||||
|
||||
g_free(acb->buf);
|
||||
acb->buf = NULL;
|
||||
|
||||
if (acb->canceled == 0) {
|
||||
acb->common.cb(acb->common.opaque, acb->status);
|
||||
}
|
||||
|
@ -184,6 +195,8 @@ iscsi_process_write(void *arg)
|
|||
iscsi_set_events(iscsilun);
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_aio_writev_acb(IscsiAIOCB *acb);
|
||||
|
||||
static void
|
||||
iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
|
||||
|
@ -194,13 +207,26 @@ iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
|
|||
trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled);
|
||||
|
||||
g_free(acb->buf);
|
||||
acb->buf = NULL;
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
if (status != 0) {
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& acb->retries-- > 0) {
|
||||
if (acb->task != NULL) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
if (iscsi_aio_writev_acb(acb) == 0) {
|
||||
iscsi_set_events(acb->iscsilun);
|
||||
return;
|
||||
}
|
||||
}
|
||||
error_report("Failed to write16 data to iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
|
@ -214,6 +240,80 @@ static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
|
|||
return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_aio_writev_acb(IscsiAIOCB *acb)
|
||||
{
|
||||
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
|
||||
size_t size;
|
||||
uint32_t num_sectors;
|
||||
uint64_t lba;
|
||||
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||
struct iscsi_data data;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
acb->buf = NULL;
|
||||
|
||||
/* this will allow us to get rid of 'buf' completely */
|
||||
size = acb->nb_sectors * BDRV_SECTOR_SIZE;
|
||||
|
||||
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||
data.size = MIN(size, acb->qiov->size);
|
||||
|
||||
/* if the iovec only contains one buffer we can pass it directly */
|
||||
if (acb->qiov->niov == 1) {
|
||||
data.data = acb->qiov->iov[0].iov_base;
|
||||
} else {
|
||||
acb->buf = g_malloc(data.size);
|
||||
qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size);
|
||||
data.data = acb->buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi WRITE16 "
|
||||
"command. %s", iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
||||
acb->task->xfer_dir = SCSI_XFER_WRITE;
|
||||
acb->task->cdb_size = 16;
|
||||
acb->task->cdb[0] = 0x8a;
|
||||
lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
|
||||
*(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
|
||||
*(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
|
||||
num_sectors = size / acb->iscsilun->block_size;
|
||||
*(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
|
||||
acb->task->expxferlen = size;
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||
ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
|
||||
iscsi_aio_write16_cb,
|
||||
NULL,
|
||||
acb);
|
||||
#else
|
||||
ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
|
||||
iscsi_aio_write16_cb,
|
||||
&data,
|
||||
acb);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
g_free(acb->buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||
scsi_task_set_iov_out(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
|
@ -221,66 +321,32 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
|||
void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
size_t size;
|
||||
uint32_t num_sectors;
|
||||
uint64_t lba;
|
||||
struct iscsi_data data;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->qiov = qiov;
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->qiov = qiov;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->sector_num = sector_num;
|
||||
acb->retries = ISCSI_CMD_RETRIES;
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
/* XXX we should pass the iovec to write16 to avoid the extra copy */
|
||||
/* this will allow us to get rid of 'buf' completely */
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
acb->buf = g_malloc(size);
|
||||
qemu_iovec_to_buf(acb->qiov, 0, acb->buf, size);
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi WRITE16 "
|
||||
"command. %s", iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
||||
acb->task->xfer_dir = SCSI_XFER_WRITE;
|
||||
acb->task->cdb_size = 16;
|
||||
acb->task->cdb[0] = 0x8a;
|
||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
*(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
|
||||
*(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
|
||||
num_sectors = size / iscsilun->block_size;
|
||||
*(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
|
||||
acb->task->expxferlen = size;
|
||||
|
||||
data.data = acb->buf;
|
||||
data.size = size;
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
|
||||
iscsi_aio_write16_cb,
|
||||
&data,
|
||||
acb) != 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
g_free(acb->buf);
|
||||
if (iscsi_aio_writev_acb(acb) != 0) {
|
||||
if (acb->task) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_aio_readv_acb(IscsiAIOCB *acb);
|
||||
|
||||
static void
|
||||
iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
|
@ -295,6 +361,18 @@ iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
|
|||
|
||||
acb->status = 0;
|
||||
if (status != 0) {
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& acb->retries-- > 0) {
|
||||
if (acb->task != NULL) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
if (iscsi_aio_readv_acb(acb) == 0) {
|
||||
iscsi_set_events(acb->iscsilun);
|
||||
return;
|
||||
}
|
||||
}
|
||||
error_report("Failed to read16 data from iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
|
@ -303,32 +381,20 @@ iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
|
|||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static int
|
||||
iscsi_aio_readv_acb(IscsiAIOCB *acb)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
size_t qemu_read_size;
|
||||
int i;
|
||||
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
|
||||
uint64_t lba;
|
||||
uint32_t num_sectors;
|
||||
|
||||
qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->qiov = qiov;
|
||||
int ret;
|
||||
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||
int i;
|
||||
#endif
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
acb->read_size = qemu_read_size;
|
||||
acb->buf = NULL;
|
||||
|
||||
/* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
|
||||
|
@ -336,30 +402,29 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
* data.
|
||||
*/
|
||||
acb->read_offset = 0;
|
||||
if (iscsilun->block_size > BDRV_SECTOR_SIZE) {
|
||||
uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num;
|
||||
if (acb->iscsilun->block_size > BDRV_SECTOR_SIZE) {
|
||||
uint64_t bdrv_offset = BDRV_SECTOR_SIZE * acb->sector_num;
|
||||
|
||||
acb->read_offset = bdrv_offset % iscsilun->block_size;
|
||||
acb->read_offset = bdrv_offset % acb->iscsilun->block_size;
|
||||
}
|
||||
|
||||
num_sectors = (qemu_read_size + iscsilun->block_size
|
||||
num_sectors = (acb->read_size + acb->iscsilun->block_size
|
||||
+ acb->read_offset - 1)
|
||||
/ iscsilun->block_size;
|
||||
/ acb->iscsilun->block_size;
|
||||
|
||||
acb->task = malloc(sizeof(struct scsi_task));
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi READ16 "
|
||||
"command. %s", iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
||||
acb->task->xfer_dir = SCSI_XFER_READ;
|
||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
acb->task->expxferlen = qemu_read_size;
|
||||
lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
|
||||
acb->task->expxferlen = acb->read_size;
|
||||
|
||||
switch (iscsilun->type) {
|
||||
switch (acb->iscsilun->type) {
|
||||
case TYPE_DISK:
|
||||
acb->task->cdb_size = 16;
|
||||
acb->task->cdb[0] = 0x88;
|
||||
|
@ -375,26 +440,59 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
break;
|
||||
}
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
|
||||
iscsi_aio_read16_cb,
|
||||
NULL,
|
||||
acb) != 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
|
||||
iscsi_aio_read16_cb,
|
||||
NULL,
|
||||
acb);
|
||||
if (ret != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||
scsi_task_set_iov_in(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
|
||||
#else
|
||||
for (i = 0; i < acb->qiov->niov; i++) {
|
||||
scsi_task_add_data_in_buffer(acb->task,
|
||||
acb->qiov->iov[i].iov_len,
|
||||
acb->qiov->iov[i].iov_base);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->sector_num = sector_num;
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->qiov = qiov;
|
||||
acb->read_size = BDRV_SECTOR_SIZE * (size_t)acb->nb_sectors;
|
||||
acb->retries = ISCSI_CMD_RETRIES;
|
||||
|
||||
if (iscsi_aio_readv_acb(acb) != 0) {
|
||||
if (acb->task) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_aio_flush_acb(IscsiAIOCB *acb);
|
||||
|
||||
static void
|
||||
iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
|
||||
|
@ -407,7 +505,19 @@ iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
|
|||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
if (status != 0) {
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& acb->retries-- > 0) {
|
||||
if (acb->task != NULL) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
if (iscsi_aio_flush_acb(acb) == 0) {
|
||||
iscsi_set_events(acb->iscsilun);
|
||||
return;
|
||||
}
|
||||
}
|
||||
error_report("Failed to sync10 data on iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
|
@ -416,28 +526,43 @@ iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
|
|||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
static int
|
||||
iscsi_aio_flush_acb(IscsiAIOCB *acb)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
|
||||
|
||||
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->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun,
|
||||
acb->task = iscsi_synchronizecache10_task(iscsi, acb->iscsilun->lun,
|
||||
0, 0, 0, 0,
|
||||
iscsi_synccache10_cb,
|
||||
acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send synchronizecache10 command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->retries = ISCSI_CMD_RETRIES;
|
||||
|
||||
if (iscsi_aio_flush_acb(acb) != 0) {
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -447,6 +572,8 @@ iscsi_aio_flush(BlockDriverState *bs,
|
|||
return &acb->common;
|
||||
}
|
||||
|
||||
static int iscsi_aio_discard_acb(IscsiAIOCB *acb);
|
||||
|
||||
static void
|
||||
iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
|
@ -458,7 +585,19 @@ iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
|
|||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
if (status != 0) {
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& acb->retries-- > 0) {
|
||||
if (acb->task != NULL) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
if (iscsi_aio_discard_acb(acb) == 0) {
|
||||
iscsi_set_events(acb->iscsilun);
|
||||
return;
|
||||
}
|
||||
}
|
||||
error_report("Failed to unmap data on iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
|
@ -467,33 +606,50 @@ iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
|
|||
iscsi_schedule_bh(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
static int iscsi_aio_discard_acb(IscsiAIOCB *acb) {
|
||||
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
|
||||
struct unmap_list list[1];
|
||||
|
||||
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;
|
||||
|
||||
list[0].lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size;
|
||||
list[0].lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
|
||||
list[0].num = acb->nb_sectors * BDRV_SECTOR_SIZE / acb->iscsilun->block_size;
|
||||
|
||||
acb->task = iscsi_unmap_task(iscsi, iscsilun->lun,
|
||||
acb->task = iscsi_unmap_task(iscsi, acb->iscsilun->lun,
|
||||
0, 0, &list[0], 1,
|
||||
iscsi_unmap_cb,
|
||||
acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send unmap command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->sector_num = sector_num;
|
||||
acb->retries = ISCSI_CMD_RETRIES;
|
||||
|
||||
if (iscsi_aio_discard_acb(acb) != 0) {
|
||||
if (acb->task) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -762,6 +918,91 @@ static char *parse_initiator_name(const char *target)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||
static void iscsi_nop_timed_event(void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = opaque;
|
||||
|
||||
if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) {
|
||||
error_report("iSCSI: NOP timeout. Reconnecting...");
|
||||
iscsi_reconnect(iscsilun->iscsi);
|
||||
}
|
||||
|
||||
if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
|
||||
error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages.");
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL);
|
||||
iscsi_set_events(iscsilun);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
|
||||
{
|
||||
struct scsi_task *task = NULL;
|
||||
struct scsi_readcapacity10 *rc10 = NULL;
|
||||
struct scsi_readcapacity16 *rc16 = NULL;
|
||||
int ret = 0;
|
||||
int retries = ISCSI_CMD_RETRIES;
|
||||
|
||||
try_again:
|
||||
switch (iscsilun->type) {
|
||||
case TYPE_DISK:
|
||||
task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& retries-- > 0) {
|
||||
scsi_free_scsi_task(task);
|
||||
goto try_again;
|
||||
}
|
||||
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(iscsilun->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;
|
||||
}
|
||||
|
||||
out:
|
||||
if (task) {
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We support iscsi url's on the form
|
||||
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
||||
|
@ -773,8 +1014,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
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;
|
||||
char *initiator_name = NULL;
|
||||
int ret;
|
||||
|
||||
|
@ -864,50 +1103,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
|
||||
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 ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
bs->total_sectors = iscsilun->num_blocks *
|
||||
iscsilun->block_size / BDRV_SECTOR_SIZE ;
|
||||
|
||||
|
@ -920,7 +1118,11 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
bs->sg = 1;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||
/* Set up a timer for sending out iSCSI NOPs */
|
||||
iscsilun->nop_timer = qemu_new_timer_ms(rt_clock, iscsi_nop_timed_event, iscsilun);
|
||||
qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL);
|
||||
#endif
|
||||
|
||||
out:
|
||||
if (initiator_name != NULL) {
|
||||
|
@ -947,16 +1149,94 @@ static void iscsi_close(BlockDriverState *bs)
|
|||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
if (iscsilun->nop_timer) {
|
||||
qemu_del_timer(iscsilun->nop_timer);
|
||||
qemu_free_timer(iscsilun->nop_timer);
|
||||
}
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL);
|
||||
iscsi_destroy_context(iscsi);
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
|
||||
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int ret = 0;
|
||||
|
||||
if (iscsilun->type != TYPE_DISK) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (offset > iscsi_getlength(bs)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_create(const char *filename, QEMUOptionParameter *options)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t total_size = 0;
|
||||
BlockDriverState bs;
|
||||
IscsiLun *iscsilun = NULL;
|
||||
|
||||
memset(&bs, 0, sizeof(BlockDriverState));
|
||||
|
||||
/* Read out options */
|
||||
while (options && options->name) {
|
||||
if (!strcmp(options->name, "size")) {
|
||||
total_size = options->value.n / BDRV_SECTOR_SIZE;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
bs.opaque = g_malloc0(sizeof(struct IscsiLun));
|
||||
iscsilun = bs.opaque;
|
||||
|
||||
ret = iscsi_open(&bs, filename, 0);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
if (iscsilun->nop_timer) {
|
||||
qemu_del_timer(iscsilun->nop_timer);
|
||||
qemu_free_timer(iscsilun->nop_timer);
|
||||
}
|
||||
if (iscsilun->type != TYPE_DISK) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (bs.total_sectors < total_size) {
|
||||
ret = -ENOSPC;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (iscsilun->iscsi != NULL) {
|
||||
iscsi_destroy_context(iscsilun->iscsi);
|
||||
}
|
||||
g_free(bs.opaque);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QEMUOptionParameter iscsi_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_iscsi = {
|
||||
.format_name = "iscsi",
|
||||
.protocol_name = "iscsi",
|
||||
|
@ -964,8 +1244,11 @@ static BlockDriver bdrv_iscsi = {
|
|||
.instance_size = sizeof(IscsiLun),
|
||||
.bdrv_file_open = iscsi_open,
|
||||
.bdrv_close = iscsi_close,
|
||||
.bdrv_create = iscsi_create,
|
||||
.create_options = iscsi_create_options,
|
||||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_truncate = iscsi_truncate,
|
||||
|
||||
.bdrv_aio_readv = iscsi_aio_readv,
|
||||
.bdrv_aio_writev = iscsi_aio_writev,
|
||||
|
@ -980,9 +1263,36 @@ static BlockDriver bdrv_iscsi = {
|
|||
#endif
|
||||
};
|
||||
|
||||
static QemuOptsList qemu_iscsi_opts = {
|
||||
.name = "iscsi",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "user",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "username for CHAP authentication to target",
|
||||
},{
|
||||
.name = "password",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "password for CHAP authentication to target",
|
||||
},{
|
||||
.name = "header-digest",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "HeaderDigest setting. "
|
||||
"{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
|
||||
},{
|
||||
.name = "initiator-name",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Initiator iqn name to use when connecting",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static void iscsi_block_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_iscsi);
|
||||
qemu_add_opts(&qemu_iscsi_opts);
|
||||
}
|
||||
|
||||
block_init(iscsi_block_init);
|
||||
|
|
386
block/mirror.c
386
block/mirror.c
|
@ -15,17 +15,17 @@
|
|||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "qemu/bitmap.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 */
|
||||
#define MAX_IN_FLIGHT 16
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
/* The mirroring buffer is a list of granularity-sized chunks.
|
||||
* Free chunks are organized in a list.
|
||||
*/
|
||||
typedef struct MirrorBuffer {
|
||||
QSIMPLEQ_ENTRY(MirrorBuffer) next;
|
||||
} MirrorBuffer;
|
||||
|
||||
typedef struct MirrorBlockJob {
|
||||
BlockJob common;
|
||||
|
@ -36,9 +36,26 @@ typedef struct MirrorBlockJob {
|
|||
bool synced;
|
||||
bool should_complete;
|
||||
int64_t sector_num;
|
||||
int64_t granularity;
|
||||
size_t buf_size;
|
||||
unsigned long *cow_bitmap;
|
||||
HBitmapIter hbi;
|
||||
uint8_t *buf;
|
||||
QSIMPLEQ_HEAD(, MirrorBuffer) buf_free;
|
||||
int buf_free_count;
|
||||
|
||||
unsigned long *in_flight_bitmap;
|
||||
int in_flight;
|
||||
int ret;
|
||||
} MirrorBlockJob;
|
||||
|
||||
typedef struct MirrorOp {
|
||||
MirrorBlockJob *s;
|
||||
QEMUIOVector qiov;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
} MirrorOp;
|
||||
|
||||
static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
||||
int error)
|
||||
{
|
||||
|
@ -52,51 +69,234 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
|||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
|
||||
BlockErrorAction *p_action)
|
||||
static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
{
|
||||
MirrorBlockJob *s = op->s;
|
||||
struct iovec *iov;
|
||||
int64_t chunk_num;
|
||||
int i, nb_chunks, sectors_per_chunk;
|
||||
|
||||
trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret);
|
||||
|
||||
s->in_flight--;
|
||||
iov = op->qiov.iov;
|
||||
for (i = 0; i < op->qiov.niov; i++) {
|
||||
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
|
||||
QSIMPLEQ_INSERT_TAIL(&s->buf_free, buf, next);
|
||||
s->buf_free_count++;
|
||||
}
|
||||
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
chunk_num = op->sector_num / sectors_per_chunk;
|
||||
nb_chunks = op->nb_sectors / sectors_per_chunk;
|
||||
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
|
||||
if (s->cow_bitmap && ret >= 0) {
|
||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||
}
|
||||
|
||||
g_slice_free(MirrorOp, op);
|
||||
qemu_coroutine_enter(s->common.co, NULL);
|
||||
}
|
||||
|
||||
static void mirror_write_complete(void *opaque, int ret)
|
||||
{
|
||||
MirrorOp *op = opaque;
|
||||
MirrorBlockJob *s = op->s;
|
||||
if (ret < 0) {
|
||||
BlockDriverState *source = s->common.bs;
|
||||
BlockErrorAction action;
|
||||
|
||||
bdrv_set_dirty(source, op->sector_num, op->nb_sectors);
|
||||
action = mirror_error_action(s, false, -ret);
|
||||
if (action == BDRV_ACTION_REPORT && s->ret >= 0) {
|
||||
s->ret = ret;
|
||||
}
|
||||
}
|
||||
mirror_iteration_done(op, ret);
|
||||
}
|
||||
|
||||
static void mirror_read_complete(void *opaque, int ret)
|
||||
{
|
||||
MirrorOp *op = opaque;
|
||||
MirrorBlockJob *s = op->s;
|
||||
if (ret < 0) {
|
||||
BlockDriverState *source = s->common.bs;
|
||||
BlockErrorAction action;
|
||||
|
||||
bdrv_set_dirty(source, op->sector_num, op->nb_sectors);
|
||||
action = mirror_error_action(s, true, -ret);
|
||||
if (action == BDRV_ACTION_REPORT && s->ret >= 0) {
|
||||
s->ret = ret;
|
||||
}
|
||||
|
||||
mirror_iteration_done(op, ret);
|
||||
return;
|
||||
}
|
||||
bdrv_aio_writev(s->target, op->sector_num, &op->qiov, op->nb_sectors,
|
||||
mirror_write_complete, op);
|
||||
}
|
||||
|
||||
static void coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source = s->common.bs;
|
||||
BlockDriverState *target = s->target;
|
||||
QEMUIOVector qiov;
|
||||
int ret, nb_sectors;
|
||||
int64_t end;
|
||||
struct iovec iov;
|
||||
int nb_sectors, sectors_per_chunk, nb_chunks;
|
||||
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
|
||||
MirrorOp *op;
|
||||
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
if (s->sector_num < 0) {
|
||||
bdrv_dirty_iter_init(source, &s->hbi);
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(source));
|
||||
assert(s->sector_num >= 0);
|
||||
}
|
||||
|
||||
hbitmap_next_sector = s->sector_num;
|
||||
sector_num = s->sector_num;
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
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);
|
||||
|
||||
/* Extend the QEMUIOVector to include all adjacent blocks that will
|
||||
* be copied in this operation.
|
||||
*
|
||||
* We have to do this if we have no backing file yet in the destination,
|
||||
* and the cluster size is very large. Then we need to do COW ourselves.
|
||||
* The first time a cluster is copied, copy it entirely. Note that,
|
||||
* because both the granularity and the cluster size are powers of two,
|
||||
* the number of sectors to copy cannot exceed one cluster.
|
||||
*
|
||||
* We also want to extend the QEMUIOVector to include more adjacent
|
||||
* dirty blocks if possible, to limit the number of I/O operations and
|
||||
* run efficiently even with a small granularity.
|
||||
*/
|
||||
nb_chunks = 0;
|
||||
nb_sectors = 0;
|
||||
next_sector = sector_num;
|
||||
next_chunk = sector_num / sectors_per_chunk;
|
||||
|
||||
/* Wait for I/O to this cluster (from a previous iteration) to be done. */
|
||||
while (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
do {
|
||||
int added_sectors, added_chunks;
|
||||
|
||||
if (!bdrv_get_dirty(source, next_sector) ||
|
||||
test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
assert(nb_sectors > 0);
|
||||
break;
|
||||
}
|
||||
|
||||
added_sectors = sectors_per_chunk;
|
||||
if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) {
|
||||
bdrv_round_to_clusters(s->target,
|
||||
next_sector, added_sectors,
|
||||
&next_sector, &added_sectors);
|
||||
|
||||
/* On the first iteration, the rounding may make us copy
|
||||
* sectors before the first dirty one.
|
||||
*/
|
||||
if (next_sector < sector_num) {
|
||||
assert(nb_sectors == 0);
|
||||
sector_num = next_sector;
|
||||
next_chunk = next_sector / sectors_per_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors));
|
||||
added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk;
|
||||
|
||||
/* When doing COW, it may happen that there is not enough space for
|
||||
* a full cluster. Wait if that is the case.
|
||||
*/
|
||||
while (nb_chunks == 0 && s->buf_free_count < added_chunks) {
|
||||
trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
if (s->buf_free_count < nb_chunks + added_chunks) {
|
||||
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We have enough free space to copy these sectors. */
|
||||
bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks);
|
||||
|
||||
nb_sectors += added_sectors;
|
||||
nb_chunks += added_chunks;
|
||||
next_sector += added_sectors;
|
||||
next_chunk += added_chunks;
|
||||
} while (next_sector < end);
|
||||
|
||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||
op = g_slice_new(MirrorOp);
|
||||
op->s = s;
|
||||
op->sector_num = sector_num;
|
||||
op->nb_sectors = nb_sectors;
|
||||
|
||||
/* Now make a QEMUIOVector taking enough granularity-sized chunks
|
||||
* from s->buf_free.
|
||||
*/
|
||||
qemu_iovec_init(&op->qiov, nb_chunks);
|
||||
next_sector = sector_num;
|
||||
while (nb_chunks-- > 0) {
|
||||
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
||||
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
||||
s->buf_free_count--;
|
||||
qemu_iovec_add(&op->qiov, buf, s->granularity);
|
||||
|
||||
/* Advance the HBitmapIter in parallel, so that we do not examine
|
||||
* the same sector twice.
|
||||
*/
|
||||
if (next_sector > hbitmap_next_sector && bdrv_get_dirty(source, next_sector)) {
|
||||
hbitmap_next_sector = hbitmap_iter_next(&s->hbi);
|
||||
}
|
||||
|
||||
next_sector += sectors_per_chunk;
|
||||
}
|
||||
|
||||
bdrv_reset_dirty(source, 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);
|
||||
s->in_flight++;
|
||||
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||
mirror_read_complete, op);
|
||||
}
|
||||
|
||||
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;
|
||||
static void mirror_free_init(MirrorBlockJob *s)
|
||||
{
|
||||
int granularity = s->granularity;
|
||||
size_t buf_size = s->buf_size;
|
||||
uint8_t *buf = s->buf;
|
||||
|
||||
fail:
|
||||
/* Try again later. */
|
||||
bdrv_set_dirty(source, s->sector_num, nb_sectors);
|
||||
return ret;
|
||||
assert(s->buf_free_count == 0);
|
||||
QSIMPLEQ_INIT(&s->buf_free);
|
||||
while (buf_size != 0) {
|
||||
MirrorBuffer *cur = (MirrorBuffer *)buf;
|
||||
QSIMPLEQ_INSERT_TAIL(&s->buf_free, cur, next);
|
||||
s->buf_free_count++;
|
||||
buf_size -= granularity;
|
||||
buf += granularity;
|
||||
}
|
||||
}
|
||||
|
||||
static void mirror_drain(MirrorBlockJob *s)
|
||||
{
|
||||
while (s->in_flight > 0) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
}
|
||||
|
||||
static void coroutine_fn mirror_run(void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = opaque;
|
||||
BlockDriverState *bs = s->common.bs;
|
||||
int64_t sector_num, end;
|
||||
int64_t sector_num, end, sectors_per_chunk, length;
|
||||
uint64_t last_pause_ns;
|
||||
BlockDriverInfo bdi;
|
||||
char backing_filename[1024];
|
||||
int ret = 0;
|
||||
int n;
|
||||
|
||||
|
@ -105,20 +305,39 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
}
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
if (s->common.len <= 0) {
|
||||
block_job_completed(&s->common, s->common.len);
|
||||
return;
|
||||
}
|
||||
|
||||
length = (bdrv_getlength(bs) + s->granularity - 1) / s->granularity;
|
||||
s->in_flight_bitmap = bitmap_new(length);
|
||||
|
||||
/* If we have no backing file yet in the destination, we cannot let
|
||||
* the destination do COW. Instead, we copy sectors around the
|
||||
* dirty data if needed. We need a bitmap to do that.
|
||||
*/
|
||||
bdrv_get_backing_filename(s->target, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
if (backing_filename[0] && !s->target->backing_hd) {
|
||||
bdrv_get_info(s->target, &bdi);
|
||||
if (s->granularity < bdi.cluster_size) {
|
||||
s->buf_size = MAX(s->buf_size, bdi.cluster_size);
|
||||
s->cow_bitmap = bitmap_new(length);
|
||||
}
|
||||
}
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
s->buf = qemu_blockalign(bs, BLOCK_SIZE);
|
||||
s->buf = qemu_blockalign(bs, s->buf_size);
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
mirror_free_init(s);
|
||||
|
||||
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;
|
||||
int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1;
|
||||
ret = bdrv_co_is_allocated_above(bs, base,
|
||||
sector_num, next - sector_num, &n);
|
||||
|
||||
|
@ -136,24 +355,40 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
}
|
||||
}
|
||||
|
||||
s->sector_num = -1;
|
||||
bdrv_dirty_iter_init(bs, &s->hbi);
|
||||
last_pause_ns = qemu_get_clock_ns(rt_clock);
|
||||
for (;;) {
|
||||
uint64_t delay_ns;
|
||||
int64_t cnt;
|
||||
bool should_complete;
|
||||
|
||||
if (s->ret < 0) {
|
||||
ret = s->ret;
|
||||
goto immediate_exit;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* periodically with no pending I/O so that qemu_aio_flush() returns.
|
||||
* We do so every SLICE_TIME nanoseconds, or when there is an error,
|
||||
* or when the source is clean, whichever comes first.
|
||||
*/
|
||||
if (qemu_get_clock_ns(rt_clock) - last_pause_ns < SLICE_TIME &&
|
||||
s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||
(cnt == 0 && s->in_flight > 0)) {
|
||||
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
|
||||
qemu_coroutine_yield();
|
||||
continue;
|
||||
} else if (cnt != 0) {
|
||||
mirror_iteration(s);
|
||||
continue;
|
||||
}
|
||||
cnt = bdrv_get_dirty_count(bs);
|
||||
}
|
||||
|
||||
should_complete = false;
|
||||
if (cnt == 0) {
|
||||
if (s->in_flight == 0 && cnt == 0) {
|
||||
trace_mirror_before_flush(s);
|
||||
ret = bdrv_flush(s->target);
|
||||
if (ret < 0) {
|
||||
|
@ -196,23 +431,20 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
trace_mirror_before_sleep(s, cnt, s->synced);
|
||||
if (!s->synced) {
|
||||
/* Publish progress */
|
||||
s->common.offset = end * BDRV_SECTOR_SIZE - cnt * BLOCK_SIZE;
|
||||
s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE;
|
||||
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, BDRV_SECTORS_PER_DIRTY_CHUNK);
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, sectors_per_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 bdrv_drain_all() 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);
|
||||
delay_ns = (s->in_flight == 0 && 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
|
||||
|
@ -222,11 +454,24 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
s->common.cancelled = false;
|
||||
break;
|
||||
}
|
||||
last_pause_ns = qemu_get_clock_ns(rt_clock);
|
||||
}
|
||||
|
||||
immediate_exit:
|
||||
g_free(s->buf);
|
||||
bdrv_set_dirty_tracking(bs, false);
|
||||
if (s->in_flight > 0) {
|
||||
/* We get here only if something went wrong. Either the job failed,
|
||||
* or it was cancelled prematurely so that we do not guarantee that
|
||||
* the target is a copy of the source.
|
||||
*/
|
||||
assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
|
||||
mirror_drain(s);
|
||||
}
|
||||
|
||||
assert(s->in_flight == 0);
|
||||
qemu_vfree(s->buf);
|
||||
g_free(s->cow_bitmap);
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_set_dirty_tracking(bs, 0);
|
||||
bdrv_iostatus_disable(s->target);
|
||||
if (s->should_complete && ret == 0) {
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) {
|
||||
|
@ -288,14 +533,28 @@ static BlockJobType mirror_job_type = {
|
|||
};
|
||||
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, MirrorSyncMode mode,
|
||||
BlockdevOnError on_source_error,
|
||||
int64_t speed, int64_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
|
||||
if (granularity == 0) {
|
||||
/* Choose the default granularity based on the target file's cluster
|
||||
* size, clamped between 4k and 64k. */
|
||||
BlockDriverInfo bdi;
|
||||
if (bdrv_get_info(target, &bdi) >= 0 && bdi.cluster_size != 0) {
|
||||
granularity = MAX(4096, bdi.cluster_size);
|
||||
granularity = MIN(65536, granularity);
|
||||
} else {
|
||||
granularity = 65536;
|
||||
}
|
||||
}
|
||||
|
||||
assert ((granularity & (granularity - 1)) == 0);
|
||||
|
||||
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
!bdrv_iostatus_is_enabled(bs)) {
|
||||
|
@ -312,7 +571,10 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
|||
s->on_target_error = on_target_error;
|
||||
s->target = target;
|
||||
s->mode = mode;
|
||||
bdrv_set_dirty_tracking(bs, true);
|
||||
s->granularity = granularity;
|
||||
s->buf_size = MAX(buf_size, granularity);
|
||||
|
||||
bdrv_set_dirty_tracking(bs, granularity);
|
||||
bdrv_set_enable_write_cache(s->target, true);
|
||||
bdrv_set_on_error(s->target, on_target_error, on_target_error);
|
||||
bdrv_iostatus_enable(s->target);
|
||||
|
|
|
@ -68,19 +68,23 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parallels_open(BlockDriverState *bs, int flags)
|
||||
static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
int i;
|
||||
struct parallels_header ph;
|
||||
int ret;
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph))
|
||||
ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
|
||||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
|
||||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -90,18 +94,21 @@ static int parallels_open(BlockDriverState *bs, int flags)
|
|||
|
||||
s->catalog_size = le32_to_cpu(ph.catalog_entries);
|
||||
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||
if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) !=
|
||||
s->catalog_size * 4)
|
||||
goto fail;
|
||||
|
||||
ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->catalog_size; i++)
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (s->catalog_bitmap)
|
||||
g_free(s->catalog_bitmap);
|
||||
return -1;
|
||||
g_free(s->catalog_bitmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
|
|
|
@ -92,7 +92,7 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_open(BlockDriverState *bs, int flags)
|
||||
static int qcow_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, shift, ret;
|
||||
|
@ -112,7 +112,7 @@ static int qcow_open(BlockDriverState *bs, int flags)
|
|||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
if (header.version != QCOW_VERSION) {
|
||||
|
|
|
@ -454,6 +454,9 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
return -EIO;
|
||||
}
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], 0,
|
||||
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
|
||||
|
@ -668,7 +671,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
|||
}
|
||||
|
||||
/* Update L2 table. */
|
||||
if (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS) {
|
||||
if (s->use_lazy_refcounts) {
|
||||
qcow2_mark_dirty(bs);
|
||||
}
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
|
|
|
@ -201,7 +201,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
|||
*refcount_block = NULL;
|
||||
|
||||
/* We write to the refcount table, so we might depend on L2 tables */
|
||||
qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
ret = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allocate the refcount block itself and mark it as used */
|
||||
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
|
||||
|
@ -237,7 +240,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
|||
goto fail_block;
|
||||
}
|
||||
|
||||
bdrv_flush(bs->file);
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
goto fail_block;
|
||||
}
|
||||
|
||||
/* Initialize the new refcount block only after updating its refcount,
|
||||
* update_refcount uses the refcount cache itself */
|
||||
|
@ -526,8 +532,6 @@ static int update_cluster_refcount(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
bdrv_flush(bs->file);
|
||||
|
||||
return get_refcount(bs, cluster_index);
|
||||
}
|
||||
|
||||
|
@ -663,7 +667,11 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
|||
}
|
||||
}
|
||||
|
||||
bdrv_flush(bs->file);
|
||||
/* The cluster refcount was incremented, either by qcow2_alloc_clusters()
|
||||
* or explicitly by update_cluster_refcount(). Refcount blocks must be
|
||||
* flushed before the caller's L2 table updates.
|
||||
*/
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -737,11 +745,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
* l1_table_offset when it is the current s->l1_table_offset! Be careful
|
||||
* when changing this! */
|
||||
if (l1_table_offset != s->l1_table_offset) {
|
||||
if (l1_size2 != 0) {
|
||||
l1_table = g_malloc0(align_offset(l1_size2, 512));
|
||||
} else {
|
||||
l1_table = NULL;
|
||||
}
|
||||
l1_table = g_malloc0(align_offset(l1_size2, 512));
|
||||
l1_allocated = 1;
|
||||
if (bdrv_pread(bs->file, l1_table_offset,
|
||||
l1_table, l1_size2) != l1_size2)
|
||||
|
@ -786,10 +790,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* TODO Flushing once for the whole function should
|
||||
* be enough */
|
||||
bdrv_flush(bs->file);
|
||||
}
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
|
@ -845,7 +845,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
ret = bdrv_flush(bs);
|
||||
fail:
|
||||
if (l2_table) {
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
|
@ -918,6 +918,12 @@ static void inc_refcounts(BlockDriverState *bs,
|
|||
}
|
||||
}
|
||||
|
||||
/* Flags for check_refcounts_l1() and check_refcounts_l2() */
|
||||
enum {
|
||||
CHECK_OFLAG_COPIED = 0x1, /* check QCOW_OFLAG_COPIED matches refcount */
|
||||
CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */
|
||||
};
|
||||
|
||||
/*
|
||||
* Increases the refcount in the given refcount table for the all clusters
|
||||
* referenced in the L2 table. While doing so, performs some checks on L2
|
||||
|
@ -928,10 +934,11 @@ static void inc_refcounts(BlockDriverState *bs,
|
|||
*/
|
||||
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset,
|
||||
int check_copied)
|
||||
int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table, l2_entry;
|
||||
uint64_t next_contiguous_offset = 0;
|
||||
int i, l2_size, nb_csectors, refcount;
|
||||
|
||||
/* Read L2 table from disk */
|
||||
|
@ -962,6 +969,18 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
l2_entry &= s->cluster_offset_mask;
|
||||
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
||||
l2_entry & ~511, nb_csectors * 512);
|
||||
|
||||
if (flags & CHECK_FRAG_INFO) {
|
||||
res->bfi.allocated_clusters++;
|
||||
res->bfi.compressed_clusters++;
|
||||
|
||||
/* Compressed clusters are fragmented by nature. Since they
|
||||
* take up sub-sector space but we only have sector granularity
|
||||
* I/O we need to re-read the same sectors even for adjacent
|
||||
* compressed clusters.
|
||||
*/
|
||||
res->bfi.fragmented_clusters++;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
|
@ -975,7 +994,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
|
||||
if (check_copied) {
|
||||
if (flags & CHECK_OFLAG_COPIED) {
|
||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
||||
if (refcount < 0) {
|
||||
fprintf(stderr, "Can't get refcount for offset %"
|
||||
|
@ -989,6 +1008,15 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
}
|
||||
}
|
||||
|
||||
if (flags & CHECK_FRAG_INFO) {
|
||||
res->bfi.allocated_clusters++;
|
||||
if (next_contiguous_offset &&
|
||||
offset != next_contiguous_offset) {
|
||||
res->bfi.fragmented_clusters++;
|
||||
}
|
||||
next_contiguous_offset = offset + s->cluster_size;
|
||||
}
|
||||
|
||||
/* Mark cluster as used */
|
||||
inc_refcounts(bs, res, refcount_table,refcount_table_size,
|
||||
offset, s->cluster_size);
|
||||
|
@ -1032,7 +1060,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
|||
uint16_t *refcount_table,
|
||||
int refcount_table_size,
|
||||
int64_t l1_table_offset, int l1_size,
|
||||
int check_copied)
|
||||
int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table, l2_offset, l1_size2;
|
||||
|
@ -1061,7 +1089,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
|||
l2_offset = l1_table[i];
|
||||
if (l2_offset) {
|
||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||
if (check_copied) {
|
||||
if (flags & CHECK_OFLAG_COPIED) {
|
||||
refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED)
|
||||
>> s->cluster_bits);
|
||||
if (refcount < 0) {
|
||||
|
@ -1090,7 +1118,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
|||
|
||||
/* Process and check L2 entries */
|
||||
ret = check_refcounts_l2(bs, res, refcount_table,
|
||||
refcount_table_size, l2_offset, check_copied);
|
||||
refcount_table_size, l2_offset, flags);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1116,7 +1144,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t size, i;
|
||||
int64_t size, i, highest_cluster;
|
||||
int nb_clusters, refcount1, refcount2;
|
||||
QCowSnapshot *sn;
|
||||
uint16_t *refcount_table;
|
||||
|
@ -1124,6 +1152,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
|
||||
size = bdrv_getlength(bs->file);
|
||||
nb_clusters = size_to_clusters(s, size);
|
||||
res->bfi.total_clusters = nb_clusters;
|
||||
refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
|
||||
|
||||
/* header */
|
||||
|
@ -1132,7 +1161,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
|
||||
/* current L1 table */
|
||||
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
|
||||
s->l1_table_offset, s->l1_size, 1);
|
||||
s->l1_table_offset, s->l1_size,
|
||||
CHECK_OFLAG_COPIED | CHECK_FRAG_INFO);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1187,7 +1217,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
}
|
||||
|
||||
/* compare ref counts */
|
||||
for(i = 0; i < nb_clusters; i++) {
|
||||
for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
|
||||
refcount1 = get_refcount(bs, i);
|
||||
if (refcount1 < 0) {
|
||||
fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
|
||||
|
@ -1197,6 +1227,11 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
}
|
||||
|
||||
refcount2 = refcount_table[i];
|
||||
|
||||
if (refcount1 > 0 || refcount2 > 0) {
|
||||
highest_cluster = i;
|
||||
}
|
||||
|
||||
if (refcount1 != refcount2) {
|
||||
|
||||
/* Check if we're allowed to fix the mismatch */
|
||||
|
@ -1231,6 +1266,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
}
|
||||
}
|
||||
|
||||
res->image_end_offset = (highest_cluster + 1) * s->cluster_size;
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -180,11 +180,14 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
|||
|
||||
/* Allocate space for the new snapshot list */
|
||||
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
|
||||
bdrv_flush(bs->file);
|
||||
offset = snapshots_offset;
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
ret = bdrv_flush(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write all snapshots to the new list */
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
|
@ -378,11 +381,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_flush(bs);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Append the new snapshot to the snapshot list */
|
||||
new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
|
||||
if (s->snapshots) {
|
||||
|
|
|
@ -285,11 +285,26 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
static QemuOptsList qcow2_runtime_opts = {
|
||||
.name = "qcow2",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "lazy_refcounts",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Postpone refcount updates",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, ret = 0;
|
||||
QCowHeader header;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
uint64_t ext_end;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
|
@ -311,7 +326,7 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
|||
be32_to_cpus(&header.nb_snapshots);
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
ret = -EINVAL;
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
if (header.version < 2 || header.version > 3) {
|
||||
|
@ -495,6 +510,28 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
|||
}
|
||||
}
|
||||
|
||||
/* Enable lazy_refcounts according to image and command line options */
|
||||
opts = qemu_opts_create_nofail(&qcow2_runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->use_lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts",
|
||||
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS));
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require "
|
||||
"a qcow2 image with at least qemu 1.1 compatibility level");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
|
@ -584,7 +621,7 @@ static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs,
|
|||
*pnum = 0;
|
||||
}
|
||||
|
||||
return (cluster_offset != 0);
|
||||
return (cluster_offset != 0) || (ret == QCOW2_CLUSTER_ZERO);
|
||||
}
|
||||
|
||||
/* handle reading after the end of the backing file */
|
||||
|
@ -665,10 +702,6 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors);
|
||||
break;
|
||||
|
||||
|
@ -759,7 +792,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
|||
QEMUIOVector hd_qiov;
|
||||
uint64_t bytes_done = 0;
|
||||
uint8_t *cluster_data = NULL;
|
||||
QCowL2Meta *l2meta;
|
||||
QCowL2Meta *l2meta = NULL;
|
||||
|
||||
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num,
|
||||
remaining_sectors);
|
||||
|
@ -912,7 +945,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
|
|||
qcow2_close(bs);
|
||||
|
||||
memset(s, 0, sizeof(BDRVQcowState));
|
||||
qcow2_open(bs, flags);
|
||||
qcow2_open(bs, NULL, flags);
|
||||
|
||||
if (crypt_method) {
|
||||
s->crypt_method = crypt_method;
|
||||
|
@ -1265,7 +1298,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||
*/
|
||||
BlockDriver* drv = bdrv_find_format("qcow2");
|
||||
assert(drv != NULL);
|
||||
ret = bdrv_open(bs, filename,
|
||||
ret = bdrv_open(bs, filename, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
|
|
|
@ -173,6 +173,7 @@ typedef struct BDRVQcowState {
|
|||
|
||||
int flags;
|
||||
int qcow_version;
|
||||
bool use_lazy_refcounts;
|
||||
|
||||
uint64_t incompatible_features;
|
||||
uint64_t compatible_features;
|
||||
|
|
|
@ -373,7 +373,7 @@ static void bdrv_qed_rebind(BlockDriverState *bs)
|
|||
s->bs = bs;
|
||||
}
|
||||
|
||||
static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
QEDHeader le_header;
|
||||
|
@ -390,7 +390,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
|||
qed_header_le_to_cpu(&le_header, &s->header);
|
||||
|
||||
if (s->header.magic != QED_MAGIC) {
|
||||
return -EINVAL;
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
if (s->header.features & ~QED_FEATURE_MASK) {
|
||||
/* image uses unsupported feature bits */
|
||||
|
@ -1526,7 +1526,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
|
|||
|
||||
bdrv_qed_close(bs);
|
||||
memset(s, 0, sizeof(BDRVQEDState));
|
||||
bdrv_qed_open(bs, bs->open_flags);
|
||||
bdrv_qed_open(bs, NULL, bs->open_flags);
|
||||
}
|
||||
|
||||
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
|
|
|
@ -20,11 +20,14 @@
|
|||
#define QEMU_AIO_WRITE 0x0002
|
||||
#define QEMU_AIO_IOCTL 0x0004
|
||||
#define QEMU_AIO_FLUSH 0x0008
|
||||
#define QEMU_AIO_DISCARD 0x0010
|
||||
#define QEMU_AIO_TYPE_MASK \
|
||||
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH)
|
||||
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \
|
||||
QEMU_AIO_DISCARD)
|
||||
|
||||
/* AIO flags */
|
||||
#define QEMU_AIO_MISALIGNED 0x1000
|
||||
#define QEMU_AIO_BLKDEV 0x2000
|
||||
|
||||
|
||||
/* linux-aio.c - Linux native implementation */
|
||||
|
|
|
@ -59,6 +59,9 @@
|
|||
#ifdef CONFIG_FIEMAP
|
||||
#include <linux/fiemap.h>
|
||||
#endif
|
||||
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
||||
#include <linux/falloc.h>
|
||||
#endif
|
||||
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <sys/disk.h>
|
||||
#include <sys/cdio.h>
|
||||
|
@ -138,6 +141,7 @@ typedef struct BDRVRawState {
|
|||
#ifdef CONFIG_XFS
|
||||
bool is_xfs : 1;
|
||||
#endif
|
||||
bool has_discard : 1;
|
||||
} BDRVRawState;
|
||||
|
||||
typedef struct BDRVRawReopenState {
|
||||
|
@ -159,7 +163,7 @@ typedef struct RawPosixAIOData {
|
|||
void *aio_ioctl_buf;
|
||||
};
|
||||
int aio_niov;
|
||||
size_t aio_nbytes;
|
||||
uint64_t aio_nbytes;
|
||||
#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
|
||||
off_t aio_offset;
|
||||
int aio_type;
|
||||
|
@ -289,6 +293,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
|
|||
}
|
||||
#endif
|
||||
|
||||
s->has_discard = 1;
|
||||
#ifdef CONFIG_XFS
|
||||
if (platform_test_xfs_fd(s->fd)) {
|
||||
s->is_xfs = 1;
|
||||
|
@ -340,11 +345,20 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
|||
|
||||
raw_s->fd = -1;
|
||||
|
||||
int fcntl_flags = O_APPEND | O_ASYNC | O_NONBLOCK;
|
||||
int fcntl_flags = O_APPEND | O_NONBLOCK;
|
||||
#ifdef O_NOATIME
|
||||
fcntl_flags |= O_NOATIME;
|
||||
#endif
|
||||
|
||||
#ifdef O_ASYNC
|
||||
/* Not all operating systems have O_ASYNC, and those that don't
|
||||
* will not let us track the state into raw_s->open_flags (typically
|
||||
* you achieve the same effect with an ioctl, for example I_SETSIG
|
||||
* on Solaris). But we do not use O_ASYNC, so that's fine.
|
||||
*/
|
||||
assert((s->open_flags & O_ASYNC) == 0);
|
||||
#endif
|
||||
|
||||
if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
|
||||
/* dup the original fd */
|
||||
/* TODO: use qemu fcntl wrapper */
|
||||
|
@ -430,22 +444,6 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
|||
#endif
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check if all memory in this vector is sector aligned.
|
||||
*/
|
||||
static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qiov->niov; i++) {
|
||||
if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret;
|
||||
|
@ -455,15 +453,7 @@ static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
|
|||
return -errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* This looks weird, but the aio code only considers a request
|
||||
* successful if it has written the full number of bytes.
|
||||
*
|
||||
* Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command,
|
||||
* so in fact we return the ioctl command here to make posix_aio_read()
|
||||
* happy..
|
||||
*/
|
||||
return aiocb->aio_nbytes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_flush(RawPosixAIOData *aiocb)
|
||||
|
@ -642,6 +632,72 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb)
|
|||
return nbytes;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes)
|
||||
{
|
||||
struct xfs_flock64 fl;
|
||||
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = offset;
|
||||
fl.l_len = bytes;
|
||||
|
||||
if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) {
|
||||
DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
BDRVRawState *s = aiocb->bs->opaque;
|
||||
|
||||
if (s->has_discard == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
|
||||
#ifdef BLKDISCARD
|
||||
do {
|
||||
uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes };
|
||||
if (ioctl(aiocb->aio_fildes, BLKDISCARD, range) == 0) {
|
||||
return 0;
|
||||
}
|
||||
} while (errno == EINTR);
|
||||
|
||||
ret = -errno;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef CONFIG_XFS
|
||||
if (s->is_xfs) {
|
||||
return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
||||
do {
|
||||
if (fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
aiocb->aio_offset, aiocb->aio_nbytes) == 0) {
|
||||
return 0;
|
||||
}
|
||||
} while (errno == EINTR);
|
||||
|
||||
ret = -errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP ||
|
||||
ret == -ENOTTY) {
|
||||
s->has_discard = 0;
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aio_worker(void *arg)
|
||||
{
|
||||
RawPosixAIOData *aiocb = arg;
|
||||
|
@ -676,6 +732,9 @@ static int aio_worker(void *arg)
|
|||
case QEMU_AIO_IOCTL:
|
||||
ret = handle_aiocb_ioctl(aiocb);
|
||||
break;
|
||||
case QEMU_AIO_DISCARD:
|
||||
ret = handle_aiocb_discard(aiocb);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||
ret = -EINVAL;
|
||||
|
@ -691,6 +750,7 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
|||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
ThreadPool *pool;
|
||||
|
||||
acb->bs = bs;
|
||||
acb->aio_type = type;
|
||||
|
@ -704,7 +764,8 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
|||
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);
|
||||
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
||||
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
|
||||
|
@ -722,7 +783,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
|
|||
* driver that it needs to copy the buffer.
|
||||
*/
|
||||
if ((bs->open_flags & BDRV_O_NOCACHE)) {
|
||||
if (!qiov_is_aligned(bs, qiov)) {
|
||||
if (!bdrv_qiov_is_aligned(bs, qiov)) {
|
||||
type |= QEMU_AIO_MISALIGNED;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
} else if (s->use_aio) {
|
||||
|
@ -1076,37 +1137,14 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors)
|
||||
static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
struct xfs_flock64 fl;
|
||||
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = sector_num << 9;
|
||||
fl.l_len = (int64_t)nb_sectors << 9;
|
||||
|
||||
if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) {
|
||||
DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static coroutine_fn int raw_co_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
#ifdef CONFIG_XFS
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (s->is_xfs) {
|
||||
return xfs_discard(s, sector_num, nb_sectors);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors,
|
||||
cb, opaque, QEMU_AIO_DISCARD);
|
||||
}
|
||||
|
||||
static QEMUOptionParameter raw_create_options[] = {
|
||||
|
@ -1129,12 +1167,12 @@ static BlockDriver bdrv_file = {
|
|||
.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,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_aio_discard = raw_aio_discard,
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
|
@ -1221,9 +1259,43 @@ static int hdev_probe_device(const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int check_hdev_writable(BDRVRawState *s)
|
||||
{
|
||||
#if defined(BLKROGET)
|
||||
/* Linux block devices can be configured "read-only" using blockdev(8).
|
||||
* This is independent of device node permissions and therefore open(2)
|
||||
* with O_RDWR succeeds. Actual writes fail with EPERM.
|
||||
*
|
||||
* bdrv_open() is supposed to fail if the disk is read-only. Explicitly
|
||||
* check for read-only block devices so that Linux block devices behave
|
||||
* properly.
|
||||
*/
|
||||
struct stat st;
|
||||
int readonly = 0;
|
||||
|
||||
if (fstat(s->fd, &st)) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!S_ISBLK(st.st_mode)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ioctl(s->fd, BLKROGET, &readonly) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (readonly) {
|
||||
return -EACCES;
|
||||
}
|
||||
#endif /* defined(BLKROGET) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
if (strstart(filename, "/dev/cdrom", NULL)) {
|
||||
|
@ -1264,7 +1336,20 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
}
|
||||
#endif
|
||||
|
||||
return raw_open_common(bs, filename, flags, 0);
|
||||
ret = raw_open_common(bs, filename, flags, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
ret = check_hdev_writable(s);
|
||||
if (ret < 0) {
|
||||
raw_close(bs);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
|
@ -1330,6 +1415,7 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
|||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawPosixAIOData *acb;
|
||||
ThreadPool *pool;
|
||||
|
||||
if (fd_open(bs) < 0)
|
||||
return NULL;
|
||||
|
@ -1341,7 +1427,8 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
|||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
||||
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
|
@ -1363,6 +1450,19 @@ static int fd_open(BlockDriverState *bs)
|
|||
|
||||
#endif /* !linux && !FreeBSD */
|
||||
|
||||
static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (fd_open(bs) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors,
|
||||
cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
|
||||
}
|
||||
|
||||
static int hdev_create(const char *filename, QEMUOptionParameter *options)
|
||||
{
|
||||
int fd;
|
||||
|
@ -1415,6 +1515,7 @@ static BlockDriver bdrv_host_device = {
|
|||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
.bdrv_aio_discard = hdev_aio_discard,
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
|
@ -1776,6 +1877,40 @@ static BlockDriver bdrv_host_cdrom = {
|
|||
};
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
/**
|
||||
* Return the file descriptor for Linux AIO
|
||||
*
|
||||
* This function is a layering violation and should be removed when it becomes
|
||||
* possible to call the block layer outside the global mutex. It allows the
|
||||
* caller to hijack the file descriptor so I/O can be performed outside the
|
||||
* block layer.
|
||||
*/
|
||||
int raw_get_aio_fd(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s;
|
||||
|
||||
if (!bs->drv) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
if (bs->drv == bdrv_find_format("raw")) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
/* raw-posix has several protocols so just check for raw_aio_readv */
|
||||
if (bs->drv->bdrv_aio_readv != raw_aio_readv) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
s = bs->opaque;
|
||||
if (!s->use_aio) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return s->fd;
|
||||
}
|
||||
#endif /* CONFIG_LINUX_AIO */
|
||||
|
||||
static void bdrv_file_init(void)
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -144,6 +144,7 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
|||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
RawWin32AIOData *acb = g_slice_new(RawWin32AIOData);
|
||||
ThreadPool *pool;
|
||||
|
||||
acb->bs = bs;
|
||||
acb->hfile = hfile;
|
||||
|
@ -157,7 +158,8 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
|||
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);
|
||||
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
||||
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
int qemu_ftruncate64(int fd, int64_t length)
|
||||
|
@ -314,11 +316,11 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
|||
*/
|
||||
dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN);
|
||||
if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||
fprintf(stderr, "SetFilePointer error: %d\n", GetLastError());
|
||||
fprintf(stderr, "SetFilePointer error: %lu\n", GetLastError());
|
||||
return -EIO;
|
||||
}
|
||||
if (SetEndOfFile(s->hfile) == 0) {
|
||||
fprintf(stderr, "SetEndOfFile error: %d\n", GetLastError());
|
||||
fprintf(stderr, "SetEndOfFile error: %lu\n", GetLastError());
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
static int raw_open(BlockDriverState *bs, int flags)
|
||||
static int raw_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
bs->sg = bs->file->sg;
|
||||
return 0;
|
||||
|
|
491
block/sheepdog.c
491
block/sheepdog.c
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/block_int.h"
|
||||
|
@ -21,7 +22,7 @@
|
|||
#define SD_PROTO_VER 0x01
|
||||
|
||||
#define SD_DEFAULT_ADDR "localhost"
|
||||
#define SD_DEFAULT_PORT "7000"
|
||||
#define SD_DEFAULT_PORT 7000
|
||||
|
||||
#define SD_OP_CREATE_AND_WRITE_OBJ 0x01
|
||||
#define SD_OP_READ_OBJ 0x02
|
||||
|
@ -36,7 +37,8 @@
|
|||
|
||||
#define SD_FLAG_CMD_WRITE 0x01
|
||||
#define SD_FLAG_CMD_COW 0x02
|
||||
#define SD_FLAG_CMD_CACHE 0x04
|
||||
#define SD_FLAG_CMD_CACHE 0x04 /* Writeback mode for cache */
|
||||
#define SD_FLAG_CMD_DIRECT 0x08 /* Don't use cache */
|
||||
|
||||
#define SD_RES_SUCCESS 0x00 /* Success */
|
||||
#define SD_RES_UNKNOWN 0x01 /* Unknown error */
|
||||
|
@ -144,7 +146,7 @@ typedef struct SheepdogVdiReq {
|
|||
uint32_t id;
|
||||
uint32_t data_length;
|
||||
uint64_t vdi_size;
|
||||
uint32_t base_vdi_id;
|
||||
uint32_t vdi_id;
|
||||
uint32_t copies;
|
||||
uint32_t snapid;
|
||||
uint32_t pad[3];
|
||||
|
@ -265,6 +267,7 @@ typedef struct AIOReq {
|
|||
enum AIOCBState {
|
||||
AIOCB_WRITE_UDATA,
|
||||
AIOCB_READ_UDATA,
|
||||
AIOCB_FLUSH_CACHE,
|
||||
};
|
||||
|
||||
struct SheepdogAIOCB {
|
||||
|
@ -293,12 +296,11 @@ typedef struct BDRVSheepdogState {
|
|||
|
||||
char name[SD_MAX_VDI_LEN];
|
||||
bool is_snapshot;
|
||||
bool cache_enabled;
|
||||
uint32_t cache_flags;
|
||||
|
||||
char *addr;
|
||||
char *port;
|
||||
char *host_spec;
|
||||
bool is_unix;
|
||||
int fd;
|
||||
int flush_fd;
|
||||
|
||||
CoMutex lock;
|
||||
Coroutine *co_send;
|
||||
|
@ -426,12 +428,11 @@ static const AIOCBInfo sd_aiocb_info = {
|
|||
};
|
||||
|
||||
static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
SheepdogAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&sd_aiocb_info, bs, cb, opaque);
|
||||
acb = qemu_aio_get(&sd_aiocb_info, bs, NULL, NULL);
|
||||
|
||||
acb->qiov = qiov;
|
||||
|
||||
|
@ -446,56 +447,31 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
|||
return acb;
|
||||
}
|
||||
|
||||
static int connect_to_sdog(const char *addr, const char *port)
|
||||
static int connect_to_sdog(BDRVSheepdogState *s)
|
||||
{
|
||||
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
||||
int fd, ret;
|
||||
struct addrinfo hints, *res, *res0;
|
||||
int fd;
|
||||
Error *err = NULL;
|
||||
|
||||
if (!addr) {
|
||||
addr = SD_DEFAULT_ADDR;
|
||||
port = SD_DEFAULT_PORT;
|
||||
}
|
||||
if (s->is_unix) {
|
||||
fd = unix_connect(s->host_spec, &err);
|
||||
} else {
|
||||
fd = inet_connect(s->host_spec, &err);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
ret = getaddrinfo(addr, port, &hints, &res0);
|
||||
if (ret) {
|
||||
error_report("unable to get address info %s, %s",
|
||||
addr, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (res = res0; res; res = res->ai_next) {
|
||||
ret = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
|
||||
sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (ret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reconnect:
|
||||
ret = connect(fd, res->ai_addr, res->ai_addrlen);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
goto reconnect;
|
||||
if (err == NULL) {
|
||||
int ret = socket_set_nodelay(fd);
|
||||
if (ret < 0) {
|
||||
error_report("%s", strerror(errno));
|
||||
}
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
|
||||
dprintf("connected to %s:%s\n", addr, port);
|
||||
goto success;
|
||||
}
|
||||
fd = -errno;
|
||||
error_report("failed connect to %s:%s", addr, port);
|
||||
success:
|
||||
freeaddrinfo(res0);
|
||||
|
||||
if (err != NULL) {
|
||||
qerror_report_err(err);
|
||||
error_free(err);
|
||||
} else {
|
||||
socket_set_nonblock(fd);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -525,6 +501,13 @@ static void restart_co_req(void *opaque)
|
|||
qemu_coroutine_enter(co, NULL);
|
||||
}
|
||||
|
||||
static int have_co_req(void *opaque)
|
||||
{
|
||||
/* this handler is set only when there is a pending request, so
|
||||
* always returns 1. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct SheepdogReqCo {
|
||||
int sockfd;
|
||||
SheepdogReq *hdr;
|
||||
|
@ -547,15 +530,14 @@ static coroutine_fn void do_co_req(void *opaque)
|
|||
unsigned int *rlen = srco->rlen;
|
||||
|
||||
co = qemu_coroutine_self();
|
||||
qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, NULL, co);
|
||||
qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, have_co_req, co);
|
||||
|
||||
socket_set_block(sockfd);
|
||||
ret = send_co_req(sockfd, hdr, data, wlen);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, NULL, co);
|
||||
qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, have_co_req, co);
|
||||
|
||||
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
|
||||
if (ret < sizeof(*hdr)) {
|
||||
|
@ -578,8 +560,9 @@ static coroutine_fn void do_co_req(void *opaque)
|
|||
}
|
||||
ret = 0;
|
||||
out:
|
||||
/* there is at most one request for this sockfd, so it is safe to
|
||||
* set each handler to NULL. */
|
||||
qemu_aio_set_fd_handler(sockfd, NULL, NULL, NULL, NULL);
|
||||
socket_set_nonblock(sockfd);
|
||||
|
||||
srco->ret = ret;
|
||||
srco->finished = true;
|
||||
|
@ -714,16 +697,17 @@ static void coroutine_fn aio_read_response(void *opaque)
|
|||
* and max_dirty_data_idx are changed to include updated
|
||||
* index between them.
|
||||
*/
|
||||
s->inode.data_vdi_id[idx] = s->inode.vdi_id;
|
||||
s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx);
|
||||
s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx);
|
||||
|
||||
if (rsp.result == SD_RES_SUCCESS) {
|
||||
s->inode.data_vdi_id[idx] = s->inode.vdi_id;
|
||||
s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx);
|
||||
s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx);
|
||||
}
|
||||
/*
|
||||
* Some requests may be blocked because simultaneous
|
||||
* 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, aio_req->oid);
|
||||
}
|
||||
break;
|
||||
case AIOCB_READ_UDATA:
|
||||
|
@ -734,6 +718,13 @@ static void coroutine_fn aio_read_response(void *opaque)
|
|||
goto out;
|
||||
}
|
||||
break;
|
||||
case AIOCB_FLUSH_CACHE:
|
||||
if (rsp.result == SD_RES_INVALID_PARMS) {
|
||||
dprintf("disable cache since the server doesn't support it\n");
|
||||
s->cache_flags = SD_FLAG_CMD_DIRECT;
|
||||
rsp.result = SD_RES_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (rsp.result != SD_RES_SUCCESS) {
|
||||
|
@ -779,15 +770,6 @@ static int aio_flush_request(void *opaque)
|
|||
!QLIST_EMPTY(&s->pending_aio_head);
|
||||
}
|
||||
|
||||
static int set_nodelay(int fd)
|
||||
{
|
||||
int ret, opt;
|
||||
|
||||
opt = 1;
|
||||
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a socket discriptor to read/write objects.
|
||||
*
|
||||
|
@ -796,29 +778,86 @@ static int set_nodelay(int fd)
|
|||
*/
|
||||
static int get_sheep_fd(BDRVSheepdogState *s)
|
||||
{
|
||||
int ret, fd;
|
||||
int fd;
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
error_report("%s", strerror(errno));
|
||||
return fd;
|
||||
}
|
||||
|
||||
socket_set_nonblock(fd);
|
||||
|
||||
ret = set_nodelay(fd);
|
||||
if (ret) {
|
||||
error_report("%s", strerror(errno));
|
||||
closesocket(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, s);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int sd_parse_uri(BDRVSheepdogState *s, const char *filename,
|
||||
char *vdi, uint32_t *snapid, char *tag)
|
||||
{
|
||||
URI *uri;
|
||||
QueryParams *qp = NULL;
|
||||
int ret = 0;
|
||||
|
||||
uri = uri_parse(filename);
|
||||
if (!uri) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* transport */
|
||||
if (!strcmp(uri->scheme, "sheepdog")) {
|
||||
s->is_unix = false;
|
||||
} else if (!strcmp(uri->scheme, "sheepdog+tcp")) {
|
||||
s->is_unix = false;
|
||||
} else if (!strcmp(uri->scheme, "sheepdog+unix")) {
|
||||
s->is_unix = true;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uri->path == NULL || !strcmp(uri->path, "/")) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
pstrcpy(vdi, SD_MAX_VDI_LEN, uri->path + 1);
|
||||
|
||||
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) {
|
||||
/* sheepdog+unix:///vdiname?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 {
|
||||
/* sheepdog[+tcp]://[host:port]/vdiname */
|
||||
s->host_spec = g_strdup_printf("%s:%d", uri->server ?: SD_DEFAULT_ADDR,
|
||||
uri->port ?: SD_DEFAULT_PORT);
|
||||
}
|
||||
|
||||
/* snapshot tag */
|
||||
if (uri->fragment) {
|
||||
*snapid = strtoul(uri->fragment, NULL, 10);
|
||||
if (*snapid == 0) {
|
||||
pstrcpy(tag, SD_MAX_VDI_TAG_LEN, uri->fragment);
|
||||
}
|
||||
} else {
|
||||
*snapid = CURRENT_VDI_ID; /* search current vdi */
|
||||
}
|
||||
|
||||
out:
|
||||
if (qp) {
|
||||
query_params_free(qp);
|
||||
}
|
||||
uri_free(uri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a filename
|
||||
* Parse a filename (old syntax)
|
||||
*
|
||||
* filename must be one of the following formats:
|
||||
* 1. [vdiname]
|
||||
|
@ -837,9 +876,11 @@ static int get_sheep_fd(BDRVSheepdogState *s)
|
|||
static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
|
||||
char *vdi, uint32_t *snapid, char *tag)
|
||||
{
|
||||
char *p, *q;
|
||||
int nr_sep;
|
||||
char *p, *q, *uri;
|
||||
const char *host_spec, *vdi_spec;
|
||||
int nr_sep, ret;
|
||||
|
||||
strstart(filename, "sheepdog:", (const char **)&filename);
|
||||
p = q = g_strdup(filename);
|
||||
|
||||
/* count the number of separators */
|
||||
|
@ -852,38 +893,32 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
|
|||
}
|
||||
p = q;
|
||||
|
||||
/* use the first two tokens as hostname and port number. */
|
||||
/* use the first two tokens as host_spec. */
|
||||
if (nr_sep >= 2) {
|
||||
s->addr = p;
|
||||
host_spec = p;
|
||||
p = strchr(p, ':');
|
||||
*p++ = '\0';
|
||||
|
||||
s->port = p;
|
||||
p++;
|
||||
p = strchr(p, ':');
|
||||
*p++ = '\0';
|
||||
} else {
|
||||
s->addr = NULL;
|
||||
s->port = 0;
|
||||
host_spec = "";
|
||||
}
|
||||
|
||||
pstrcpy(vdi, SD_MAX_VDI_LEN, p);
|
||||
vdi_spec = p;
|
||||
|
||||
p = strchr(vdi, ':');
|
||||
p = strchr(vdi_spec, ':');
|
||||
if (p) {
|
||||
*p++ = '\0';
|
||||
*snapid = strtoul(p, NULL, 10);
|
||||
if (*snapid == 0) {
|
||||
pstrcpy(tag, SD_MAX_VDI_TAG_LEN, p);
|
||||
}
|
||||
} else {
|
||||
*snapid = CURRENT_VDI_ID; /* search current vdi */
|
||||
*p++ = '#';
|
||||
}
|
||||
|
||||
if (s->addr == NULL) {
|
||||
g_free(q);
|
||||
}
|
||||
uri = g_strdup_printf("sheepdog://%s/%s", host_spec, vdi_spec);
|
||||
|
||||
return 0;
|
||||
ret = sd_parse_uri(s, uri, vdi, snapid, tag);
|
||||
|
||||
g_free(q);
|
||||
g_free(uri);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
|
||||
|
@ -895,7 +930,7 @@ static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
|
|||
unsigned int wlen, rlen = 0;
|
||||
char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -948,7 +983,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
|||
{
|
||||
int nr_copies = s->inode.nr_copies;
|
||||
SheepdogObjReq hdr;
|
||||
unsigned int wlen;
|
||||
unsigned int wlen = 0;
|
||||
int ret;
|
||||
uint64_t oid = aio_req->oid;
|
||||
unsigned int datalen = aio_req->data_len;
|
||||
|
@ -962,22 +997,27 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
|||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
if (aiocb_type == AIOCB_READ_UDATA) {
|
||||
wlen = 0;
|
||||
switch (aiocb_type) {
|
||||
case AIOCB_FLUSH_CACHE:
|
||||
hdr.opcode = SD_OP_FLUSH_VDI;
|
||||
break;
|
||||
case AIOCB_READ_UDATA:
|
||||
hdr.opcode = SD_OP_READ_OBJ;
|
||||
hdr.flags = flags;
|
||||
} else if (create) {
|
||||
break;
|
||||
case AIOCB_WRITE_UDATA:
|
||||
if (create) {
|
||||
hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ;
|
||||
} else {
|
||||
hdr.opcode = SD_OP_WRITE_OBJ;
|
||||
}
|
||||
wlen = datalen;
|
||||
hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ;
|
||||
hdr.flags = SD_FLAG_CMD_WRITE | flags;
|
||||
} else {
|
||||
wlen = datalen;
|
||||
hdr.opcode = SD_OP_WRITE_OBJ;
|
||||
hdr.flags = SD_FLAG_CMD_WRITE | flags;
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->cache_enabled) {
|
||||
hdr.flags |= SD_FLAG_CMD_CACHE;
|
||||
if (s->cache_flags) {
|
||||
hdr.flags |= s->cache_flags;
|
||||
}
|
||||
|
||||
hdr.oid = oid;
|
||||
|
@ -1022,7 +1062,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)
|
||||
bool write, bool create, uint32_t cache_flags)
|
||||
{
|
||||
SheepdogObjReq hdr;
|
||||
SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr;
|
||||
|
@ -1046,9 +1086,7 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
|
|||
hdr.opcode = SD_OP_READ_OBJ;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
hdr.flags |= SD_FLAG_CMD_CACHE;
|
||||
}
|
||||
hdr.flags |= cache_flags;
|
||||
|
||||
hdr.oid = oid;
|
||||
hdr.data_length = datalen;
|
||||
|
@ -1071,18 +1109,19 @@ 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,
|
||||
uint32_t cache_flags)
|
||||
{
|
||||
return read_write_object(fd, buf, oid, copies, datalen, offset, false,
|
||||
false, cache);
|
||||
false, cache_flags);
|
||||
}
|
||||
|
||||
static int write_object(int fd, char *buf, uint64_t oid, int copies,
|
||||
unsigned int datalen, uint64_t offset, bool create,
|
||||
bool cache)
|
||||
uint32_t cache_flags)
|
||||
{
|
||||
return read_write_object(fd, buf, oid, copies, datalen, offset, true,
|
||||
create, cache);
|
||||
create, cache_flags);
|
||||
}
|
||||
|
||||
static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
@ -1094,16 +1133,19 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
uint32_t snapid;
|
||||
char *buf = NULL;
|
||||
|
||||
strstart(filename, "sheepdog:", (const char **)&filename);
|
||||
|
||||
QLIST_INIT(&s->inflight_aio_head);
|
||||
QLIST_INIT(&s->pending_aio_head);
|
||||
s->fd = -1;
|
||||
|
||||
memset(vdi, 0, sizeof(vdi));
|
||||
memset(tag, 0, sizeof(tag));
|
||||
if (parse_vdiname(s, filename, vdi, &snapid, tag) < 0) {
|
||||
ret = -EINVAL;
|
||||
|
||||
if (strstr(filename, "://")) {
|
||||
ret = sd_parse_uri(s, filename, vdi, &snapid, tag);
|
||||
} else {
|
||||
ret = parse_vdiname(s, filename, vdi, &snapid, tag);
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
s->fd = get_sheep_fd(s);
|
||||
|
@ -1117,12 +1159,13 @@ 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;
|
||||
/*
|
||||
* QEMU block layer emulates writethrough cache as 'writeback + flush', so
|
||||
* we always set SD_FLAG_CMD_CACHE (writeback cache) as default.
|
||||
*/
|
||||
s->cache_flags = SD_FLAG_CMD_CACHE;
|
||||
if (flags & BDRV_O_NOCACHE) {
|
||||
s->cache_flags = SD_FLAG_CMD_DIRECT;
|
||||
}
|
||||
|
||||
if (snapid || tag[0] != '\0') {
|
||||
|
@ -1130,16 +1173,15 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
|
|||
s->is_snapshot = true;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
error_report("failed to connect");
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = g_malloc(SD_INODE_SIZE);
|
||||
ret = read_object(fd, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0,
|
||||
s->cache_enabled);
|
||||
s->cache_flags);
|
||||
|
||||
closesocket(fd);
|
||||
|
||||
|
@ -1165,9 +1207,8 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int do_sd_create(char *filename, int64_t vdi_size,
|
||||
uint32_t base_vid, uint32_t *vdi_id, int snapshot,
|
||||
const char *addr, const char *port)
|
||||
static int do_sd_create(BDRVSheepdogState *s, char *filename, int64_t vdi_size,
|
||||
uint32_t base_vid, uint32_t *vdi_id, int snapshot)
|
||||
{
|
||||
SheepdogVdiReq hdr;
|
||||
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||
|
@ -1175,7 +1216,7 @@ static int do_sd_create(char *filename, int64_t vdi_size,
|
|||
unsigned int wlen, rlen = 0;
|
||||
char buf[SD_MAX_VDI_LEN];
|
||||
|
||||
fd = connect_to_sdog(addr, port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -1188,7 +1229,7 @@ static int do_sd_create(char *filename, int64_t vdi_size,
|
|||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.opcode = SD_OP_NEW_VDI;
|
||||
hdr.base_vdi_id = base_vid;
|
||||
hdr.vdi_id = base_vid;
|
||||
|
||||
wlen = SD_MAX_VDI_LEN;
|
||||
|
||||
|
@ -1271,17 +1312,17 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
|
|||
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
|
||||
uint32_t snapid;
|
||||
bool prealloc = false;
|
||||
const char *vdiname;
|
||||
|
||||
s = g_malloc0(sizeof(BDRVSheepdogState));
|
||||
|
||||
strstart(filename, "sheepdog:", &vdiname);
|
||||
|
||||
memset(vdi, 0, sizeof(vdi));
|
||||
memset(tag, 0, sizeof(tag));
|
||||
if (parse_vdiname(s, vdiname, vdi, &snapid, tag) < 0) {
|
||||
error_report("invalid filename");
|
||||
ret = -EINVAL;
|
||||
if (strstr(filename, "://")) {
|
||||
ret = sd_parse_uri(s, filename, vdi, &snapid, tag);
|
||||
} else {
|
||||
ret = parse_vdiname(s, filename, vdi, &snapid, tag);
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1342,7 +1383,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
|
|||
bdrv_delete(bs);
|
||||
}
|
||||
|
||||
ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s->addr, s->port);
|
||||
ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0);
|
||||
if (!prealloc || ret) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1363,7 +1404,7 @@ static void sd_close(BlockDriverState *bs)
|
|||
|
||||
dprintf("%s\n", s->name);
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -1371,6 +1412,7 @@ static void sd_close(BlockDriverState *bs)
|
|||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
hdr.opcode = SD_OP_RELEASE_VDI;
|
||||
hdr.vdi_id = s->inode.vdi_id;
|
||||
wlen = strlen(s->name) + 1;
|
||||
hdr.data_length = wlen;
|
||||
hdr.flags = SD_FLAG_CMD_WRITE;
|
||||
|
@ -1386,10 +1428,7 @@ static void sd_close(BlockDriverState *bs)
|
|||
|
||||
qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL);
|
||||
closesocket(s->fd);
|
||||
if (s->cache_enabled) {
|
||||
closesocket(s->flush_fd);
|
||||
}
|
||||
g_free(s->addr);
|
||||
g_free(s->host_spec);
|
||||
}
|
||||
|
||||
static int64_t sd_getlength(BlockDriverState *bs)
|
||||
|
@ -1413,7 +1452,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -1422,7 +1461,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, false, s->cache_flags);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -1489,23 +1528,21 @@ static int sd_create_branch(BDRVSheepdogState *s)
|
|||
|
||||
buf = g_malloc(SD_INODE_SIZE);
|
||||
|
||||
ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1,
|
||||
s->addr, s->port);
|
||||
ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintf("%" PRIx32 " is created.\n", vid);
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
error_report("failed to connect");
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
|
||||
SD_INODE_SIZE, 0, s->cache_enabled);
|
||||
SD_INODE_SIZE, 0, s->cache_flags);
|
||||
|
||||
closesocket(fd);
|
||||
|
||||
|
@ -1661,7 +1698,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||
bs->total_sectors = sector_num + nb_sectors;
|
||||
}
|
||||
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, NULL, NULL);
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
|
||||
acb->aio_done_func = sd_write_done;
|
||||
acb->aiocb_type = AIOCB_WRITE_UDATA;
|
||||
|
||||
|
@ -1682,7 +1719,7 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
SheepdogAIOCB *acb;
|
||||
int ret;
|
||||
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, NULL, NULL);
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
|
||||
acb->aiocb_type = AIOCB_READ_UDATA;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
|
@ -1700,39 +1737,31 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
|
||||
{
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
SheepdogObjReq hdr = { 0 };
|
||||
SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr;
|
||||
SheepdogInode *inode = &s->inode;
|
||||
SheepdogAIOCB *acb;
|
||||
AIOReq *aio_req;
|
||||
int ret;
|
||||
unsigned int wlen = 0, rlen = 0;
|
||||
|
||||
if (!s->cache_enabled) {
|
||||
if (s->cache_flags != SD_FLAG_CMD_CACHE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdr.opcode = SD_OP_FLUSH_VDI;
|
||||
hdr.oid = vid_to_vdi_oid(inode->vdi_id);
|
||||
acb = sd_aio_setup(bs, NULL, 0, 0);
|
||||
acb->aiocb_type = AIOCB_FLUSH_CACHE;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
ret = do_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen);
|
||||
if (ret) {
|
||||
error_report("failed to send a request to the sheep");
|
||||
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
0, 0, 0, 0, 0);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
ret = add_aio_request(s, aio_req, NULL, 0, false, acb->aiocb_type);
|
||||
if (ret < 0) {
|
||||
error_report("add_aio_request is failed");
|
||||
free_aio_req(s, aio_req);
|
||||
qemu_aio_release(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rsp->result == SD_RES_INVALID_PARMS) {
|
||||
dprintf("disable write cache since the server doesn't support it\n");
|
||||
|
||||
s->cache_enabled = false;
|
||||
closesocket(s->flush_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rsp->result != SD_RES_SUCCESS) {
|
||||
error_report("%s", sd_strerror(rsp->result));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
qemu_coroutine_yield();
|
||||
return acb->ret;
|
||||
}
|
||||
|
||||
static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
@ -1766,21 +1795,21 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||
|
||||
/* refresh inode. */
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
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, false, s->cache_flags);
|
||||
if (ret < 0) {
|
||||
error_report("failed to write snapshot's inode.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, 1,
|
||||
s->addr, s->port);
|
||||
ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid,
|
||||
1);
|
||||
if (ret < 0) {
|
||||
error_report("failed to create inode for snapshot. %s",
|
||||
strerror(errno));
|
||||
|
@ -1790,7 +1819,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
inode = (SheepdogInode *)g_malloc(datalen);
|
||||
|
||||
ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid),
|
||||
s->inode.nr_copies, datalen, 0, s->cache_enabled);
|
||||
s->inode.nr_copies, datalen, 0, s->cache_flags);
|
||||
|
||||
if (ret < 0) {
|
||||
error_report("failed to read new inode info. %s", strerror(errno));
|
||||
|
@ -1835,16 +1864,15 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
|||
goto out;
|
||||
}
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
error_report("failed to connect");
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = g_malloc(SD_INODE_SIZE);
|
||||
ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
|
||||
SD_INODE_SIZE, 0, s->cache_enabled);
|
||||
SD_INODE_SIZE, 0, s->cache_flags);
|
||||
|
||||
closesocket(fd);
|
||||
|
||||
|
@ -1899,7 +1927,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
|
||||
vdi_inuse = g_malloc(max);
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out;
|
||||
|
@ -1926,9 +1954,8 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
hval = fnv_64a_buf(s->name, strlen(s->name), FNV1A_64_INIT);
|
||||
start_nr = hval & (SD_NR_VDIS - 1);
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
error_report("failed to connect");
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1941,7 +1968,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
/* we don't need to read entire object */
|
||||
ret = read_object(fd, (char *)&inode, vid_to_vdi_oid(vid),
|
||||
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
|
||||
s->cache_enabled);
|
||||
s->cache_flags);
|
||||
|
||||
if (ret) {
|
||||
continue;
|
||||
|
@ -1985,7 +2012,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
|||
uint32_t vdi_index;
|
||||
uint64_t offset;
|
||||
|
||||
fd = connect_to_sdog(s->addr, s->port);
|
||||
fd = connect_to_sdog(s);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
@ -2002,11 +2029,11 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
|
|||
if (load) {
|
||||
ret = read_object(fd, (char *)data, vmstate_oid,
|
||||
s->inode.nr_copies, data_len, offset,
|
||||
s->cache_enabled);
|
||||
s->cache_flags);
|
||||
} else {
|
||||
ret = write_object(fd, (char *)data, vmstate_oid,
|
||||
s->inode.nr_copies, data_len, offset, create,
|
||||
s->cache_enabled);
|
||||
s->cache_flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -2060,7 +2087,7 @@ static QEMUOptionParameter sd_create_options[] = {
|
|||
{ NULL }
|
||||
};
|
||||
|
||||
BlockDriver bdrv_sheepdog = {
|
||||
static BlockDriver bdrv_sheepdog = {
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
|
@ -2085,8 +2112,60 @@ BlockDriver bdrv_sheepdog = {
|
|||
.create_options = sd_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_sheepdog_tcp = {
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog+tcp",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
|
||||
.create_options = sd_create_options,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_sheepdog_unix = {
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog+unix",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
|
||||
.create_options = sd_create_options,
|
||||
};
|
||||
|
||||
static void bdrv_sheepdog_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_sheepdog);
|
||||
bdrv_register(&bdrv_sheepdog_tcp);
|
||||
bdrv_register(&bdrv_sheepdog_unix);
|
||||
}
|
||||
block_init(bdrv_sheepdog_init);
|
||||
|
|
31
block/vdi.c
31
block/vdi.c
|
@ -246,7 +246,7 @@ static void vdi_header_print(VdiHeader *header)
|
|||
{
|
||||
char uuid[37];
|
||||
logout("text %s", header->text);
|
||||
logout("signature 0x%04x\n", header->signature);
|
||||
logout("signature 0x%08x\n", header->signature);
|
||||
logout("header size 0x%04x\n", header->header_size);
|
||||
logout("image type 0x%04x\n", header->image_type);
|
||||
logout("image flags 0x%04x\n", header->image_flags);
|
||||
|
@ -364,15 +364,17 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return result;
|
||||
}
|
||||
|
||||
static int vdi_open(BlockDriverState *bs, int flags)
|
||||
static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
VdiHeader header;
|
||||
size_t bmap_size;
|
||||
int ret;
|
||||
|
||||
logout("\n");
|
||||
|
||||
if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) {
|
||||
ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -390,33 +392,45 @@ static int vdi_open(BlockDriverState *bs, int flags)
|
|||
header.disk_size &= ~(SECTOR_SIZE - 1);
|
||||
}
|
||||
|
||||
if (header.version != VDI_VERSION_1_1) {
|
||||
if (header.signature != VDI_SIGNATURE) {
|
||||
logout("bad vdi signature %08x\n", header.signature);
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
} else if (header.version != VDI_VERSION_1_1) {
|
||||
logout("unsupported version %u.%u\n",
|
||||
header.version >> 16, header.version & 0xffff);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.offset_bmap % SECTOR_SIZE != 0) {
|
||||
/* We only support block maps which start on a sector boundary. */
|
||||
logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.offset_data % SECTOR_SIZE != 0) {
|
||||
/* We only support data blocks which start on a sector boundary. */
|
||||
logout("unsupported data offset 0x%x B\n", header.offset_data);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.sector_size != SECTOR_SIZE) {
|
||||
logout("unsupported sector size %u B\n", header.sector_size);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.block_size != 1 * MiB) {
|
||||
logout("unsupported block size %u B\n", header.block_size);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.disk_size >
|
||||
(uint64_t)header.blocks_in_image * header.block_size) {
|
||||
logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (!uuid_is_null(header.uuid_link)) {
|
||||
logout("link uuid != 0, unsupported\n");
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (!uuid_is_null(header.uuid_parent)) {
|
||||
logout("parent uuid != 0, unsupported\n");
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -429,10 +443,9 @@ static int vdi_open(BlockDriverState *bs, int flags)
|
|||
|
||||
bmap_size = header.blocks_in_image * sizeof(uint32_t);
|
||||
bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
||||
if (bmap_size > 0) {
|
||||
s->bmap = g_malloc(bmap_size * SECTOR_SIZE);
|
||||
}
|
||||
if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) {
|
||||
s->bmap = g_malloc(bmap_size * SECTOR_SIZE);
|
||||
ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size);
|
||||
if (ret < 0) {
|
||||
goto fail_free_bmap;
|
||||
}
|
||||
|
||||
|
@ -448,7 +461,7 @@ static int vdi_open(BlockDriverState *bs, int flags)
|
|||
g_free(s->bmap);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vdi_reopen_prepare(BDRVReopenState *state,
|
||||
|
|
49
block/vmdk.c
49
block/vmdk.c
|
@ -616,7 +616,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
|||
return vmdk_open_vmdk4(bs, file, flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
return -EMEDIUMTYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -641,7 +641,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
* RW [size in sectors] SPARSE "file-name.vmdk"
|
||||
*/
|
||||
flat_offset = -1;
|
||||
ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64,
|
||||
ret = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
|
||||
access, §ors, type, fname, &flat_offset);
|
||||
if (ret < 4 || strcmp(access, "RW")) {
|
||||
goto next_line;
|
||||
|
@ -653,14 +653,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* trim the quotation marks around */
|
||||
if (fname[0] == '"') {
|
||||
memmove(fname, fname + 1, strlen(fname));
|
||||
if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') {
|
||||
return -EINVAL;
|
||||
}
|
||||
fname[strlen(fname) - 1] = '\0';
|
||||
}
|
||||
if (sectors <= 0 ||
|
||||
(strcmp(type, "FLAT") && strcmp(type, "SPARSE")) ||
|
||||
(strcmp(access, "RW"))) {
|
||||
|
@ -718,7 +710,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
|||
}
|
||||
buf[2047] = '\0';
|
||||
if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
|
||||
return -EINVAL;
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
if (strcmp(ct, "monolithicFlat") &&
|
||||
strcmp(ct, "twoGbMaxExtentSparse") &&
|
||||
|
@ -731,7 +723,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
|||
return vmdk_parse_extents(buf, bs, bs->file->filename);
|
||||
}
|
||||
|
||||
static int vmdk_open(BlockDriverState *bs, int flags)
|
||||
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
int ret;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
@ -1442,6 +1434,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
int fd, idx = 0;
|
||||
char desc[BUF_SIZE];
|
||||
int64_t total_size = 0, filesize;
|
||||
const char *adapter_type = NULL;
|
||||
const char *backing_file = NULL;
|
||||
const char *fmt = NULL;
|
||||
int flags = 0;
|
||||
|
@ -1453,6 +1446,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
const char *desc_extent_line;
|
||||
char parent_desc_line[BUF_SIZE] = "";
|
||||
uint32_t parent_cid = 0xffffffff;
|
||||
uint32_t number_heads = 16;
|
||||
const char desc_template[] =
|
||||
"# Disk DescriptorFile\n"
|
||||
"version=1\n"
|
||||
|
@ -1469,9 +1463,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
"\n"
|
||||
"ddb.virtualHWVersion = \"%d\"\n"
|
||||
"ddb.geometry.cylinders = \"%" PRId64 "\"\n"
|
||||
"ddb.geometry.heads = \"16\"\n"
|
||||
"ddb.geometry.heads = \"%d\"\n"
|
||||
"ddb.geometry.sectors = \"63\"\n"
|
||||
"ddb.adapterType = \"ide\"\n";
|
||||
"ddb.adapterType = \"%s\"\n";
|
||||
|
||||
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
|
||||
return -EINVAL;
|
||||
|
@ -1480,6 +1474,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
while (options && options->name) {
|
||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||
total_size = options->value.n;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_ADAPTER_TYPE)) {
|
||||
adapter_type = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
|
||||
backing_file = options->value.s;
|
||||
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
|
||||
|
@ -1489,6 +1485,20 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
}
|
||||
options++;
|
||||
}
|
||||
if (!adapter_type) {
|
||||
adapter_type = "ide";
|
||||
} else if (strcmp(adapter_type, "ide") &&
|
||||
strcmp(adapter_type, "buslogic") &&
|
||||
strcmp(adapter_type, "lsilogic") &&
|
||||
strcmp(adapter_type, "legacyESX")) {
|
||||
fprintf(stderr, "VMDK: Unknown adapter type: '%s'.\n", adapter_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (strcmp(adapter_type, "ide") != 0) {
|
||||
/* that's the number of heads with which vmware operates when
|
||||
creating, exporting, etc. vmdk files with a non-ide adapter type */
|
||||
number_heads = 255;
|
||||
}
|
||||
if (!fmt) {
|
||||
/* Default format to monolithicSparse */
|
||||
fmt = "monolithicSparse";
|
||||
|
@ -1517,7 +1527,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
if (backing_file) {
|
||||
char parent_filename[PATH_MAX];
|
||||
BlockDriverState *bs = bdrv_new("");
|
||||
ret = bdrv_open(bs, backing_file, 0, NULL);
|
||||
ret = bdrv_open(bs, backing_file, NULL, 0, NULL);
|
||||
if (ret != 0) {
|
||||
bdrv_delete(bs);
|
||||
return ret;
|
||||
|
@ -1576,7 +1586,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||
parent_desc_line,
|
||||
ext_desc_lines,
|
||||
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
||||
total_size / (int64_t)(63 * 16 * 512));
|
||||
total_size / (int64_t)(63 * number_heads * 512), number_heads,
|
||||
adapter_type);
|
||||
if (split || flat) {
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
|
@ -1660,6 +1671,12 @@ static QEMUOptionParameter vmdk_create_options[] = {
|
|||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_ADAPTER_TYPE,
|
||||
.type = OPT_STRING,
|
||||
.help = "Virtual adapter type, can be one of "
|
||||
"ide (default), lsilogic, buslogic or legacyESX"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = OPT_STRING,
|
||||
|
|
44
block/vpc.c
44
block/vpc.c
|
@ -155,7 +155,7 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_open(BlockDriverState *bs, int flags)
|
||||
static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int i;
|
||||
|
@ -163,24 +163,33 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
|||
struct vhd_dyndisk_header* dyndisk_header;
|
||||
uint8_t buf[HEADER_SIZE];
|
||||
uint32_t checksum;
|
||||
int err = -1;
|
||||
int disk_type = VHD_DYNAMIC;
|
||||
int ret;
|
||||
|
||||
if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
|
||||
ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
footer = (struct vhd_footer*) s->footer_buf;
|
||||
if (strncmp(footer->creator, "conectix", 8)) {
|
||||
int64_t offset = bdrv_getlength(bs->file);
|
||||
if (offset < HEADER_SIZE) {
|
||||
if (offset < 0) {
|
||||
ret = offset;
|
||||
goto fail;
|
||||
} else if (offset < HEADER_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* If a fixed disk, the footer is found only at the end of the file */
|
||||
if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE)
|
||||
!= HEADER_SIZE) {
|
||||
ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf,
|
||||
HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (strncmp(footer->creator, "conectix", 8)) {
|
||||
ret = -EMEDIUMTYPE;
|
||||
goto fail;
|
||||
}
|
||||
disk_type = VHD_FIXED;
|
||||
|
@ -203,19 +212,21 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
|||
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
if (bs->total_sectors >= 65535LL * 255 * 255) {
|
||||
err = -EFBIG;
|
||||
ret = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf,
|
||||
HEADER_SIZE) != HEADER_SIZE) {
|
||||
ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf,
|
||||
HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dyndisk_header = (struct vhd_dyndisk_header *) buf;
|
||||
|
||||
if (strncmp(dyndisk_header->magic, "cxsparse", 8)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -226,8 +237,10 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
|||
s->pagetable = g_malloc(s->max_table_entries * 4);
|
||||
|
||||
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||
if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
|
||||
s->max_table_entries * 4) != s->max_table_entries * 4) {
|
||||
|
||||
ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable,
|
||||
s->max_table_entries * 4);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -265,8 +278,13 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
|||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return err;
|
||||
|
||||
fail:
|
||||
g_free(s->pagetable);
|
||||
#ifdef CACHE
|
||||
g_free(s->pageentry_u8);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpc_reopen_prepare(BDRVReopenState *state,
|
||||
|
|
|
@ -529,13 +529,9 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
|
|||
/* if return_time==0, this returns the fat_date, else the fat_time */
|
||||
static uint16_t fat_datetime(time_t time,int return_time) {
|
||||
struct tm* t;
|
||||
#ifdef _WIN32
|
||||
t=localtime(&time); /* this is not thread safe */
|
||||
#else
|
||||
struct tm t1;
|
||||
t = &t1;
|
||||
localtime_r(&time,t);
|
||||
#endif
|
||||
if(return_time)
|
||||
return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
|
||||
return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
|
||||
|
@ -2834,7 +2830,7 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ret = bdrv_open(s->qcow, s->qcow_filename,
|
||||
ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "block/aio.h"
|
||||
#include "raw-aio.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/iov.h"
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
|
@ -80,15 +81,9 @@ static void win32_aio_process_completion(QEMUWin32AIOState *s,
|
|||
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(p, qiov->iov[i].iov_base, qiov->iov[i].iov_len);
|
||||
p += qiov->iov[i].iov_len;
|
||||
}
|
||||
g_free(waiocb->buf);
|
||||
iov_from_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size);
|
||||
}
|
||||
qemu_vfree(waiocb->buf);
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,13 +148,7 @@ BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
|||
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;
|
||||
}
|
||||
iov_to_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size);
|
||||
}
|
||||
waiocb->is_linear = false;
|
||||
} else {
|
||||
|
|
328
blockdev.c
328
blockdev.c
|
@ -5,6 +5,29 @@
|
|||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright (c) 2003-2008 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 "sysemu/blockdev.h"
|
||||
|
@ -22,6 +45,7 @@
|
|||
#include "sysemu/arch_init.h"
|
||||
|
||||
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
||||
extern QemuOptsList qemu_common_drive_opts;
|
||||
|
||||
static const char *const if_name[IF_COUNT] = {
|
||||
[IF_NONE] = "none",
|
||||
|
@ -191,6 +215,7 @@ static void drive_uninit(DriveInfo *dinfo)
|
|||
bdrv_delete(dinfo->bdrv);
|
||||
g_free(dinfo->id);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
g_free(dinfo->serial);
|
||||
g_free(dinfo);
|
||||
}
|
||||
|
||||
|
@ -255,7 +280,7 @@ static int parse_block_error_action(const char *buf, bool is_read)
|
|||
}
|
||||
}
|
||||
|
||||
static bool do_check_io_limits(BlockIOLimit *io_limits)
|
||||
static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp)
|
||||
{
|
||||
bool bps_flag;
|
||||
bool iops_flag;
|
||||
|
@ -269,13 +294,25 @@ static bool do_check_io_limits(BlockIOLimit *io_limits)
|
|||
&& ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
|
||||
|| (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
|
||||
if (bps_flag || iops_flag) {
|
||||
error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
|
||||
"cannot be used at the same time");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] < 0 ||
|
||||
io_limits->bps[BLOCK_IO_LIMIT_WRITE] < 0 ||
|
||||
io_limits->bps[BLOCK_IO_LIMIT_READ] < 0 ||
|
||||
io_limits->iops[BLOCK_IO_LIMIT_TOTAL] < 0 ||
|
||||
io_limits->iops[BLOCK_IO_LIMIT_WRITE] < 0 ||
|
||||
io_limits->iops[BLOCK_IO_LIMIT_READ] < 0) {
|
||||
error_setg(errp, "bps and iops values must be 0 or greater");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
||||
DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
{
|
||||
const char *buf;
|
||||
const char *file = NULL;
|
||||
|
@ -298,10 +335,37 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
|||
bool copy_on_read;
|
||||
bool locked;
|
||||
int ret;
|
||||
Error *error = NULL;
|
||||
QemuOpts *opts;
|
||||
QDict *bs_opts;
|
||||
const char *id;
|
||||
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
media = MEDIA_DISK;
|
||||
|
||||
/* Check common options by copying from all_opts to opts, all other options
|
||||
* are stored in bs_opts. */
|
||||
id = qemu_opts_id(all_opts);
|
||||
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
||||
if (error_is_set(&error)) {
|
||||
qerror_report_err(error);
|
||||
error_free(error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bs_opts = qdict_new();
|
||||
qemu_opts_to_qdict(all_opts, bs_opts);
|
||||
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
||||
if (error_is_set(&error)) {
|
||||
qerror_report_err(error);
|
||||
error_free(error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (id) {
|
||||
qdict_del(bs_opts, "id");
|
||||
}
|
||||
|
||||
/* extract parameters */
|
||||
bus_id = qemu_opt_get_number(opts, "bus", 0);
|
||||
unit_id = qemu_opt_get_number(opts, "unit", -1);
|
||||
|
@ -381,6 +445,13 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
|||
}
|
||||
}
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
|
||||
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
|
||||
error_report("invalid discard option");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_flags |= BDRV_O_CACHE_WB;
|
||||
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
|
||||
if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
|
||||
|
@ -430,9 +501,9 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
|||
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
|
||||
qemu_opt_get_number(opts, "iops_wr", 0);
|
||||
|
||||
if (!do_check_io_limits(&io_limits)) {
|
||||
error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
|
||||
"cannot be used at the same time");
|
||||
if (!do_check_io_limits(&io_limits, &error)) {
|
||||
error_report("%s", error_get_pretty(error));
|
||||
error_free(error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -547,10 +618,12 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
|||
dinfo->heads = heads;
|
||||
dinfo->secs = secs;
|
||||
dinfo->trans = translation;
|
||||
dinfo->opts = opts;
|
||||
dinfo->opts = all_opts;
|
||||
dinfo->refcount = 1;
|
||||
dinfo->serial = serial;
|
||||
dinfo->locked = locked;
|
||||
if (serial != NULL) {
|
||||
dinfo->serial = g_strdup(serial);
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
||||
|
||||
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
||||
|
@ -571,17 +644,20 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
|||
case IF_MTD:
|
||||
break;
|
||||
case IF_VIRTIO:
|
||||
{
|
||||
/* add virtio block device */
|
||||
opts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
QemuOpts *devopts;
|
||||
devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-s390");
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-pci");
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-pci");
|
||||
}
|
||||
qemu_opt_set(opts, "drive", dinfo->id);
|
||||
qemu_opt_set(devopts, "drive", dinfo->id);
|
||||
if (devaddr)
|
||||
qemu_opt_set(opts, "addr", devaddr);
|
||||
qemu_opt_set(devopts, "addr", devaddr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -619,18 +695,30 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
|||
error_report("warning: disabling copy_on_read on readonly drive");
|
||||
}
|
||||
|
||||
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
|
||||
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv);
|
||||
bs_opts = NULL;
|
||||
|
||||
if (ret < 0) {
|
||||
error_report("could not open disk image %s: %s",
|
||||
file, strerror(-ret));
|
||||
if (ret == -EMEDIUMTYPE) {
|
||||
error_report("could not open disk image %s: not in %s format",
|
||||
file, drv->format_name);
|
||||
} else {
|
||||
error_report("could not open disk image %s: %s",
|
||||
file, strerror(-ret));
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bdrv_key_required(dinfo->bdrv))
|
||||
autostart = 0;
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
return dinfo;
|
||||
|
||||
err:
|
||||
qemu_opts_del(opts);
|
||||
QDECREF(bs_opts);
|
||||
bdrv_delete(dinfo->bdrv);
|
||||
g_free(dinfo->id);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
|
@ -646,21 +734,17 @@ void do_commit(Monitor *mon, const QDict *qdict)
|
|||
|
||||
if (!strcmp(device, "all")) {
|
||||
ret = bdrv_commit_all();
|
||||
if (ret == -EBUSY) {
|
||||
qerror_report(QERR_DEVICE_IN_USE, device);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
||||
monitor_printf(mon, "Device '%s' not found\n", device);
|
||||
return;
|
||||
}
|
||||
ret = bdrv_commit(bs);
|
||||
if (ret == -EBUSY) {
|
||||
qerror_report(QERR_DEVICE_IN_USE, device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
monitor_printf(mon, "'commit' error for '%s': %s\n", device,
|
||||
strerror(-ret));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -794,7 +878,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
|||
bdrv_img_create(new_image_file, format,
|
||||
states->old_bs->filename,
|
||||
states->old_bs->drv->format_name,
|
||||
NULL, -1, flags, &local_err);
|
||||
NULL, -1, flags, &local_err, false);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto delete_and_fail;
|
||||
|
@ -803,7 +887,9 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
|||
|
||||
/* We will manually add the backing_hd field to the bs later */
|
||||
states->new_bs = bdrv_new("");
|
||||
ret = bdrv_open(states->new_bs, new_image_file,
|
||||
/* TODO Inherit bs->options or only take explicit options with an
|
||||
* extended QMP command? */
|
||||
ret = bdrv_open(states->new_bs, new_image_file, NULL,
|
||||
flags | BDRV_O_NO_BACKING, drv);
|
||||
if (ret != 0) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
|
||||
|
@ -904,7 +990,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
|
|||
int bdrv_flags, BlockDriver *drv,
|
||||
const char *password, Error **errp)
|
||||
{
|
||||
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
|
||||
if (bdrv_open(bs, filename, NULL, bdrv_flags, drv) < 0) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
|
||||
return;
|
||||
}
|
||||
|
@ -978,8 +1064,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
|||
io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
|
||||
io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
|
||||
|
||||
if (!do_check_io_limits(&io_limits)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
|
||||
if (!do_check_io_limits(&io_limits, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1192,16 +1277,19 @@ void qmp_block_commit(const char *device,
|
|||
drive_get_ref(drive_get_by_blockdev(bs));
|
||||
}
|
||||
|
||||
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
|
||||
|
||||
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_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
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;
|
||||
|
@ -1223,6 +1311,21 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
if (!has_mode) {
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
if (!has_granularity) {
|
||||
granularity = 0;
|
||||
}
|
||||
if (!has_buf_size) {
|
||||
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
||||
}
|
||||
|
||||
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, device);
|
||||
return;
|
||||
}
|
||||
if (granularity & (granularity - 1)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, device);
|
||||
return;
|
||||
}
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (!bs) {
|
||||
|
@ -1263,13 +1366,13 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
return;
|
||||
}
|
||||
|
||||
bdrv_get_geometry(bs, &size);
|
||||
size *= 512;
|
||||
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;
|
||||
bdrv_img_create(target, format,
|
||||
NULL, NULL, NULL, size, flags, &local_err);
|
||||
NULL, NULL, NULL, size, flags, &local_err, false);
|
||||
} else {
|
||||
switch (mode) {
|
||||
case NEW_IMAGE_MODE_EXISTING:
|
||||
|
@ -1280,7 +1383,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
bdrv_img_create(target, format,
|
||||
source->filename,
|
||||
source->drv->format_name,
|
||||
NULL, -1, flags, &local_err);
|
||||
NULL, size, flags, &local_err, false);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
|
@ -1292,8 +1395,11 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Mirroring takes care of copy-on-write using the source's backing
|
||||
* file.
|
||||
*/
|
||||
target_bs = bdrv_new("");
|
||||
ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv);
|
||||
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv);
|
||||
|
||||
if (ret < 0) {
|
||||
bdrv_delete(target_bs);
|
||||
|
@ -1301,18 +1407,8 @@ void qmp_drive_mirror(const char *device, const char *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,
|
||||
mirror_start(bs, target_bs, speed, granularity, buf_size, sync,
|
||||
on_source_error, on_target_error,
|
||||
block_job_cb, bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
bdrv_delete(target_bs);
|
||||
|
@ -1431,3 +1527,141 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
|||
bdrv_iterate(do_qmp_query_block_jobs_one, &prev);
|
||||
return dummy.next;
|
||||
}
|
||||
|
||||
QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "bus",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "bus number",
|
||||
},{
|
||||
.name = "unit",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "unit number (i.e. lun for scsi)",
|
||||
},{
|
||||
.name = "if",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
||||
},{
|
||||
.name = "index",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "index number",
|
||||
},{
|
||||
.name = "cyls",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of cylinders (ide disk geometry)",
|
||||
},{
|
||||
.name = "heads",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of heads (ide disk geometry)",
|
||||
},{
|
||||
.name = "secs",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of sectors (ide disk geometry)",
|
||||
},{
|
||||
.name = "trans",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "chs translation (auto, lba. none)",
|
||||
},{
|
||||
.name = "media",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "media type (disk, cdrom)",
|
||||
},{
|
||||
.name = "snapshot",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enable/disable snapshot mode",
|
||||
},{
|
||||
.name = "file",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "disk image",
|
||||
},{
|
||||
.name = "discard",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = "cache",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "host cache usage (none, writeback, writethrough, "
|
||||
"directsync, unsafe)",
|
||||
},{
|
||||
.name = "aio",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "host AIO implementation (threads, native)",
|
||||
},{
|
||||
.name = "format",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "disk format (raw, qcow2, ...)",
|
||||
},{
|
||||
.name = "serial",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "disk serial number",
|
||||
},{
|
||||
.name = "rerror",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "read error action",
|
||||
},{
|
||||
.name = "werror",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "write error action",
|
||||
},{
|
||||
.name = "addr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "pci address (virtio only)",
|
||||
},{
|
||||
.name = "readonly",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "open drive file as read-only",
|
||||
},{
|
||||
.name = "iops",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "limit total I/O operations per second",
|
||||
},{
|
||||
.name = "iops_rd",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "limit read operations per second",
|
||||
},{
|
||||
.name = "iops_wr",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "limit write operations per second",
|
||||
},{
|
||||
.name = "bps",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "limit total bytes per second",
|
||||
},{
|
||||
.name = "bps_rd",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "limit read bytes per second",
|
||||
},{
|
||||
.name = "bps_wr",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "limit write bytes per second",
|
||||
},{
|
||||
.name = "copy-on-read",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "copy read data from backing file into image file",
|
||||
},{
|
||||
.name = "boot",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "(deprecated, ignored)",
|
||||
},{
|
||||
.name = "locked",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "emulate a security locked drive",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
QemuOptsList qemu_drive_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
||||
.desc = {
|
||||
/*
|
||||
* no elements => accept any params
|
||||
* validation will happen later
|
||||
*/
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
|
|
@ -34,8 +34,6 @@
|
|||
#include "qemu/timer.h"
|
||||
#include "qemu/envlist.h"
|
||||
|
||||
#define DEBUG_LOGFILE "/tmp/qemu.log"
|
||||
|
||||
int singlestep;
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
unsigned long mmap_min_addr;
|
||||
|
@ -691,11 +689,12 @@ static void usage(void)
|
|||
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
|
||||
"\n"
|
||||
"Debug options:\n"
|
||||
"-d options activate log (default logfile=%s)\n"
|
||||
"-D logfile override default logfile location\n"
|
||||
"-p pagesize set the host page size to 'pagesize'\n"
|
||||
"-singlestep always run in singlestep mode\n"
|
||||
"-strace log system calls\n"
|
||||
"-d item1[,...] enable logging of specified items\n"
|
||||
" (use '-d help' for a list of log items)\n"
|
||||
"-D logfile write logs to 'logfile' (default stderr)\n"
|
||||
"-p pagesize set the host page size to 'pagesize'\n"
|
||||
"-singlestep always run in singlestep mode\n"
|
||||
"-strace log system calls\n"
|
||||
"\n"
|
||||
"Environment variables:\n"
|
||||
"QEMU_STRACE Print system calls and arguments similar to the\n"
|
||||
|
@ -709,8 +708,7 @@ static void usage(void)
|
|||
,
|
||||
TARGET_ARCH,
|
||||
interp_prefix,
|
||||
x86_stack_size,
|
||||
DEBUG_LOGFILE);
|
||||
x86_stack_size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -733,7 +731,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
const char *filename;
|
||||
const char *cpu_model;
|
||||
const char *log_file = DEBUG_LOGFILE;
|
||||
const char *log_file = NULL;
|
||||
const char *log_mask = NULL;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
struct image_info info1, *info = &info1;
|
||||
|
@ -861,20 +859,16 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
/* init debug */
|
||||
cpu_set_log_filename(log_file);
|
||||
qemu_set_log_filename(log_file);
|
||||
if (log_mask) {
|
||||
int mask;
|
||||
const CPULogItem *item;
|
||||
|
||||
mask = cpu_str_to_log_mask(log_mask);
|
||||
mask = qemu_str_to_log_mask(log_mask);
|
||||
if (!mask) {
|
||||
printf("Log items (comma separated):\n");
|
||||
for (item = cpu_log_items; item->mask != 0; item++) {
|
||||
printf("%-10s %s\n", item->name, item->help);
|
||||
}
|
||||
qemu_print_log_usage(stdout);
|
||||
exit(1);
|
||||
}
|
||||
cpu_set_log(mask);
|
||||
qemu_set_log(mask);
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
|
@ -917,7 +911,7 @@ int main(int argc, char **argv)
|
|||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
cpu_reset(ENV_GET_CPU(env));
|
||||
#endif
|
||||
thread_env = env;
|
||||
|
|
|
@ -74,7 +74,7 @@ void mmap_unlock(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
void *qemu_vmalloc(size_t size)
|
||||
static void *bsd_vmalloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
mmap_lock();
|
||||
|
@ -98,7 +98,7 @@ void *g_malloc(size_t size)
|
|||
{
|
||||
char * p;
|
||||
size += 16;
|
||||
p = qemu_vmalloc(size);
|
||||
p = bsd_vmalloc(size);
|
||||
*(size_t *)p = size;
|
||||
return p + 16;
|
||||
}
|
||||
|
|
|
@ -158,6 +158,7 @@ vnc_tls=""
|
|||
vnc_sasl=""
|
||||
vnc_jpeg=""
|
||||
vnc_png=""
|
||||
vnc_ws=""
|
||||
xen=""
|
||||
xen_ctrl_version=""
|
||||
xen_pci_passthrough=""
|
||||
|
@ -176,6 +177,8 @@ strip_opt="yes"
|
|||
tcg_interpreter="no"
|
||||
bigendian="no"
|
||||
mingw32="no"
|
||||
gcov="no"
|
||||
gcov_tool="gcov"
|
||||
EXESUF=""
|
||||
prefix="/usr/local"
|
||||
mandir="\${prefix}/share/man"
|
||||
|
@ -212,7 +215,6 @@ trace_backend="nop"
|
|||
trace_file="trace"
|
||||
spice=""
|
||||
rbd=""
|
||||
smartcard=""
|
||||
smartcard_nss=""
|
||||
usb_redir=""
|
||||
opengl=""
|
||||
|
@ -223,6 +225,10 @@ libiscsi=""
|
|||
coroutine=""
|
||||
seccomp=""
|
||||
glusterfs=""
|
||||
virtio_blk_data_plane=""
|
||||
gtk=""
|
||||
gtkabi="2.0"
|
||||
tpm="no"
|
||||
|
||||
# parse CC options first
|
||||
for opt do
|
||||
|
@ -237,8 +243,10 @@ for opt do
|
|||
--cpu=*) cpu="$optarg"
|
||||
;;
|
||||
--extra-cflags=*) QEMU_CFLAGS="$optarg $QEMU_CFLAGS"
|
||||
EXTRA_CFLAGS="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="$optarg $LDFLAGS"
|
||||
EXTRA_LDFLAGS="$optarg"
|
||||
;;
|
||||
--enable-debug-info) debug_info="yes"
|
||||
;;
|
||||
|
@ -261,6 +269,8 @@ else
|
|||
fi
|
||||
|
||||
ar="${AR-${cross_prefix}ar}"
|
||||
as="${AS-${cross_prefix}as}"
|
||||
cpp="${CPP-$cc -E}"
|
||||
objcopy="${OBJCOPY-${cross_prefix}objcopy}"
|
||||
ld="${LD-${cross_prefix}ld}"
|
||||
libtool="${LIBTOOL-${cross_prefix}libtool}"
|
||||
|
@ -599,6 +609,8 @@ for opt do
|
|||
;;
|
||||
--python=*) python="$optarg"
|
||||
;;
|
||||
--gcov=*) gcov_tool="$optarg"
|
||||
;;
|
||||
--smbd=*) smbd="$optarg"
|
||||
;;
|
||||
--extra-cflags=*)
|
||||
|
@ -619,6 +631,8 @@ for opt do
|
|||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
--enable-gcov) gcov="yes"
|
||||
;;
|
||||
--static)
|
||||
static="yes"
|
||||
LDFLAGS="-static $LDFLAGS"
|
||||
|
@ -656,6 +670,8 @@ for opt do
|
|||
;;
|
||||
--without-system-pixman) pixman="internal"
|
||||
;;
|
||||
--without-pixman) pixman="none"
|
||||
;;
|
||||
--disable-sdl) sdl="no"
|
||||
;;
|
||||
--enable-sdl) sdl="yes"
|
||||
|
@ -712,6 +728,10 @@ for opt do
|
|||
;;
|
||||
--enable-vnc-png) vnc_png="yes"
|
||||
;;
|
||||
--disable-vnc-ws) vnc_ws="no"
|
||||
;;
|
||||
--enable-vnc-ws) vnc_ws="yes"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
;;
|
||||
--disable-uuid) uuid="no"
|
||||
|
@ -850,10 +870,6 @@ for opt do
|
|||
;;
|
||||
--enable-xfsctl) xfs="yes"
|
||||
;;
|
||||
--disable-smartcard) smartcard="no"
|
||||
;;
|
||||
--enable-smartcard) smartcard="yes"
|
||||
;;
|
||||
--disable-smartcard-nss) smartcard_nss="no"
|
||||
;;
|
||||
--enable-smartcard-nss) smartcard_nss="yes"
|
||||
|
@ -880,6 +896,18 @@ for opt do
|
|||
;;
|
||||
--enable-glusterfs) glusterfs="yes"
|
||||
;;
|
||||
--disable-virtio-blk-data-plane) virtio_blk_data_plane="no"
|
||||
;;
|
||||
--enable-virtio-blk-data-plane) virtio_blk_data_plane="yes"
|
||||
;;
|
||||
--disable-gtk) gtk="no"
|
||||
;;
|
||||
--enable-gtk) gtk="yes"
|
||||
;;
|
||||
--with-gtkabi=*) gtkabi="$optarg"
|
||||
;;
|
||||
--enable-tpm) tpm="yes"
|
||||
;;
|
||||
*) echo "ERROR: unknown option $opt"; show_help="yes"
|
||||
;;
|
||||
esac
|
||||
|
@ -961,6 +989,10 @@ microblaze-linux-user \
|
|||
microblazeel-linux-user \
|
||||
mips-linux-user \
|
||||
mipsel-linux-user \
|
||||
mips64-linux-user \
|
||||
mips64el-linux-user \
|
||||
mipsn32-linux-user \
|
||||
mipsn32el-linux-user \
|
||||
or32-linux-user \
|
||||
ppc-linux-user \
|
||||
ppc64-linux-user \
|
||||
|
@ -1030,6 +1062,8 @@ echo " --disable-strip disable stripping binaries"
|
|||
echo " --disable-werror disable compilation abort on warning"
|
||||
echo " --disable-sdl disable SDL"
|
||||
echo " --enable-sdl enable SDL"
|
||||
echo " --disable-gtk disable gtk UI"
|
||||
echo " --enable-gtk enable gtk UI"
|
||||
echo " --disable-virtfs disable VirtFS"
|
||||
echo " --enable-virtfs enable VirtFS"
|
||||
echo " --disable-vnc disable VNC"
|
||||
|
@ -1057,6 +1091,8 @@ echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server"
|
|||
echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server"
|
||||
echo " --disable-vnc-png disable PNG compression for VNC server (default)"
|
||||
echo " --enable-vnc-png enable PNG compression for VNC server"
|
||||
echo " --disable-vnc-ws disable Websockets support for VNC server"
|
||||
echo " --enable-vnc-ws enable Websockets support for VNC server"
|
||||
echo " --disable-curses disable curses output"
|
||||
echo " --enable-curses enable curses output"
|
||||
echo " --disable-curl disable curl connectivity"
|
||||
|
@ -1089,7 +1125,6 @@ echo " --fmod-inc path to FMOD includes"
|
|||
echo " --oss-lib path to OSS library"
|
||||
echo " --enable-uname-release=R Return R for uname -r in usermode emulation"
|
||||
echo " --cpu=CPU Build for host CPU [$cpu]"
|
||||
echo " --sparc_cpu=V Build qemu for Sparc architecture v7, v8, v8plus, v8plusa, v9"
|
||||
echo " --disable-uuid disable uuid support"
|
||||
echo " --enable-uuid enable uuid support"
|
||||
echo " --disable-vde disable support for vde network"
|
||||
|
@ -1114,8 +1149,6 @@ echo " --enable-spice enable spice"
|
|||
echo " --enable-rbd enable building the rados block device (rbd)"
|
||||
echo " --disable-libiscsi disable iscsi support"
|
||||
echo " --enable-libiscsi enable iscsi support"
|
||||
echo " --disable-smartcard disable smartcard support"
|
||||
echo " --enable-smartcard enable smartcard support"
|
||||
echo " --disable-smartcard-nss disable smartcard nss support"
|
||||
echo " --enable-smartcard-nss enable smartcard nss support"
|
||||
echo " --disable-usb-redir disable usb network redirection support"
|
||||
|
@ -1128,6 +1161,9 @@ echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
|
|||
echo " gthread, ucontext, sigaltstack, windows"
|
||||
echo " --enable-glusterfs enable GlusterFS backend"
|
||||
echo " --disable-glusterfs disable GlusterFS backend"
|
||||
echo " --enable-gcov enable test coverage analysis with gcov"
|
||||
echo " --gcov=GCOV use specified gcov [$gcov_tool]"
|
||||
echo " --enable-tpm enable TPM support"
|
||||
echo ""
|
||||
echo "NOTE: The object files are built at the place where configure is launched"
|
||||
exit 1
|
||||
|
@ -1162,7 +1198,7 @@ fi
|
|||
z_version=`cut -f3 -d. $source_path/VERSION`
|
||||
|
||||
if test -z "$werror" ; then
|
||||
if test "$z_version" = "50" -a \
|
||||
if test -d "$source_path/.git" -a \
|
||||
"$linux" = "yes" ; then
|
||||
werror="yes"
|
||||
else
|
||||
|
@ -1416,7 +1452,7 @@ fi
|
|||
|
||||
if test "$seccomp" != "no" ; then
|
||||
if $pkg_config --atleast-version=1.0.0 libseccomp --modversion >/dev/null 2>&1; then
|
||||
LIBS=`$pkg_config --libs libseccomp`
|
||||
libs_softmmu="$libs_softmmu `$pkg_config --libs libseccomp`"
|
||||
seccomp="yes"
|
||||
else
|
||||
if test "$seccomp" = "yes"; then
|
||||
|
@ -1617,6 +1653,36 @@ if test "$sparse" != "no" ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# GTK probe
|
||||
|
||||
if test "$gtk" != "no"; then
|
||||
gtkpackage="gtk+-$gtkabi"
|
||||
if test "$gtkabi" = "3.0" ; then
|
||||
gtkversion="3.0.0"
|
||||
vtepackage="vte-2.90"
|
||||
vteversion="0.32.0"
|
||||
else
|
||||
gtkversion="2.18.0"
|
||||
vtepackage="vte"
|
||||
vteversion="0.24.0"
|
||||
fi
|
||||
if $pkg_config --exists "$gtkpackage >= $gtkversion" && \
|
||||
$pkg_config --exists "$vtepackage >= $vteversion"; then
|
||||
gtk_cflags=`$pkg_config --cflags $gtkpackage 2>/dev/null`
|
||||
gtk_libs=`$pkg_config --libs $gtkpackage 2>/dev/null`
|
||||
vte_cflags=`$pkg_config --cflags $vtepackage 2>/dev/null`
|
||||
vte_libs=`$pkg_config --libs $vtepackage 2>/dev/null`
|
||||
libs_softmmu="$gtk_libs $vte_libs $libs_softmmu"
|
||||
gtk="yes"
|
||||
else
|
||||
if test "$gtk" = "yes" ; then
|
||||
feature_not_found "gtk"
|
||||
fi
|
||||
gtk="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# SDL probe
|
||||
|
||||
|
@ -1701,8 +1767,8 @@ EOF
|
|||
fi
|
||||
|
||||
##########################################
|
||||
# VNC TLS detection
|
||||
if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then
|
||||
# VNC TLS/WS detection
|
||||
if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then
|
||||
cat > $TMPC <<EOF
|
||||
#include <gnutls/gnutls.h>
|
||||
int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
|
||||
|
@ -1710,14 +1776,23 @@ EOF
|
|||
vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
|
||||
vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
|
||||
if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then
|
||||
vnc_tls=yes
|
||||
if test "$vnc_tls" != "no" ; then
|
||||
vnc_tls=yes
|
||||
fi
|
||||
if test "$vnc_ws" != "no" ; then
|
||||
vnc_ws=yes
|
||||
fi
|
||||
libs_softmmu="$vnc_tls_libs $libs_softmmu"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
|
||||
else
|
||||
if test "$vnc_tls" = "yes" ; then
|
||||
feature_not_found "vnc-tls"
|
||||
fi
|
||||
if test "$vnc_ws" = "yes" ; then
|
||||
feature_not_found "vnc-ws"
|
||||
fi
|
||||
vnc_tls=no
|
||||
vnc_ws=no
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -2028,7 +2103,7 @@ fi
|
|||
if test "$mingw32" = "yes" ; then
|
||||
curses_list="-lpdcurses"
|
||||
else
|
||||
curses_list="-lncurses -lcurses"
|
||||
curses_list="-lncurses:-lcurses:$($pkg_config --libs ncurses 2>/dev/null)"
|
||||
fi
|
||||
|
||||
if test "$curses" != "no" ; then
|
||||
|
@ -2041,13 +2116,16 @@ int main(void) {
|
|||
return s != 0;
|
||||
}
|
||||
EOF
|
||||
IFS=:
|
||||
for curses_lib in $curses_list; do
|
||||
unset IFS
|
||||
if compile_prog "" "$curses_lib" ; then
|
||||
curses_found=yes
|
||||
libs_softmmu="$curses_lib $libs_softmmu"
|
||||
break
|
||||
fi
|
||||
done
|
||||
unset IFS
|
||||
if test "$curses_found" = "yes" ; then
|
||||
curses=yes
|
||||
else
|
||||
|
@ -2130,13 +2208,25 @@ fi
|
|||
# pixman support probe
|
||||
|
||||
if test "$pixman" = ""; then
|
||||
if $pkg_config pixman-1 > /dev/null 2>&1; then
|
||||
if test "$want_tools" = "no" -a "$softmmu" = "no"; then
|
||||
pixman="none"
|
||||
elif $pkg_config pixman-1 > /dev/null 2>&1; then
|
||||
pixman="system"
|
||||
else
|
||||
pixman="internal"
|
||||
fi
|
||||
fi
|
||||
if test "$pixman" = "system"; then
|
||||
if test "$pixman" = "none"; then
|
||||
if test "$want_tools" != "no" -o "$softmmu" != "no"; then
|
||||
echo "ERROR: pixman disabled but system emulation or tools build"
|
||||
echo " enabled. You can turn off pixman only if you also"
|
||||
echo " disable all system emulation targets and the tools"
|
||||
echo " build with '--disable-tools --disable-system'."
|
||||
exit 1
|
||||
fi
|
||||
pixman_cflags=
|
||||
pixman_libs=
|
||||
elif test "$pixman" = "system"; then
|
||||
pixman_cflags=`$pkg_config --cflags pixman-1 2>/dev/null`
|
||||
pixman_libs=`$pkg_config --libs pixman-1 2>/dev/null`
|
||||
else
|
||||
|
@ -2259,6 +2349,17 @@ EOF
|
|||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# adjust virtio-blk-data-plane based on linux-aio
|
||||
|
||||
if test "$virtio_blk_data_plane" = "yes" -a \
|
||||
"$linux_aio" != "yes" ; then
|
||||
echo "Error: virtio-blk-data-plane requires Linux AIO, please try --enable-linux-aio"
|
||||
exit 1
|
||||
elif test -z "$virtio_blk_data_plane" ; then
|
||||
virtio_blk_data_plane=$linux_aio
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# attr probe
|
||||
|
||||
|
@ -2323,6 +2424,7 @@ int main(void) { return 0; }
|
|||
EOF
|
||||
if compile_prog "" "$fdt_libs" ; then
|
||||
fdt=yes
|
||||
libs_softmmu="$libs_softmmu $fdt_libs"
|
||||
else
|
||||
if test "$fdt" = "yes" ; then
|
||||
feature_not_found "fdt"
|
||||
|
@ -2345,7 +2447,7 @@ if test "$opengl" != "no" ; then
|
|||
int main(void) { return GL_VERSION != 0; }
|
||||
EOF
|
||||
else
|
||||
opengl_libs="-lGL"
|
||||
opengl_libs="-lGL -lX11"
|
||||
cat > $TMPC << EOF
|
||||
#include <X11/Xlib.h>
|
||||
#include <GL/gl.h>
|
||||
|
@ -2560,6 +2662,22 @@ if compile_prog "" "" ; then
|
|||
fallocate=yes
|
||||
fi
|
||||
|
||||
# check for fallocate hole punching
|
||||
fallocate_punch_hole=no
|
||||
cat > $TMPC << EOF
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
fallocate(0, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
fallocate_punch_hole=yes
|
||||
fi
|
||||
|
||||
# check for sync_file_range
|
||||
sync_file_range=no
|
||||
cat > $TMPC << EOF
|
||||
|
@ -2659,6 +2777,20 @@ if compile_prog "" "" ; then
|
|||
epoll_pwait=yes
|
||||
fi
|
||||
|
||||
# check for sendfile support
|
||||
sendfile=no
|
||||
cat > $TMPC << EOF
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return sendfile(0, 0, 0, 0);
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
sendfile=yes
|
||||
fi
|
||||
|
||||
# Check if tools are available to build documentation.
|
||||
if test "$docs" != "no" ; then
|
||||
if has makeinfo && has pod2man; then
|
||||
|
@ -2681,7 +2813,7 @@ if compile_prog "" "" ; then
|
|||
byteswap_h=yes
|
||||
fi
|
||||
|
||||
# Search for bswap_32 function
|
||||
# Search for bswap32 function
|
||||
bswap_h=no
|
||||
cat > $TMPC << EOF
|
||||
#include <sys/endian.h>
|
||||
|
@ -2703,7 +2835,13 @@ if test "$libiscsi" != "no" ; then
|
|||
#include <iscsi/iscsi.h>
|
||||
int main(void) { iscsi_unmap_sync(NULL,0,0,0,NULL,0); return 0; }
|
||||
EOF
|
||||
if compile_prog "" "-liscsi" ; then
|
||||
if $pkg_config --atleast-version=1.7.0 libiscsi --modversion >/dev/null 2>&1; then
|
||||
libiscsi="yes"
|
||||
libiscsi_cflags=$($pkg_config --cflags libiscsi 2>/dev/null)
|
||||
libiscsi_libs=$($pkg_config --libs libiscsi 2>/dev/null)
|
||||
CFLAGS="$CFLAGS $libiscsi_cflags"
|
||||
LIBS="$LIBS $libiscsi_libs"
|
||||
elif compile_prog "" "-liscsi" ; then
|
||||
libiscsi="yes"
|
||||
LIBS="$LIBS -liscsi"
|
||||
else
|
||||
|
@ -2771,7 +2909,7 @@ EOF
|
|||
spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null)
|
||||
spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null)
|
||||
if $pkg_config --atleast-version=0.12.0 spice-server >/dev/null 2>&1 && \
|
||||
$pkg_config --atleast-version=0.12.2 spice-protocol > /dev/null 2>&1 && \
|
||||
$pkg_config --atleast-version=0.12.3 spice-protocol > /dev/null 2>&1 && \
|
||||
compile_prog "$spice_cflags" "$spice_libs" ; then
|
||||
spice="yes"
|
||||
libs_softmmu="$libs_softmmu $spice_libs"
|
||||
|
@ -2787,47 +2925,42 @@ EOF
|
|||
fi
|
||||
|
||||
# check for libcacard for smartcard support
|
||||
if test "$smartcard" != "no" ; then
|
||||
smartcard="yes"
|
||||
smartcard_cflags=""
|
||||
# TODO - what's the minimal nss version we support?
|
||||
if test "$smartcard_nss" != "no"; then
|
||||
cat > $TMPC << EOF
|
||||
smartcard_cflags=""
|
||||
# TODO - what's the minimal nss version we support?
|
||||
if test "$smartcard_nss" != "no"; then
|
||||
cat > $TMPC << EOF
|
||||
#include <pk11pub.h>
|
||||
int main(void) { PK11_FreeSlot(0); return 0; }
|
||||
EOF
|
||||
smartcard_includes="-I\$(SRC_PATH)/libcacard"
|
||||
libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs"
|
||||
libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags"
|
||||
test_cflags="$libcacard_cflags"
|
||||
# The header files in nss < 3.13.3 have a bug which causes them to
|
||||
# emit a warning. If we're going to compile QEMU with -Werror, then
|
||||
# test that the headers don't have this bug. Otherwise we would pass
|
||||
# the configure test but fail to compile QEMU later.
|
||||
if test "$werror" = "yes"; then
|
||||
test_cflags="-Werror $test_cflags"
|
||||
fi
|
||||
if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \
|
||||
compile_prog "$test_cflags" "$libcacard_libs"; then
|
||||
smartcard_nss="yes"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags"
|
||||
QEMU_INCLUDES="$QEMU_INCLUDES $smartcard_includes"
|
||||
libs_softmmu="$libcacard_libs $libs_softmmu"
|
||||
else
|
||||
if test "$smartcard_nss" = "yes"; then
|
||||
feature_not_found "nss"
|
||||
fi
|
||||
smartcard_nss="no"
|
||||
fi
|
||||
smartcard_includes="-I\$(SRC_PATH)/libcacard"
|
||||
libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs"
|
||||
libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags"
|
||||
test_cflags="$libcacard_cflags"
|
||||
# The header files in nss < 3.13.3 have a bug which causes them to
|
||||
# emit a warning. If we're going to compile QEMU with -Werror, then
|
||||
# test that the headers don't have this bug. Otherwise we would pass
|
||||
# the configure test but fail to compile QEMU later.
|
||||
if test "$werror" = "yes"; then
|
||||
test_cflags="-Werror $test_cflags"
|
||||
fi
|
||||
if test -n "$libtool" &&
|
||||
$pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \
|
||||
compile_prog "$test_cflags" "$libcacard_libs"; then
|
||||
smartcard_nss="yes"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags"
|
||||
QEMU_INCLUDES="$QEMU_INCLUDES $smartcard_includes"
|
||||
libs_softmmu="$libcacard_libs $libs_softmmu"
|
||||
else
|
||||
if test "$smartcard_nss" = "yes"; then
|
||||
feature_not_found "nss"
|
||||
fi
|
||||
smartcard_nss="no"
|
||||
fi
|
||||
fi
|
||||
if test "$smartcard" = "no" ; then
|
||||
smartcard_nss="no"
|
||||
fi
|
||||
|
||||
# check for usbredirparser for usb network redirection support
|
||||
if test "$usb_redir" != "no" ; then
|
||||
if $pkg_config --atleast-version=0.5.3 libusbredirparser-0.5 >/dev/null 2>&1 ; then
|
||||
if $pkg_config --atleast-version=0.6 libusbredirparser-0.5 >/dev/null 2>&1 ; then
|
||||
usb_redir="yes"
|
||||
usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null)
|
||||
usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null)
|
||||
|
@ -3039,20 +3172,27 @@ if compile_prog "" "" ; then
|
|||
fi
|
||||
|
||||
########################################
|
||||
# check whether we can disable the -Wunused-but-set-variable
|
||||
# option with a pragma (this is needed to silence a warning in
|
||||
# some versions of the valgrind VALGRIND_STACK_DEREGISTER macro.)
|
||||
# This test has to be compiled with -Werror as otherwise an
|
||||
# unknown pragma is only a warning.
|
||||
# check whether we can disable warning option with a pragma (this is needed
|
||||
# to silence warnings in the headers of some versions of external libraries).
|
||||
# This test has to be compiled with -Werror as otherwise an unknown pragma is
|
||||
# only a warning.
|
||||
#
|
||||
# If we can't selectively disable warning in the code, disable -Werror so that
|
||||
# the build doesn't fail anyway.
|
||||
|
||||
pragma_disable_unused_but_set=no
|
||||
cat > $TMPC << EOF
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "-Werror" "" ; then
|
||||
pragma_disable_unused_but_set=yes
|
||||
pragma_diagnostic_available=yes
|
||||
else
|
||||
werror=no
|
||||
fi
|
||||
|
||||
########################################
|
||||
|
@ -3085,14 +3225,49 @@ if compile_prog "" "" ; then
|
|||
has_environ=yes
|
||||
fi
|
||||
|
||||
########################################
|
||||
# check if cpuid.h is usable.
|
||||
|
||||
cpuid_h=no
|
||||
cat > $TMPC << EOF
|
||||
#include <cpuid.h>
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
cpuid_h=yes
|
||||
fi
|
||||
|
||||
########################################
|
||||
# check if __[u]int128_t is usable.
|
||||
|
||||
int128=no
|
||||
cat > $TMPC << EOF
|
||||
__int128_t a;
|
||||
__uint128_t b;
|
||||
int main (void) {
|
||||
a = a + b;
|
||||
b = a * b;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
int128=yes
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# End of CC checks
|
||||
# After here, no more $cc or $ld runs
|
||||
|
||||
if test "$debug" = "no" ; then
|
||||
if test "$gcov" = "yes" ; then
|
||||
CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS"
|
||||
LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS"
|
||||
elif test "$debug" = "no" ; then
|
||||
CFLAGS="-O2 -D_FORTIFY_SOURCE=2 $CFLAGS"
|
||||
fi
|
||||
|
||||
|
||||
# Disable zero malloc errors for official releases unless explicitly told to
|
||||
# enable/disable
|
||||
if test -z "$zero_malloc" ; then
|
||||
|
@ -3132,6 +3307,7 @@ fi
|
|||
|
||||
qemu_confdir=$sysconfdir$confsuffix
|
||||
qemu_datadir=$datadir$confsuffix
|
||||
qemu_localedir="$datadir/locale"
|
||||
|
||||
tools=""
|
||||
if test "$want_tools" = "yes" ; then
|
||||
|
@ -3158,9 +3334,6 @@ if test "$softmmu" = yes ; then
|
|||
tools="qemu-ga\$(EXESUF) $tools"
|
||||
fi
|
||||
fi
|
||||
if test "$smartcard_nss" = "yes" ; then
|
||||
tools="vscclient\$(EXESUF) $tools"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Mac OS X ships with a broken assembler
|
||||
|
@ -3218,6 +3391,7 @@ if test "$darwin" = "yes" ; then
|
|||
fi
|
||||
echo "pixman $pixman"
|
||||
echo "SDL support $sdl"
|
||||
echo "GTK support $gtk"
|
||||
echo "curses support $curses"
|
||||
echo "curl support $curl"
|
||||
echo "mingw32 support $mingw32"
|
||||
|
@ -3232,6 +3406,7 @@ if test "$vnc" = "yes" ; then
|
|||
echo "VNC SASL support $vnc_sasl"
|
||||
echo "VNC JPEG support $vnc_jpeg"
|
||||
echo "VNC PNG support $vnc_png"
|
||||
echo "VNC WS support $vnc_ws"
|
||||
fi
|
||||
if test -n "$sparc_cpu"; then
|
||||
echo "Target Sparc Arch $sparc_cpu"
|
||||
|
@ -3273,6 +3448,10 @@ echo "build guest agent $guest_agent"
|
|||
echo "seccomp support $seccomp"
|
||||
echo "coroutine backend $coroutine_backend"
|
||||
echo "GlusterFS support $glusterfs"
|
||||
echo "virtio-blk-data-plane $virtio_blk_data_plane"
|
||||
echo "gcov $gcov_tool"
|
||||
echo "gcov enabled $gcov"
|
||||
echo "TPM support $tpm"
|
||||
|
||||
if test "$sdl_too_old" = "yes"; then
|
||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||
|
@ -3281,6 +3460,8 @@ fi
|
|||
config_host_mak="config-host.mak"
|
||||
config_host_ld="config-host.ld"
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" >config-all-disas.mak
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $config_host_mak
|
||||
printf "# Configured with:" >> $config_host_mak
|
||||
printf " '%s'" "$0" "$@" >> $config_host_mak
|
||||
|
@ -3299,6 +3480,9 @@ echo "qemu_datadir=$qemu_datadir" >> $config_host_mak
|
|||
echo "qemu_docdir=$qemu_docdir" >> $config_host_mak
|
||||
echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
|
||||
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
|
||||
echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
|
||||
echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
|
||||
echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
|
||||
|
||||
echo "ARCH=$ARCH" >> $config_host_mak
|
||||
if test "$debug_tcg" = "yes" ; then
|
||||
|
@ -3403,6 +3587,10 @@ fi
|
|||
if test "$vnc_png" = "yes" ; then
|
||||
echo "CONFIG_VNC_PNG=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vnc_ws" = "yes" ; then
|
||||
echo "CONFIG_VNC_WS=y" >> $config_host_mak
|
||||
echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak
|
||||
fi
|
||||
if test "$fnmatch" = "yes" ; then
|
||||
echo "CONFIG_FNMATCH=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -3451,6 +3639,9 @@ fi
|
|||
if test "$fallocate" = "yes" ; then
|
||||
echo "CONFIG_FALLOCATE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$fallocate_punch_hole" = "yes" ; then
|
||||
echo "CONFIG_FALLOCATE_PUNCH_HOLE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$sync_file_range" = "yes" ; then
|
||||
echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -3469,6 +3660,9 @@ fi
|
|||
if test "$epoll_pwait" = "yes" ; then
|
||||
echo "CONFIG_EPOLL_PWAIT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$sendfile" = "yes" ; then
|
||||
echo "CONFIG_SENDFILE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$inotify" = "yes" ; then
|
||||
echo "CONFIG_INOTIFY=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -3493,6 +3687,11 @@ if test "$bluez" = "yes" ; then
|
|||
echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
|
||||
fi
|
||||
echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
|
||||
if test "$gtk" = "yes" ; then
|
||||
echo "CONFIG_GTK=y" >> $config_host_mak
|
||||
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
|
||||
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
||||
fi
|
||||
if test "$xen" = "yes" ; then
|
||||
echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
|
||||
echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
|
||||
|
@ -3544,10 +3743,6 @@ if test "$spice" = "yes" ; then
|
|||
echo "CONFIG_SPICE=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$smartcard" = "yes" ; then
|
||||
echo "CONFIG_SMARTCARD=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$smartcard_nss" = "yes" ; then
|
||||
echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
|
||||
echo "libcacard_libs=$libcacard_libs" >> $config_host_mak
|
||||
|
@ -3598,8 +3793,8 @@ if test "$linux_magic_h" = "yes" ; then
|
|||
echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$pragma_disable_unused_but_set" = "yes" ; then
|
||||
echo "CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET=y" >> $config_host_mak
|
||||
if test "$pragma_diagnostic_available" = "yes" ; then
|
||||
echo "CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$valgrind_h" = "yes" ; then
|
||||
|
@ -3610,14 +3805,26 @@ if test "$has_environ" = "yes" ; then
|
|||
echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$cpuid_h" = "yes" ; then
|
||||
echo "CONFIG_CPUID_H=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$int128" = "yes" ; then
|
||||
echo "CONFIG_INT128=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$glusterfs" = "yes" ; then
|
||||
echo "CONFIG_GLUSTERFS=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$virtio_blk_data_plane" = "yes" ; then
|
||||
echo "CONFIG_VIRTIO_BLK_DATA_PLANE=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
# USB host support
|
||||
case "$usb" in
|
||||
linux)
|
||||
echo "HOST_USB=linux" >> $config_host_mak
|
||||
echo "HOST_USB=linux legacy" >> $config_host_mak
|
||||
;;
|
||||
bsd)
|
||||
echo "HOST_USB=bsd" >> $config_host_mak
|
||||
|
@ -3663,13 +3870,21 @@ echo "MAKE=$make" >> $config_host_mak
|
|||
echo "INSTALL=$install" >> $config_host_mak
|
||||
echo "INSTALL_DIR=$install -d -m 0755" >> $config_host_mak
|
||||
echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak
|
||||
echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak
|
||||
if test -n "$libtool"; then
|
||||
echo "INSTALL_PROG=\$(LIBTOOL) --mode=install $install -c -m 0755" >> $config_host_mak
|
||||
echo "INSTALL_LIB=\$(LIBTOOL) --mode=install $install -c -m 0644" >> $config_host_mak
|
||||
else
|
||||
echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak
|
||||
echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak
|
||||
fi
|
||||
echo "PYTHON=$python" >> $config_host_mak
|
||||
echo "CC=$cc" >> $config_host_mak
|
||||
echo "CC_I386=$cc_i386" >> $config_host_mak
|
||||
echo "HOST_CC=$host_cc" >> $config_host_mak
|
||||
echo "OBJCC=$objcc" >> $config_host_mak
|
||||
echo "AR=$ar" >> $config_host_mak
|
||||
echo "AS=$as" >> $config_host_mak
|
||||
echo "CPP=$cpp" >> $config_host_mak
|
||||
echo "OBJCOPY=$objcopy" >> $config_host_mak
|
||||
echo "LD=$ld" >> $config_host_mak
|
||||
echo "WINDRES=$windres" >> $config_host_mak
|
||||
|
@ -3696,6 +3911,10 @@ echo "EXESUF=$EXESUF" >> $config_host_mak
|
|||
echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
|
||||
echo "POD2MAN=$POD2MAN" >> $config_host_mak
|
||||
echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak
|
||||
if test "$gcov" = "yes" ; then
|
||||
echo "CONFIG_GCOV=y" >> $config_host_mak
|
||||
echo "GCOV=$gcov_tool" >> $config_host_mak
|
||||
fi
|
||||
|
||||
# generate list of library paths for linker script
|
||||
|
||||
|
@ -3808,7 +4027,6 @@ case "$target_arch2" in
|
|||
target_nptl="yes"
|
||||
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
|
||||
target_llong_alignment=4
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
cris)
|
||||
target_nptl="yes"
|
||||
|
@ -3826,7 +4044,6 @@ case "$target_arch2" in
|
|||
TARGET_ARCH=microblaze
|
||||
bflt="yes"
|
||||
target_nptl="yes"
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
mips|mipsel)
|
||||
TARGET_ARCH=mips
|
||||
|
@ -3834,9 +4051,10 @@ case "$target_arch2" in
|
|||
target_nptl="yes"
|
||||
;;
|
||||
mipsn32|mipsn32el)
|
||||
TARGET_ARCH=mipsn32
|
||||
TARGET_ARCH=mips64
|
||||
TARGET_BASE_ARCH=mips
|
||||
echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
;;
|
||||
mips64|mips64el)
|
||||
TARGET_ARCH=mips64
|
||||
|
@ -3851,21 +4069,18 @@ case "$target_arch2" in
|
|||
ppc)
|
||||
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||
target_nptl="yes"
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
ppcemb)
|
||||
TARGET_BASE_ARCH=ppc
|
||||
TARGET_ABI_DIR=ppc
|
||||
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||
target_nptl="yes"
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
ppc64)
|
||||
TARGET_BASE_ARCH=ppc
|
||||
TARGET_ABI_DIR=ppc
|
||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||
target_long_alignment=8
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
ppc64abi32)
|
||||
TARGET_ARCH=ppc64
|
||||
|
@ -3873,7 +4088,6 @@ case "$target_arch2" in
|
|||
TARGET_ABI_DIR=ppc
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
sh4|sh4eb)
|
||||
TARGET_ARCH=sh4
|
||||
|
@ -3955,7 +4169,7 @@ case "$target_arch2" in
|
|||
echo "CONFIG_NO_XEN=y" >> $config_target_mak
|
||||
esac
|
||||
case "$target_arch2" in
|
||||
i386|x86_64|ppcemb|ppc|ppc64|s390x)
|
||||
arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
|
||||
# Make sure the target and host cpus are compatible
|
||||
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
|
||||
\( "$target_arch2" = "$cpu" -o \
|
||||
|
@ -3975,18 +4189,12 @@ case "$target_arch2" in
|
|||
i386|x86_64)
|
||||
echo "CONFIG_HAVE_GET_MEMORY_MAPPING=y" >> $config_target_mak
|
||||
esac
|
||||
if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then
|
||||
echo "CONFIG_PSERIES=y" >> $config_target_mak
|
||||
fi
|
||||
if test "$target_bigendian" = "yes" ; then
|
||||
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
|
||||
fi
|
||||
if test "$target_softmmu" = "yes" ; then
|
||||
echo "CONFIG_SOFTMMU=y" >> $config_target_mak
|
||||
echo "LIBS+=$libs_softmmu $target_libs_softmmu" >> $config_target_mak
|
||||
if test "$smartcard_nss" = "yes" ; then
|
||||
echo "subdir-$target: subdir-libcacard" >> $config_host_mak
|
||||
fi
|
||||
case "$target_arch2" in
|
||||
i386|x86_64)
|
||||
echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak
|
||||
|
@ -4155,6 +4363,12 @@ if test "$gprof" = "yes" ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
if test "$tpm" = "yes"; then
|
||||
if test "$target_softmmu" = "yes" ; then
|
||||
echo "CONFIG_TPM=y" >> $config_host_mak
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ARCH" = "tci"; then
|
||||
linker_script=""
|
||||
else
|
||||
|
@ -4187,15 +4401,16 @@ DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32"
|
|||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
DIRS="$DIRS qapi-generated"
|
||||
DIRS="$DIRS libcacard libcacard/libcacard libcacard/trace"
|
||||
FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
|
||||
FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
|
||||
FILES="$FILES tests/tcg/lm32/Makefile libcacard/Makefile"
|
||||
FILES="$FILES tests/tcg/lm32/Makefile po/Makefile"
|
||||
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
|
||||
FILES="$FILES pc-bios/spapr-rtas/Makefile"
|
||||
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
||||
FILES="$FILES pc-bios/qemu-icon.bmp"
|
||||
for bios_file in \
|
||||
$source_path/pc-bios/*.bin \
|
||||
$source_path/pc-bios/*.aml \
|
||||
$source_path/pc-bios/*.rom \
|
||||
$source_path/pc-bios/*.dtb \
|
||||
$source_path/pc-bios/openbios-* \
|
||||
|
@ -4215,9 +4430,10 @@ for rom in seabios vgabios ; do
|
|||
config_mak=roms/$rom/config.mak
|
||||
echo "# Automatically generated by configure - do not modify" > $config_mak
|
||||
echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak
|
||||
echo "AS=$as" >> $config_mak
|
||||
echo "CC=$cc" >> $config_mak
|
||||
echo "BCC=bcc" >> $config_mak
|
||||
echo "CPP=${cross_prefix}cpp" >> $config_mak
|
||||
echo "CPP=$cpp" >> $config_mak
|
||||
echo "OBJCOPY=objcopy" >> $config_mak
|
||||
echo "IASL=iasl" >> $config_mak
|
||||
echo "LD=$ld" >> $config_mak
|
||||
|
|
|
@ -33,19 +33,10 @@
|
|||
#include "qemu-common.h"
|
||||
#include "block/coroutine_int.h"
|
||||
|
||||
enum {
|
||||
/* Maximum free pool size prevents holding too many freed coroutines */
|
||||
POOL_MAX_SIZE = 64,
|
||||
};
|
||||
|
||||
/** Free list to speed up creation */
|
||||
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
|
||||
static unsigned int pool_size;
|
||||
|
||||
typedef struct {
|
||||
Coroutine base;
|
||||
void *stack;
|
||||
jmp_buf env;
|
||||
sigjmp_buf env;
|
||||
} CoroutineUContext;
|
||||
|
||||
/**
|
||||
|
@ -59,7 +50,7 @@ typedef struct {
|
|||
CoroutineUContext leader;
|
||||
|
||||
/** Information for the signal handler (trampoline) */
|
||||
jmp_buf tr_reenter;
|
||||
sigjmp_buf tr_reenter;
|
||||
volatile sig_atomic_t tr_called;
|
||||
void *tr_handler;
|
||||
} CoroutineThreadState;
|
||||
|
@ -85,17 +76,6 @@ static void qemu_coroutine_thread_cleanup(void *opaque)
|
|||
g_free(s);
|
||||
}
|
||||
|
||||
static void __attribute__((destructor)) coroutine_cleanup(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
Coroutine *tmp;
|
||||
|
||||
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
|
||||
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
|
||||
g_free(co);
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) coroutine_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -115,8 +95,8 @@ static void __attribute__((constructor)) coroutine_init(void)
|
|||
static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co)
|
||||
{
|
||||
/* Initialize longjmp environment and switch back the caller */
|
||||
if (!setjmp(self->env)) {
|
||||
longjmp(*(jmp_buf *)co->entry_arg, 1);
|
||||
if (!sigsetjmp(self->env, 0)) {
|
||||
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
@ -145,14 +125,14 @@ static void coroutine_trampoline(int signal)
|
|||
/*
|
||||
* Here we have to do a bit of a ping pong between the caller, given that
|
||||
* this is a signal handler and we have to do a return "soon". Then the
|
||||
* caller can reestablish everything and do a longjmp here again.
|
||||
* caller can reestablish everything and do a siglongjmp here again.
|
||||
*/
|
||||
if (!setjmp(coTS->tr_reenter)) {
|
||||
if (!sigsetjmp(coTS->tr_reenter, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, the caller has longjmp'ed back to us, so now prepare
|
||||
* Ok, the caller has siglongjmp'ed back to us, so now prepare
|
||||
* us for the real machine state switching. We have to jump
|
||||
* into another function here to get a new stack context for
|
||||
* the auto variables (which have to be auto-variables
|
||||
|
@ -164,7 +144,7 @@ static void coroutine_trampoline(int signal)
|
|||
coroutine_bootstrap(self, co);
|
||||
}
|
||||
|
||||
static Coroutine *coroutine_new(void)
|
||||
Coroutine *qemu_coroutine_new(void)
|
||||
{
|
||||
const size_t stack_size = 1 << 20;
|
||||
CoroutineUContext *co;
|
||||
|
@ -179,7 +159,7 @@ static Coroutine *coroutine_new(void)
|
|||
|
||||
/* The way to manipulate stack is with the sigaltstack function. We
|
||||
* prepare a stack, with it delivering a signal to ourselves and then
|
||||
* put setjmp/longjmp where needed.
|
||||
* put sigsetjmp/siglongjmp where needed.
|
||||
* This has been done keeping coroutine-ucontext as a model and with the
|
||||
* pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics
|
||||
* of the coroutines and see pth_mctx.c (from the pth project) for the
|
||||
|
@ -220,7 +200,7 @@ static Coroutine *coroutine_new(void)
|
|||
|
||||
/*
|
||||
* Now transfer control onto the signal stack and set it up.
|
||||
* It will return immediately via "return" after the setjmp()
|
||||
* It will return immediately via "return" after the sigsetjmp()
|
||||
* was performed. Be careful here with race conditions. The
|
||||
* signal can be delivered the first time sigsuspend() is
|
||||
* called.
|
||||
|
@ -261,8 +241,8 @@ static Coroutine *coroutine_new(void)
|
|||
* type-conversion warnings related to the `volatile' qualifier and
|
||||
* the fact that `jmp_buf' usually is an array type.
|
||||
*/
|
||||
if (!setjmp(old_env)) {
|
||||
longjmp(coTS->tr_reenter, 1);
|
||||
if (!sigsetjmp(old_env, 0)) {
|
||||
siglongjmp(coTS->tr_reenter, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -272,31 +252,10 @@ static Coroutine *coroutine_new(void)
|
|||
return &co->base;
|
||||
}
|
||||
|
||||
Coroutine *qemu_coroutine_new(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
|
||||
co = QSLIST_FIRST(&pool);
|
||||
if (co) {
|
||||
QSLIST_REMOVE_HEAD(&pool, pool_next);
|
||||
pool_size--;
|
||||
} else {
|
||||
co = coroutine_new();
|
||||
}
|
||||
return co;
|
||||
}
|
||||
|
||||
void qemu_coroutine_delete(Coroutine *co_)
|
||||
{
|
||||
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
|
||||
|
||||
if (pool_size < POOL_MAX_SIZE) {
|
||||
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
|
||||
co->base.caller = NULL;
|
||||
pool_size++;
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(co->stack);
|
||||
g_free(co);
|
||||
}
|
||||
|
@ -311,9 +270,9 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
|
|||
|
||||
s->current = to_;
|
||||
|
||||
ret = setjmp(from->env);
|
||||
ret = sigsetjmp(from->env, 0);
|
||||
if (ret == 0) {
|
||||
longjmp(to->env, action);
|
||||
siglongjmp(to->env, action);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -34,19 +34,10 @@
|
|||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
enum {
|
||||
/* Maximum free pool size prevents holding too many freed coroutines */
|
||||
POOL_MAX_SIZE = 64,
|
||||
};
|
||||
|
||||
/** Free list to speed up creation */
|
||||
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
|
||||
static unsigned int pool_size;
|
||||
|
||||
typedef struct {
|
||||
Coroutine base;
|
||||
void *stack;
|
||||
jmp_buf env;
|
||||
sigjmp_buf env;
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
unsigned int valgrind_stack_id;
|
||||
|
@ -96,17 +87,6 @@ static void qemu_coroutine_thread_cleanup(void *opaque)
|
|||
g_free(s);
|
||||
}
|
||||
|
||||
static void __attribute__((destructor)) coroutine_cleanup(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
Coroutine *tmp;
|
||||
|
||||
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
|
||||
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
|
||||
g_free(co);
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) coroutine_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -130,8 +110,8 @@ static void coroutine_trampoline(int i0, int i1)
|
|||
co = &self->base;
|
||||
|
||||
/* Initialize longjmp environment and switch back the caller */
|
||||
if (!setjmp(self->env)) {
|
||||
longjmp(*(jmp_buf *)co->entry_arg, 1);
|
||||
if (!sigsetjmp(self->env, 0)) {
|
||||
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
@ -140,19 +120,20 @@ static void coroutine_trampoline(int i0, int i1)
|
|||
}
|
||||
}
|
||||
|
||||
static Coroutine *coroutine_new(void)
|
||||
Coroutine *qemu_coroutine_new(void)
|
||||
{
|
||||
const size_t stack_size = 1 << 20;
|
||||
CoroutineUContext *co;
|
||||
ucontext_t old_uc, uc;
|
||||
jmp_buf old_env;
|
||||
sigjmp_buf old_env;
|
||||
union cc_arg arg = {0};
|
||||
|
||||
/* The ucontext functions preserve signal masks which incurs a system call
|
||||
* overhead. setjmp()/longjmp() does not preserve signal masks but only
|
||||
* works on the current stack. Since we need a way to create and switch to
|
||||
* a new stack, use the ucontext functions for that but setjmp()/longjmp()
|
||||
* for everything else.
|
||||
/* The ucontext functions preserve signal masks which incurs a
|
||||
* system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not
|
||||
* preserve signal masks but only works on the current stack.
|
||||
* Since we need a way to create and switch to a new stack, use
|
||||
* the ucontext functions for that but sigsetjmp()/siglongjmp() for
|
||||
* everything else.
|
||||
*/
|
||||
|
||||
if (getcontext(&uc) == -1) {
|
||||
|
@ -178,29 +159,15 @@ static Coroutine *coroutine_new(void)
|
|||
makecontext(&uc, (void (*)(void))coroutine_trampoline,
|
||||
2, arg.i[0], arg.i[1]);
|
||||
|
||||
/* swapcontext() in, longjmp() back out */
|
||||
if (!setjmp(old_env)) {
|
||||
/* swapcontext() in, siglongjmp() back out */
|
||||
if (!sigsetjmp(old_env, 0)) {
|
||||
swapcontext(&old_uc, &uc);
|
||||
}
|
||||
return &co->base;
|
||||
}
|
||||
|
||||
Coroutine *qemu_coroutine_new(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
|
||||
co = QSLIST_FIRST(&pool);
|
||||
if (co) {
|
||||
QSLIST_REMOVE_HEAD(&pool, pool_next);
|
||||
pool_size--;
|
||||
} else {
|
||||
co = coroutine_new();
|
||||
}
|
||||
return co;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET
|
||||
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
|
||||
/* Work around an unused variable in the valgrind.h macro... */
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#endif
|
||||
|
@ -208,7 +175,7 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co)
|
|||
{
|
||||
VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
|
||||
}
|
||||
#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET
|
||||
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
|
||||
#pragma GCC diagnostic error "-Wunused-but-set-variable"
|
||||
#endif
|
||||
#endif
|
||||
|
@ -217,13 +184,6 @@ void qemu_coroutine_delete(Coroutine *co_)
|
|||
{
|
||||
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
|
||||
|
||||
if (pool_size < POOL_MAX_SIZE) {
|
||||
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
|
||||
co->base.caller = NULL;
|
||||
pool_size++;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
valgrind_stack_deregister(co);
|
||||
#endif
|
||||
|
@ -242,9 +202,9 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
|
|||
|
||||
s->current = to_;
|
||||
|
||||
ret = setjmp(from->env);
|
||||
ret = sigsetjmp(from->env, 0);
|
||||
if (ret == 0) {
|
||||
longjmp(to->env, action);
|
||||
siglongjmp(to->env, action);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
174
cpu-exec.c
174
cpu-exec.c
|
@ -23,8 +23,6 @@
|
|||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
int tb_invalidated_flag;
|
||||
|
||||
//#define CONFIG_DEBUG_EXEC
|
||||
|
||||
bool qemu_cpu_has_work(CPUState *cpu)
|
||||
|
@ -34,8 +32,10 @@ bool qemu_cpu_has_work(CPUState *cpu)
|
|||
|
||||
void cpu_loop_exit(CPUArchState *env)
|
||||
{
|
||||
env->current_tb = NULL;
|
||||
longjmp(env->jmp_env, 1);
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
cpu->current_tb = NULL;
|
||||
siglongjmp(env->jmp_env, 1);
|
||||
}
|
||||
|
||||
/* exit the current TB from a signal handler. The host registers are
|
||||
|
@ -47,16 +47,38 @@ void cpu_resume_from_signal(CPUArchState *env, void *puc)
|
|||
/* XXX: restore cpu registers saved in host registers */
|
||||
|
||||
env->exception_index = -1;
|
||||
longjmp(env->jmp_env, 1);
|
||||
siglongjmp(env->jmp_env, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Execute a TB, and fix up the CPU state afterwards if necessary */
|
||||
static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
tcg_target_ulong next_tb = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
if ((next_tb & TB_EXIT_MASK) > TB_EXIT_IDX1) {
|
||||
/* We didn't start executing this TB (eg because the instruction
|
||||
* counter hit zero); we must restore the guest PC to the address
|
||||
* of the start of the TB.
|
||||
*/
|
||||
TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
|
||||
cpu_pc_from_tb(env, tb);
|
||||
}
|
||||
if ((next_tb & TB_EXIT_MASK) == TB_EXIT_REQUESTED) {
|
||||
/* We were asked to stop executing TBs (probably a pending
|
||||
* interrupt. We've now stopped, so clear the flag.
|
||||
*/
|
||||
cpu->tcg_exit_req = 0;
|
||||
}
|
||||
return next_tb;
|
||||
}
|
||||
|
||||
/* Execute the code without caching the generated code. An interpreter
|
||||
could be used if available. */
|
||||
static void cpu_exec_nocache(CPUArchState *env, int max_cycles,
|
||||
TranslationBlock *orig_tb)
|
||||
{
|
||||
tcg_target_ulong next_tb;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
TranslationBlock *tb;
|
||||
|
||||
/* Should never happen.
|
||||
|
@ -66,16 +88,10 @@ static void cpu_exec_nocache(CPUArchState *env, int max_cycles,
|
|||
|
||||
tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
|
||||
max_cycles);
|
||||
env->current_tb = tb;
|
||||
cpu->current_tb = tb;
|
||||
/* execute the generated code */
|
||||
next_tb = tcg_qemu_tb_exec(env, tb->tc_ptr);
|
||||
env->current_tb = NULL;
|
||||
|
||||
if ((next_tb & 3) == 2) {
|
||||
/* Restore PC. This may happen if async event occurs before
|
||||
the TB starts executing. */
|
||||
cpu_pc_from_tb(env, tb);
|
||||
}
|
||||
cpu_tb_exec(cpu, tb->tc_ptr);
|
||||
cpu->current_tb = NULL;
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tb_free(tb);
|
||||
}
|
||||
|
@ -90,13 +106,13 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
|
|||
tb_page_addr_t phys_pc, phys_page1;
|
||||
target_ulong virt_page2;
|
||||
|
||||
tb_invalidated_flag = 0;
|
||||
tcg_ctx.tb_ctx.tb_invalidated_flag = 0;
|
||||
|
||||
/* find translated block using physical mappings */
|
||||
phys_pc = get_page_addr_code(env, pc);
|
||||
phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
||||
h = tb_phys_hash_func(phys_pc);
|
||||
ptb1 = &tb_phys_hash[h];
|
||||
ptb1 = &tcg_ctx.tb_ctx.tb_phys_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb1;
|
||||
if (!tb)
|
||||
|
@ -128,8 +144,8 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
|
|||
/* Move the last found TB to the head of the list */
|
||||
if (likely(*ptb1)) {
|
||||
*ptb1 = tb->phys_hash_next;
|
||||
tb->phys_hash_next = tb_phys_hash[h];
|
||||
tb_phys_hash[h] = tb;
|
||||
tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h];
|
||||
tcg_ctx.tb_ctx.tb_phys_hash[h] = tb;
|
||||
}
|
||||
/* we add the TB in the virtual pc hash table */
|
||||
env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
|
||||
|
@ -182,23 +198,27 @@ volatile sig_atomic_t exit_request;
|
|||
int cpu_exec(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
#if !(defined(CONFIG_USER_ONLY) && \
|
||||
(defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X)))
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
#endif
|
||||
int ret, interrupt_request;
|
||||
TranslationBlock *tb;
|
||||
uint8_t *tc_ptr;
|
||||
tcg_target_ulong next_tb;
|
||||
|
||||
if (env->halted) {
|
||||
if (cpu->halted) {
|
||||
if (!cpu_has_work(cpu)) {
|
||||
return EXCP_HALTED;
|
||||
}
|
||||
|
||||
env->halted = 0;
|
||||
cpu->halted = 0;
|
||||
}
|
||||
|
||||
cpu_single_env = env;
|
||||
|
||||
if (unlikely(exit_request)) {
|
||||
env->exit_request = 1;
|
||||
cpu->exit_request = 1;
|
||||
}
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
@ -233,7 +253,7 @@ int cpu_exec(CPUArchState *env)
|
|||
|
||||
/* prepare setjmp context for exception handling */
|
||||
for(;;) {
|
||||
if (setjmp(env->jmp_env) == 0) {
|
||||
if (sigsetjmp(env->jmp_env, 0) == 0) {
|
||||
/* if an exception is pending, we execute it here */
|
||||
if (env->exception_index >= 0) {
|
||||
if (env->exception_index >= EXCP_INTERRUPT) {
|
||||
|
@ -249,12 +269,12 @@ int cpu_exec(CPUArchState *env)
|
|||
which will be handled outside the cpu execution
|
||||
loop */
|
||||
#if defined(TARGET_I386)
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
#endif
|
||||
ret = env->exception_index;
|
||||
break;
|
||||
#else
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
env->exception_index = -1;
|
||||
#endif
|
||||
}
|
||||
|
@ -262,14 +282,14 @@ int cpu_exec(CPUArchState *env)
|
|||
|
||||
next_tb = 0; /* force lookup of first TB */
|
||||
for(;;) {
|
||||
interrupt_request = env->interrupt_request;
|
||||
interrupt_request = cpu->interrupt_request;
|
||||
if (unlikely(interrupt_request)) {
|
||||
if (unlikely(env->singlestep_enabled & SSTEP_NOIRQ)) {
|
||||
/* Mask out external interrupts for this step. */
|
||||
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
||||
env->exception_index = EXCP_DEBUG;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
|
@ -277,8 +297,8 @@ int cpu_exec(CPUArchState *env)
|
|||
defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \
|
||||
defined(TARGET_MICROBLAZE) || defined(TARGET_LM32) || defined(TARGET_UNICORE32)
|
||||
if (interrupt_request & CPU_INTERRUPT_HALT) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
||||
env->halted = 1;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
||||
cpu->halted = 1;
|
||||
env->exception_index = EXCP_HLT;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
|
@ -286,7 +306,7 @@ int cpu_exec(CPUArchState *env)
|
|||
#if defined(TARGET_I386)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (interrupt_request & CPU_INTERRUPT_POLL) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_POLL;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
|
||||
apic_poll_irq(env->apic_state);
|
||||
}
|
||||
#endif
|
||||
|
@ -303,17 +323,17 @@ int cpu_exec(CPUArchState *env)
|
|||
!(env->hflags & HF_SMM_MASK)) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_SMI,
|
||||
0);
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_SMI;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
|
||||
do_smm_enter(env);
|
||||
next_tb = 0;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
|
||||
!(env->hflags2 & HF2_NMI_MASK)) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_NMI;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_NMI;
|
||||
env->hflags2 |= HF2_NMI_MASK;
|
||||
do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
|
||||
next_tb = 0;
|
||||
} else if (interrupt_request & CPU_INTERRUPT_MCE) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_MCE;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_MCE;
|
||||
do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
|
||||
next_tb = 0;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
|
@ -325,7 +345,8 @@ int cpu_exec(CPUArchState *env)
|
|||
int intno;
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INTR,
|
||||
0);
|
||||
env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ);
|
||||
cpu->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);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
|
@ -343,7 +364,7 @@ int cpu_exec(CPUArchState *env)
|
|||
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);
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||
next_tb = 0;
|
||||
#endif
|
||||
}
|
||||
|
@ -354,15 +375,16 @@ int cpu_exec(CPUArchState *env)
|
|||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
ppc_hw_interrupt(env);
|
||||
if (env->pending_interrupts == 0)
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||
if (env->pending_interrupts == 0) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||
}
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_LM32)
|
||||
if ((interrupt_request & CPU_INTERRUPT_HARD)
|
||||
&& (env->ie & IE_IE)) {
|
||||
env->exception_index = EXCP_IRQ;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
|
@ -371,7 +393,7 @@ int cpu_exec(CPUArchState *env)
|
|||
&& !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))
|
||||
&& !(env->iflags & (D_FLAG | IMM_FLAG))) {
|
||||
env->exception_index = EXCP_IRQ;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_MIPS)
|
||||
|
@ -380,7 +402,7 @@ int cpu_exec(CPUArchState *env)
|
|||
/* Raise it */
|
||||
env->exception_index = EXCP_EXT_INTERRUPT;
|
||||
env->error_code = 0;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_OPENRISC)
|
||||
|
@ -396,7 +418,7 @@ int cpu_exec(CPUArchState *env)
|
|||
}
|
||||
if (idx >= 0) {
|
||||
env->exception_index = idx;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +433,7 @@ int cpu_exec(CPUArchState *env)
|
|||
cpu_pil_allowed(env, pil)) ||
|
||||
type != TT_EXTINT) {
|
||||
env->exception_index = env->interrupt_index;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
|
@ -420,7 +442,7 @@ int cpu_exec(CPUArchState *env)
|
|||
if (interrupt_request & CPU_INTERRUPT_FIQ
|
||||
&& !(env->uncached_cpsr & CPSR_F)) {
|
||||
env->exception_index = EXCP_FIQ;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
/* ARMv7-M interrupt return works by loading a magic value
|
||||
|
@ -436,19 +458,19 @@ int cpu_exec(CPUArchState *env)
|
|||
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
||||
|| !(env->uncached_cpsr & CPSR_I))) {
|
||||
env->exception_index = EXCP_IRQ;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& !(env->uncached_asr & ASR_I)) {
|
||||
env->exception_index = UC32_EXCP_INTR;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_SH4)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_ALPHA)
|
||||
|
@ -479,7 +501,7 @@ int cpu_exec(CPUArchState *env)
|
|||
if (idx >= 0) {
|
||||
env->exception_index = idx;
|
||||
env->error_code = 0;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +510,7 @@ int cpu_exec(CPUArchState *env)
|
|||
&& (env->pregs[PR_CCS] & I_FLAG)
|
||||
&& !env->locked_irq) {
|
||||
env->exception_index = EXCP_IRQ;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
if (interrupt_request & CPU_INTERRUPT_NMI) {
|
||||
|
@ -500,7 +522,7 @@ int cpu_exec(CPUArchState *env)
|
|||
}
|
||||
if ((env->pregs[PR_CCS] & m_flag_archval)) {
|
||||
env->exception_index = EXCP_NMI;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
|
@ -520,27 +542,27 @@ int cpu_exec(CPUArchState *env)
|
|||
#elif defined(TARGET_S390X) && !defined(CONFIG_USER_ONLY)
|
||||
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
(env->psw.mask & PSW_MASK_EXT)) {
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#elif defined(TARGET_XTENSA)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
env->exception_index = EXC_IRQ;
|
||||
do_interrupt(env);
|
||||
cc->do_interrupt(cpu);
|
||||
next_tb = 0;
|
||||
}
|
||||
#endif
|
||||
/* Don't use the cached interrupt_request value,
|
||||
do_interrupt may have updated the EXITTB flag. */
|
||||
if (env->interrupt_request & CPU_INTERRUPT_EXITTB) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
|
||||
if (cpu->interrupt_request & CPU_INTERRUPT_EXITTB) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
|
||||
/* ensure that no TB jump will be modified as
|
||||
the program flow was changed */
|
||||
next_tb = 0;
|
||||
}
|
||||
}
|
||||
if (unlikely(env->exit_request)) {
|
||||
env->exit_request = 0;
|
||||
if (unlikely(cpu->exit_request)) {
|
||||
cpu->exit_request = 0;
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
|
@ -563,16 +585,16 @@ int cpu_exec(CPUArchState *env)
|
|||
#endif
|
||||
}
|
||||
#endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */
|
||||
spin_lock(&tb_lock);
|
||||
spin_lock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
tb = tb_find_fast(env);
|
||||
/* Note: we do it here to avoid a gcc bug on Mac OS X when
|
||||
doing it in tb_find_slow */
|
||||
if (tb_invalidated_flag) {
|
||||
if (tcg_ctx.tb_ctx.tb_invalidated_flag) {
|
||||
/* as some TB could have been invalidated because
|
||||
of memory exceptions while generating the code, we
|
||||
must recompute the hash index here */
|
||||
next_tb = 0;
|
||||
tb_invalidated_flag = 0;
|
||||
tcg_ctx.tb_ctx.tb_invalidated_flag = 0;
|
||||
}
|
||||
#ifdef CONFIG_DEBUG_EXEC
|
||||
qemu_log_mask(CPU_LOG_EXEC, "Trace %p [" TARGET_FMT_lx "] %s\n",
|
||||
|
@ -583,26 +605,38 @@ int cpu_exec(CPUArchState *env)
|
|||
spans two pages, we cannot safely do a direct
|
||||
jump. */
|
||||
if (next_tb != 0 && tb->page_addr[1] == -1) {
|
||||
tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb);
|
||||
tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK),
|
||||
next_tb & TB_EXIT_MASK, tb);
|
||||
}
|
||||
spin_unlock(&tb_lock);
|
||||
spin_unlock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
|
||||
/* cpu_interrupt might be called while translating the
|
||||
TB, but before it is linked into a potentially
|
||||
infinite loop and becomes env->current_tb. Avoid
|
||||
starting execution if there is a pending interrupt. */
|
||||
env->current_tb = tb;
|
||||
cpu->current_tb = tb;
|
||||
barrier();
|
||||
if (likely(!env->exit_request)) {
|
||||
if (likely(!cpu->exit_request)) {
|
||||
tc_ptr = tb->tc_ptr;
|
||||
/* execute the generated code */
|
||||
next_tb = tcg_qemu_tb_exec(env, tc_ptr);
|
||||
if ((next_tb & 3) == 2) {
|
||||
next_tb = cpu_tb_exec(cpu, tc_ptr);
|
||||
switch (next_tb & TB_EXIT_MASK) {
|
||||
case TB_EXIT_REQUESTED:
|
||||
/* Something asked us to stop executing
|
||||
* chained TBs; just continue round the main
|
||||
* loop. Whatever requested the exit will also
|
||||
* have set something else (eg exit_request or
|
||||
* interrupt_request) which we will handle
|
||||
* next time around the loop.
|
||||
*/
|
||||
tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
|
||||
next_tb = 0;
|
||||
break;
|
||||
case TB_EXIT_ICOUNT_EXPIRED:
|
||||
{
|
||||
/* Instruction counter expired. */
|
||||
int insns_left;
|
||||
tb = (TranslationBlock *)(next_tb & ~3);
|
||||
/* Restore PC. */
|
||||
cpu_pc_from_tb(env, tb);
|
||||
tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
|
||||
insns_left = env->icount_decr.u32;
|
||||
if (env->icount_extra && insns_left >= 0) {
|
||||
/* Refill decrementer and continue execution. */
|
||||
|
@ -623,9 +657,13 @@ int cpu_exec(CPUArchState *env)
|
|||
next_tb = 0;
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
env->current_tb = NULL;
|
||||
cpu->current_tb = NULL;
|
||||
/* reset soft MMU for next block (it can currently
|
||||
only be set by a memory fault) */
|
||||
} /* for(;;) */
|
||||
|
|
66
cpus.c
66
cpus.c
|
@ -72,7 +72,7 @@ static bool cpu_thread_is_idle(CPUArchState *env)
|
|||
if (cpu->stopped || !runstate_is_running()) {
|
||||
return true;
|
||||
}
|
||||
if (!env->halted || qemu_cpu_has_work(cpu) ||
|
||||
if (!cpu->halted || qemu_cpu_has_work(cpu) ||
|
||||
kvm_async_interrupts_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -390,13 +390,15 @@ void hw_error(const char *fmt, ...)
|
|||
{
|
||||
va_list ap;
|
||||
CPUArchState *env;
|
||||
CPUState *cpu;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "qemu: hardware error: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
fprintf(stderr, "CPU #%d:\n", env->cpu_index);
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
cpu = ENV_GET_CPU(env);
|
||||
fprintf(stderr, "CPU #%d:\n", cpu->cpu_index);
|
||||
cpu_dump_state(env, stderr, fprintf, CPU_DUMP_FPU);
|
||||
}
|
||||
va_end(ap);
|
||||
|
@ -515,7 +517,7 @@ static void qemu_init_sigbus(void)
|
|||
prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0);
|
||||
}
|
||||
|
||||
static void qemu_kvm_eat_signals(CPUArchState *env)
|
||||
static void qemu_kvm_eat_signals(CPUState *cpu)
|
||||
{
|
||||
struct timespec ts = { 0, 0 };
|
||||
siginfo_t siginfo;
|
||||
|
@ -536,7 +538,7 @@ static void qemu_kvm_eat_signals(CPUArchState *env)
|
|||
|
||||
switch (r) {
|
||||
case SIGBUS:
|
||||
if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) {
|
||||
if (kvm_on_sigbus_vcpu(cpu, siginfo.si_code, siginfo.si_addr)) {
|
||||
sigbus_reraise();
|
||||
}
|
||||
break;
|
||||
|
@ -558,7 +560,7 @@ static void qemu_init_sigbus(void)
|
|||
{
|
||||
}
|
||||
|
||||
static void qemu_kvm_eat_signals(CPUArchState *env)
|
||||
static void qemu_kvm_eat_signals(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
#endif /* !CONFIG_LINUX */
|
||||
|
@ -725,7 +727,7 @@ static void qemu_kvm_wait_io_event(CPUArchState *env)
|
|||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
qemu_kvm_eat_signals(env);
|
||||
qemu_kvm_eat_signals(cpu);
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
|
||||
|
@ -740,7 +742,7 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
|
|||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu_single_env = env;
|
||||
|
||||
r = kvm_init_vcpu(env);
|
||||
r = kvm_init_vcpu(cpu);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r));
|
||||
exit(1);
|
||||
|
@ -1041,8 +1043,8 @@ 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->nr_cores = smp_cores;
|
||||
cpu->nr_threads = smp_threads;
|
||||
cpu->stopped = true;
|
||||
if (kvm_enabled()) {
|
||||
qemu_kvm_start_vcpu(env);
|
||||
|
@ -1160,38 +1162,19 @@ static void tcg_exec_all(void)
|
|||
void set_numa_modes(void)
|
||||
{
|
||||
CPUArchState *env;
|
||||
CPUState *cpu;
|
||||
int i;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
cpu = ENV_GET_CPU(env);
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
if (test_bit(env->cpu_index, node_cpumask[i])) {
|
||||
env->numa_node = i;
|
||||
if (test_bit(cpu->cpu_index, node_cpumask[i])) {
|
||||
cpu->numa_node = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_cpu_log(const char *optarg)
|
||||
{
|
||||
int mask;
|
||||
const CPULogItem *item;
|
||||
|
||||
mask = cpu_str_to_log_mask(optarg);
|
||||
if (!mask) {
|
||||
printf("Log items (comma separated):\n");
|
||||
for (item = cpu_log_items; item->mask != 0; item++) {
|
||||
printf("%-10s %s\n", item->name, item->help);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
cpu_set_log(mask);
|
||||
}
|
||||
|
||||
void set_cpu_log_filename(const char *optarg)
|
||||
{
|
||||
cpu_set_log_filename(optarg);
|
||||
}
|
||||
|
||||
void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
|
||||
{
|
||||
/* XXX: implement xxx_cpu_list for targets that still miss it */
|
||||
|
@ -1213,9 +1196,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
|||
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info->value = g_malloc0(sizeof(*info->value));
|
||||
info->value->CPU = env->cpu_index;
|
||||
info->value->CPU = cpu->cpu_index;
|
||||
info->value->current = (env == first_cpu);
|
||||
info->value->halted = env->halted;
|
||||
info->value->halted = cpu->halted;
|
||||
info->value->thread_id = cpu->thread_id;
|
||||
#if defined(TARGET_I386)
|
||||
info->value->has_pc = true;
|
||||
|
@ -1251,23 +1234,20 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename,
|
|||
FILE *f;
|
||||
uint32_t l;
|
||||
CPUArchState *env;
|
||||
CPUState *cpu;
|
||||
uint8_t buf[1024];
|
||||
|
||||
if (!has_cpu) {
|
||||
cpu_index = 0;
|
||||
}
|
||||
|
||||
for (env = first_cpu; env; env = env->next_cpu) {
|
||||
if (cpu_index == env->cpu_index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (env == NULL) {
|
||||
cpu = qemu_get_cpu(cpu_index);
|
||||
if (cpu == NULL) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
|
||||
"a CPU number");
|
||||
return;
|
||||
}
|
||||
env = cpu->env_ptr;
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
if (!f) {
|
||||
|
@ -1329,7 +1309,7 @@ void qmp_inject_nmi(Error **errp)
|
|||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (!env->apic_state) {
|
||||
cpu_interrupt(env, CPU_INTERRUPT_NMI);
|
||||
cpu_interrupt(CPU(x86_env_get_cpu(env)), CPU_INTERRUPT_NMI);
|
||||
} else {
|
||||
apic_deliver_nmi(env->apic_state);
|
||||
}
|
||||
|
|
6
cputlb.c
6
cputlb.c
|
@ -54,6 +54,7 @@ static const CPUTLBEntry s_cputlb_empty_entry = {
|
|||
*/
|
||||
void tlb_flush(CPUArchState *env, int flush_global)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int i;
|
||||
|
||||
#if defined(DEBUG_TLB)
|
||||
|
@ -61,7 +62,7 @@ void tlb_flush(CPUArchState *env, int flush_global)
|
|||
#endif
|
||||
/* must reset current TB so that interrupts cannot modify the
|
||||
links while we are modifying them */
|
||||
env->current_tb = NULL;
|
||||
cpu->current_tb = NULL;
|
||||
|
||||
for (i = 0; i < CPU_TLB_SIZE; i++) {
|
||||
int mmu_idx;
|
||||
|
@ -92,6 +93,7 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
|
|||
|
||||
void tlb_flush_page(CPUArchState *env, target_ulong addr)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
|
@ -110,7 +112,7 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr)
|
|||
}
|
||||
/* must reset current TB so that interrupts cannot modify the
|
||||
links while we are modifying them */
|
||||
env->current_tb = NULL;
|
||||
cpu->current_tb = NULL;
|
||||
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for alpha-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for arm-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_VGA=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
|
@ -46,3 +47,5 @@ CONFIG_XGMAC=y
|
|||
|
||||
CONFIG_VERSATILE_PCI=y
|
||||
CONFIG_VERSATILE_I2C=y
|
||||
|
||||
CONFIG_SDHCI=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for i386-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_VGA_ISA=y
|
||||
|
@ -25,3 +26,5 @@ CONFIG_HPET=y
|
|||
CONFIG_APPLESMC=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_TPM_TIS=y
|
||||
CONFIG_TPM_PASSTHROUGH=y
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Default configuration for m68k-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_PTIMER=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for mips-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for mips64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for mips64el-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for mipsel-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
|
|
@ -21,3 +21,4 @@ CONFIG_ESP=y
|
|||
CONFIG_ESP_PCI=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_PCI=y
|
||||
CONFIG_IPACK=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for ppc-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESCC=y
|
||||
|
@ -8,6 +9,7 @@ CONFIG_M48T59=y
|
|||
CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_FDC=y
|
||||
|
@ -16,6 +18,7 @@ CONFIG_I82374=y
|
|||
CONFIG_OPENPIC=y
|
||||
CONFIG_PREP_PCI=y
|
||||
CONFIG_I82378=y
|
||||
CONFIG_PC87312=y
|
||||
CONFIG_MACIO=y
|
||||
CONFIG_PCSPK=y
|
||||
CONFIG_CUDA=y
|
||||
|
@ -37,3 +40,4 @@ CONFIG_PFLASH_CFI02=y
|
|||
CONFIG_PTIMER=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_E500=$(CONFIG_FDT)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for ppc64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESCC=y
|
||||
|
@ -8,13 +9,18 @@ CONFIG_M48T59=y
|
|||
CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_FDC=y
|
||||
CONFIG_DMA=y
|
||||
CONFIG_I82374=y
|
||||
CONFIG_OPENPIC=y
|
||||
CONFIG_PREP_PCI=y
|
||||
CONFIG_I82378=y
|
||||
CONFIG_PC87312=y
|
||||
CONFIG_MACIO=y
|
||||
CONFIG_PCSPK=y
|
||||
CONFIG_CUDA=y
|
||||
CONFIG_ADB=y
|
||||
CONFIG_MAC_NVRAM=y
|
||||
|
@ -34,3 +40,5 @@ CONFIG_PFLASH_CFI02=y
|
|||
CONFIG_PTIMER=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_PSERIES=$(CONFIG_FDT)
|
||||
CONFIG_E500=$(CONFIG_FDT)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for ppcemb-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESCC=y
|
||||
|
@ -34,3 +35,4 @@ CONFIG_PFLASH_CFI02=y
|
|||
CONFIG_PTIMER=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_E500=$(CONFIG_FDT)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for sh4-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for sh4eb-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for sparc64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_M48T59=y
|
||||
CONFIG_PTIMER=y
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
CONFIG_USB_TABLET_WACOM=y
|
||||
CONFIG_USB_STORAGE_BOT=y
|
||||
CONFIG_USB_STORAGE_UAS=y
|
||||
CONFIG_USB_SMARTCARD=y
|
||||
CONFIG_USB_AUDIO=y
|
||||
CONFIG_USB_SERIAL=y
|
||||
CONFIG_USB_NETWORK=y
|
||||
CONFIG_USB_BLUETOOTH=y
|
|
@ -1,6 +1,7 @@
|
|||
# Default configuration for x86_64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_VGA_ISA=y
|
||||
|
@ -25,3 +26,5 @@ CONFIG_HPET=y
|
|||
CONFIG_APPLESMC=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_TPM_TIS=y
|
||||
CONFIG_TPM_PASSTHROUGH=y
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hw.h"
|
||||
#include "boards.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
@ -47,15 +47,6 @@ DriveInfo *add_init_drive(const char *optstr)
|
|||
return dinfo;
|
||||
}
|
||||
|
||||
#if !defined(TARGET_I386)
|
||||
int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo)
|
||||
{
|
||||
/* On non-x86 we don't do PCI hotplug */
|
||||
monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void drive_hot_add(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
DriveInfo *dinfo = NULL;
|
|
@ -1,16 +1,18 @@
|
|||
universal-obj-$(CONFIG_ALPHA_DIS) += alpha.o
|
||||
universal-obj-$(CONFIG_ARM_DIS) += arm.o
|
||||
universal-obj-$(CONFIG_CRIS_DIS) += cris.o
|
||||
universal-obj-$(CONFIG_HPPA_DIS) += hppa.o
|
||||
universal-obj-$(CONFIG_I386_DIS) += i386.o
|
||||
universal-obj-$(CONFIG_IA64_DIS) += ia64.o
|
||||
universal-obj-$(CONFIG_M68K_DIS) += m68k.o
|
||||
universal-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o
|
||||
universal-obj-$(CONFIG_MIPS_DIS) += mips.o
|
||||
universal-obj-$(CONFIG_PPC_DIS) += ppc.o
|
||||
universal-obj-$(CONFIG_S390_DIS) += s390.o
|
||||
universal-obj-$(CONFIG_SH4_DIS) += sh4.o
|
||||
universal-obj-$(CONFIG_SPARC_DIS) += sparc.o
|
||||
universal-obj-$(CONFIG_LM32_DIS) += lm32.o
|
||||
common-obj-$(CONFIG_ALPHA_DIS) += alpha.o
|
||||
common-obj-$(CONFIG_ARM_DIS) += arm.o
|
||||
common-obj-$(CONFIG_CRIS_DIS) += cris.o
|
||||
common-obj-$(CONFIG_HPPA_DIS) += hppa.o
|
||||
common-obj-$(CONFIG_I386_DIS) += i386.o
|
||||
common-obj-$(CONFIG_IA64_DIS) += ia64.o
|
||||
common-obj-$(CONFIG_M68K_DIS) += m68k.o
|
||||
common-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o
|
||||
common-obj-$(CONFIG_MIPS_DIS) += mips.o
|
||||
common-obj-$(CONFIG_PPC_DIS) += ppc.o
|
||||
common-obj-$(CONFIG_S390_DIS) += s390.o
|
||||
common-obj-$(CONFIG_SH4_DIS) += sh4.o
|
||||
common-obj-$(CONFIG_SPARC_DIS) += sparc.o
|
||||
common-obj-$(CONFIG_LM32_DIS) += lm32.o
|
||||
|
||||
universal-obj-$(CONFIG_TCI_DIS) += tci.o
|
||||
# TODO: As long as the TCG interpreter and its generated code depend
|
||||
# on the QEMU target, we cannot compile the disassembler here.
|
||||
#common-obj-$(CONFIG_TCI_DIS) += tci.o
|
||||
|
|
|
@ -226,7 +226,7 @@ struct dis_private {
|
|||
bfd_byte the_buffer[MAX_MNEM_SIZE];
|
||||
bfd_vma insn_start;
|
||||
int orig_sizeflag;
|
||||
jmp_buf bailout;
|
||||
sigjmp_buf bailout;
|
||||
};
|
||||
|
||||
enum address_mode
|
||||
|
@ -303,7 +303,7 @@ fetch_data2(struct disassemble_info *info, bfd_byte *addr)
|
|||
STATUS. */
|
||||
if (priv->max_fetched == priv->the_buffer)
|
||||
(*info->memory_error_func) (status, start, info);
|
||||
longjmp (priv->bailout, 1);
|
||||
siglongjmp(priv->bailout, 1);
|
||||
}
|
||||
else
|
||||
priv->max_fetched = addr;
|
||||
|
@ -3661,7 +3661,7 @@ print_insn (bfd_vma pc, disassemble_info *info)
|
|||
start_codep = priv.the_buffer;
|
||||
codep = priv.the_buffer;
|
||||
|
||||
if (setjmp (priv.bailout) != 0)
|
||||
if (sigsetjmp(priv.bailout, 0) != 0)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
|
@ -4720,7 +4720,8 @@ print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp)
|
|||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
snprintf_vma (tmp, sizeof(tmp), disp);
|
||||
for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++);
|
||||
for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++) {
|
||||
}
|
||||
pstrcpy (buf + 2, bufsize - 2, tmp + i);
|
||||
}
|
||||
else
|
||||
|
|
11
disas/m68k.c
11
disas/m68k.c
|
@ -624,7 +624,7 @@ struct private
|
|||
bfd_byte *max_fetched;
|
||||
bfd_byte the_buffer[MAXLEN];
|
||||
bfd_vma insn_start;
|
||||
jmp_buf bailout;
|
||||
sigjmp_buf bailout;
|
||||
};
|
||||
|
||||
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
|
||||
|
@ -644,7 +644,7 @@ fetch_data2(struct disassemble_info *info, bfd_byte *addr)
|
|||
if (status != 0)
|
||||
{
|
||||
(*info->memory_error_func) (status, start, info);
|
||||
longjmp (priv->bailout, 1);
|
||||
siglongjmp(priv->bailout, 1);
|
||||
}
|
||||
else
|
||||
priv->max_fetched = addr;
|
||||
|
@ -1912,9 +1912,10 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
|
|||
priv.max_fetched = priv.the_buffer;
|
||||
priv.insn_start = memaddr;
|
||||
|
||||
if (setjmp (priv.bailout) != 0)
|
||||
/* Error return. */
|
||||
return -1;
|
||||
if (sigsetjmp(priv.bailout, 0) != 0) {
|
||||
/* Error return. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (info->mach)
|
||||
{
|
||||
|
|
173
disas/s390.c
173
disas/s390.c
|
@ -589,6 +589,16 @@ static const struct s390_operand s390_operands[] =
|
|||
{ 4, 32, S390_OPERAND_CCODE },
|
||||
#define I8_32 46 /* 8 bit signed value starting at 32 */
|
||||
{ 8, 32, S390_OPERAND_SIGNED },
|
||||
#define U8_24 47 /* 8 bit unsigned value starting at 24 */
|
||||
{ 8, 24, 0 },
|
||||
#define U8_32 48 /* 8 bit unsigned value starting at 32 */
|
||||
{ 8, 32, 0 },
|
||||
#define I16_32 49
|
||||
{ 16, 32, S390_OPERAND_SIGNED },
|
||||
#define M4_16 50 /* 4-bit condition-code starting at 12 */
|
||||
{ 4, 16, S390_OPERAND_CCODE },
|
||||
#define I8_16 51
|
||||
{ 8, 16, S390_OPERAND_SIGNED },
|
||||
/* QEMU-END */
|
||||
};
|
||||
|
||||
|
@ -663,7 +673,9 @@ static const struct s390_operand s390_operands[] =
|
|||
This is just a workaround for existing code e.g. glibc. */
|
||||
#define INSTR_RRE_RR_OPT 4, { R_24,RO_28,0,0,0,0 } /* efpc, sfpc */
|
||||
#define INSTR_RRF_F0FF 4, { F_16,F_24,F_28,0,0,0 } /* e.g. madbr */
|
||||
#define INSTR_RRF_F0FF2 4, { F_24,F_16,F_28,0,0,0 } /* e.g. cpsdr */
|
||||
/* QEMU-MOD */
|
||||
#define INSTR_RRF_F0FF2 4, { F_24,F_28,F_16,0,0,0 } /* e.g. cpsdr */
|
||||
/* QEMU-END */
|
||||
#define INSTR_RRF_F0FR 4, { F_24,F_16,R_28,0,0,0 } /* e.g. iedtr */
|
||||
#define INSTR_RRF_FUFF 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. didbr */
|
||||
#define INSTR_RRF_RURR 4, { R_24,R_28,R_16,U4_20,0,0 } /* e.g. .insn */
|
||||
|
@ -801,11 +813,35 @@ static const struct s390_operand s390_operands[] =
|
|||
#define MASK_SSF_RRDRD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
|
||||
|
||||
/* QEMU-ADD: */
|
||||
#define INSTR_RIE_MRRP 6, { M4_32,R_8,R_12,J16_16,0,0 } /* e.g. crj */
|
||||
#define INSTR_RIE_MRRP 6, { M4_32, R_8, R_12, J16_16, 0, 0 } /* e.g. crj */
|
||||
#define MASK_RIE_MRRP { 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff }
|
||||
|
||||
#define INSTR_RIE_MRIP 6, { M4_12,R_8,I8_32,J16_16,0,0 } /* e.g. cij */
|
||||
#define INSTR_RIE_MRIP 6, { M4_12, R_8, I8_32, J16_16, 0, 0 } /* e.g. cij */
|
||||
#define MASK_RIE_MRIP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
|
||||
#define INSTR_RIE_RRIII 6, { R_8, R_12, U8_16, U8_24, U8_32, 0 } /* risbg */
|
||||
#define MASK_RIE_RRIII { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
#define INSTR_RIE_MRI 6, { M4_32, R_8, I16_16, 0, 0, 0 } /* e.g. cit */
|
||||
#define MASK_RIE_MRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
#define INSTR_RIE_MRU 6, { M4_32, R_8, U16_16, 0, 0, 0 } /* e.g. clfit */
|
||||
#define MASK_RIE_MRU { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
#define INSTR_RIE_RRI 6, { R_8, R_12, I16_16, 0, 0, 0 }
|
||||
#define MASK_RIE_RRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
|
||||
#define INSTR_RXY_URRD 6, { U8_8, D20_20, X_12, B_16, 0, 0 }
|
||||
#define MASK_RXY_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
|
||||
#define INSTR_SIL_DRI 6, { D_20, B_16, I16_32, 0, 0, 0 }
|
||||
#define MASK_SIL_DRI { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
|
||||
|
||||
#define INSTR_RSY_MRRD 6, { M4_12, R_8, D20_20, B_16, 0, 0 }
|
||||
#define MASK_SRY_MRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
|
||||
#define INSTR_RRF_MRR 6, { M4_16, R_24, R_28, 0, 0, 0 }
|
||||
#define MASK_RRF_MRR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
|
||||
|
||||
#define INSTR_SIY_DRI 6, { D20_20, B_16, I8_16, 0, 0, 0 }
|
||||
#define MASK_SIY_DRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
|
||||
/* QEMU-END */
|
||||
|
||||
/* The opcode formats table (blueprints for .insn pseudo mnemonic). */
|
||||
|
@ -926,6 +962,30 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "ldeb", OP48(0xed0000000004LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
|
||||
{ "brxlg", OP48(0xec0000000045LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2},
|
||||
{ "brxhg", OP48(0xec0000000044LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2},
|
||||
/* QEMU-ADD: */
|
||||
{ "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "risbg", OP48(0xec0000000055LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6},
|
||||
{ "risbhg", OP48(0xec000000005dLL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6},
|
||||
{ "risblg", OP48(0xec0000000051LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6},
|
||||
{ "rnsbg", OP48(0xec0000000054LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6},
|
||||
{ "rosbg", OP48(0xec0000000056LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6},
|
||||
{ "rxsbg", OP48(0xec0000000057LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6},
|
||||
{ "cit", OP48(0xec0000000072LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6},
|
||||
{ "cgit", OP48(0xec0000000070LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6},
|
||||
{ "clfit", OP48(0xec0000000073LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6},
|
||||
{ "clgit", OP48(0xec0000000071LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6},
|
||||
{ "ahik", OP48(0xec00000000d8LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6},
|
||||
{ "aghik", OP48(0xec00000000d9LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6},
|
||||
{ "alhsik", OP48(0xec00000000daLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6},
|
||||
{ "alghsik", OP48(0xec00000000dbLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "tp", OP48(0xeb00000000c0LL), MASK_RSL_R0RD, INSTR_RSL_R0RD, 3, 0},
|
||||
{ "stamy", OP48(0xeb000000009bLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3},
|
||||
{ "lamy", OP48(0xeb000000009aLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3},
|
||||
|
@ -985,6 +1045,20 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "srag", OP48(0xeb000000000aLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
|
||||
{ "lmg", OP48(0xeb0000000004LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
|
||||
{ "lmg", OP48(0xeb0000000004LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
|
||||
/* QEMU-ADD: */
|
||||
{ "loc", OP48(0xeb00000000f2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6},
|
||||
{ "locg", OP48(0xeb00000000e2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6},
|
||||
{ "stoc", OP48(0xeb00000000f3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6},
|
||||
{ "stocg", OP48(0xeb00000000e3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6},
|
||||
{ "srak", OP48(0xeb00000000dcLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6},
|
||||
{ "slak", OP48(0xeb00000000ddLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6},
|
||||
{ "srlk", OP48(0xeb00000000deLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6},
|
||||
{ "sllk", OP48(0xeb00000000dfLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6},
|
||||
{ "asi", OP48(0xeb000000006aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6},
|
||||
{ "alsi", OP48(0xeb000000006eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6},
|
||||
{ "agsi", OP48(0xeb000000007aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6},
|
||||
{ "algsi", OP48(0xeb000000007eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "unpka", OP8(0xeaLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
|
||||
{ "pka", OP8(0xe9LL), MASK_SS_L2RDRD, INSTR_SS_L2RDRD, 3, 0},
|
||||
{ "mvcin", OP8(0xe8LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
|
||||
|
@ -993,6 +1067,17 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "tprot", OP16(0xe501LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0},
|
||||
{ "strag", OP48(0xe50000000002LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 2, 2},
|
||||
{ "lasp", OP16(0xe500LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0},
|
||||
/* QEMU-ADD: */
|
||||
{ "mvhhi", OP16(0xe544LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "mvghi", OP16(0xe548LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "mvhi", OP16(0xe54cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "chhsi", OP16(0xe554LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "clhhsi", OP16(0xe555LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "cghsi", OP16(0xe558LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "clghsi", OP16(0xe559LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "chsi", OP16(0xe55cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
{ "clfhsi", OP16(0xe55dLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "slb", OP48(0xe30000000099LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
|
||||
{ "slb", OP48(0xe30000000099LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
|
||||
{ "alc", OP48(0xe30000000098LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
|
||||
|
@ -1116,6 +1201,9 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "lrag", OP48(0xe30000000003LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
|
||||
{ "lrag", OP48(0xe30000000003LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
|
||||
{ "ltg", OP48(0xe30000000002LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4},
|
||||
/* QEMU-ADD: */
|
||||
{ "pfd", OP48(0xe30000000036LL), MASK_RXY_URRD, INSTR_RXY_URRD, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "unpku", OP8(0xe2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
|
||||
{ "pku", OP8(0xe1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
|
||||
{ "edmk", OP8(0xdfLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
|
||||
|
@ -1135,6 +1223,32 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "csst", OP16(0xc802LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5},
|
||||
{ "ectg", OP16(0xc801LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5},
|
||||
{ "mvcos", OP16(0xc800LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 4},
|
||||
/* QEMU-ADD: */
|
||||
{ "exrl", OP16(0xc600ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "pfdrl", OP16(0xc602ll), MASK_RIL_UP, INSTR_RIL_UP, 3, 6},
|
||||
{ "cghrl", OP16(0xc604ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "chrl", OP16(0xc605ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "clghrl", OP16(0xc606ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "clhrl", OP16(0xc607ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "cgrl", OP16(0xc608ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "clgrl", OP16(0xc60all), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "cgfrl", OP16(0xc60cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "crl", OP16(0xc60dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "clgfrl", OP16(0xc60ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "clrl", OP16(0xc60fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
|
||||
{ "llhrl", OP16(0xc400ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lghrl", OP16(0xc404ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lhrl", OP16(0xc405ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "llghrl", OP16(0xc406ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "sthrl", OP16(0xc407ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "stgrl", OP16(0xc40bll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "llgfrl", OP16(0xc40ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "strl", OP16(0xc40fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "clfi", OP16(0xc20fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
|
||||
{ "clgfi", OP16(0xc20eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
|
||||
{ "cfi", OP16(0xc20dLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4},
|
||||
|
@ -1265,6 +1379,29 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "ltgr", OP16(0xb902LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
|
||||
{ "lngr", OP16(0xb901LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
|
||||
{ "lpgr", OP16(0xb900LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
|
||||
/* QEMU-ADD: */
|
||||
{ "crt", OP16(0xb972LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6},
|
||||
{ "cgrt", OP16(0xb960LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6},
|
||||
{ "clrt", OP16(0xb973LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6},
|
||||
{ "clgrt", OP16(0xb961LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6},
|
||||
{ "locr", OP16(0xb9f2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6},
|
||||
{ "locgr", OP16(0xb9e2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6},
|
||||
{ "popcnt", OP16(0xb9e1LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 6},
|
||||
{ "ngrk", OP16(0xb9e4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "ogrk", OP16(0xb9e6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "xgrk", OP16(0xb9e7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "agrk", OP16(0xb9e8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "sgrk", OP16(0xb9e9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "algrk", OP16(0xb9eaLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "slgrk", OP16(0xb9ebLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "nrk", OP16(0xb9f4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "ork", OP16(0xb9f6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "xrk", OP16(0xb9f7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "ark", OP16(0xb9f8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "srk", OP16(0xb9f9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "alrk", OP16(0xb9faLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
{ "slrk", OP16(0xb9fbLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "lctl", OP8(0xb7LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0},
|
||||
{ "stctl", OP8(0xb6LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0},
|
||||
{ "rrxtr", OP16(0xb3ffLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5},
|
||||
|
@ -1426,6 +1563,20 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "ltebr", OP16(0xb302LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
|
||||
{ "lnebr", OP16(0xb301LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
|
||||
{ "lpebr", OP16(0xb300LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
|
||||
/* QEMU-ADD: */
|
||||
{ "clfebr", OP16(0xb39cLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "clfdbr", OP16(0xb39dLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "clfxbr", OP16(0xb39eLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "clgebr", OP16(0xb3acLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "clgdbr", OP16(0xb3adLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "clgxbr", OP16(0xb3aeLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "celfbr", OP16(0xb390LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "cdlfbr", OP16(0xb391LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "cxlfbr", OP16(0xb392LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "celgbr", OP16(0xb3a0LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "cdlgbr", OP16(0xb3a1LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
{ "cxlgbr", OP16(0xb3a2LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6},
|
||||
/* QEMU-END */
|
||||
{ "trap4", OP16(0xb2ffLL), MASK_S_RD, INSTR_S_RD, 3, 0},
|
||||
{ "lfas", OP16(0xb2bdLL), MASK_S_RD, INSTR_S_RD, 2, 5},
|
||||
{ "srnmt", OP16(0xb2b9LL), MASK_S_RD, INSTR_S_RD, 2, 5},
|
||||
|
@ -1774,22 +1925,6 @@ static const struct s390_opcode s390_opcodes[] =
|
|||
{ "sckpf", OP16(0x0107LL), MASK_E, INSTR_E, 3, 0},
|
||||
{ "upt", OP16(0x0102LL), MASK_E, INSTR_E, 3, 0},
|
||||
{ "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0},
|
||||
|
||||
/* QEMU-ADD: */
|
||||
{ "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
{ "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
|
||||
|
||||
{ "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
{ "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
|
||||
|
||||
{ "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
{ "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
|
||||
/* QEMU-END */
|
||||
};
|
||||
|
||||
static const int s390_num_opcodes =
|
||||
|
|
|
@ -55,10 +55,7 @@ QEMUFile with:
|
|||
QEMUFile *qemu_fopen_ops(void *opaque,
|
||||
QEMUFilePutBufferFunc *put_buffer,
|
||||
QEMUFileGetBufferFunc *get_buffer,
|
||||
QEMUFileCloseFunc *close,
|
||||
QEMUFileRateLimit *rate_limit,
|
||||
QEMUFileSetRateLimit *set_rate_limit,
|
||||
QEMUFileGetRateLimit *get_rate_limit);
|
||||
QEMUFileCloseFunc *close);
|
||||
|
||||
The functions have the following functionality:
|
||||
|
||||
|
@ -80,24 +77,9 @@ Close a file and return an error code.
|
|||
|
||||
typedef int (QEMUFileCloseFunc)(void *opaque);
|
||||
|
||||
Called to determine if the file has exceeded its bandwidth allocation. The
|
||||
bandwidth capping is a soft limit, not a hard limit.
|
||||
|
||||
typedef int (QEMUFileRateLimit)(void *opaque);
|
||||
|
||||
Called to change the current bandwidth allocation. This function must return
|
||||
the new actual bandwidth. It should be new_rate if everything goes OK, and
|
||||
the old rate otherwise.
|
||||
|
||||
typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate);
|
||||
typedef size_t (QEMUFileGetRateLimit)(void *opaque);
|
||||
|
||||
You can use any internal state that you need using the opaque void *
|
||||
pointer that is passed to all functions.
|
||||
|
||||
The rate limiting functions are used to limit the bandwidth used by
|
||||
QEMU migration.
|
||||
|
||||
The important functions for us are put_buffer()/get_buffer() that
|
||||
allow to write/read a buffer into the QEMUFile.
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
################################################################
|
||||
#
|
||||
# qemu -M q35 creates a bare machine with just the very essential
|
||||
# chipset devices being present:
|
||||
#
|
||||
# 00.0 - Host bridge
|
||||
# 1f.0 - ISA bridge / LPC
|
||||
# 1f.2 - SATA (AHCI) controller
|
||||
# 1f.3 - SMBus controller
|
||||
#
|
||||
# This config file documents the other devices and how they are
|
||||
# created. You can simply use "-readconfig $thisfile" to create
|
||||
# them all. Here is a overview:
|
||||
#
|
||||
# 19.0 - Ethernet controller (not created, our e1000 emulation
|
||||
# doesn't emulate the ich9 device).
|
||||
# 1a.* - USB Controller #2 (ehci + uhci companions)
|
||||
# 1b.0 - HD Audio Controller
|
||||
# 1c.* - PCI Express Ports
|
||||
# 1d.* - USB Controller #1 (ehci + uhci companions,
|
||||
# "qemu -M q35 -usb" creates these too)
|
||||
# 1e.0 - PCI Bridge
|
||||
#
|
||||
|
||||
[device "ich9-ehci-2"]
|
||||
driver = "ich9-usb-ehci2"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1a.7"
|
||||
|
||||
[device "ich9-uhci-4"]
|
||||
driver = "ich9-usb-uhci4"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1a.0"
|
||||
masterbus = "ich9-ehci-2.0"
|
||||
firstport = "0"
|
||||
|
||||
[device "ich9-uhci-5"]
|
||||
driver = "ich9-usb-uhci5"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1a.1"
|
||||
masterbus = "ich9-ehci-2.0"
|
||||
firstport = "2"
|
||||
|
||||
[device "ich9-uhci-6"]
|
||||
driver = "ich9-usb-uhci6"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1a.2"
|
||||
masterbus = "ich9-ehci-2.0"
|
||||
firstport = "4"
|
||||
|
||||
|
||||
[device "ich9-hda-audio"]
|
||||
driver = "ich9-intel-hda"
|
||||
bus = "pcie.0"
|
||||
addr = "1b.0"
|
||||
|
||||
|
||||
[device "ich9-pcie-port-1"]
|
||||
driver = "ioh3420"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1c.0"
|
||||
port = "1"
|
||||
chassis = "1"
|
||||
|
||||
[device "ich9-pcie-port-2"]
|
||||
driver = "ioh3420"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1c.1"
|
||||
port = "2"
|
||||
chassis = "2"
|
||||
|
||||
[device "ich9-pcie-port-3"]
|
||||
driver = "ioh3420"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1c.2"
|
||||
port = "3"
|
||||
chassis = "3"
|
||||
|
||||
[device "ich9-pcie-port-4"]
|
||||
driver = "ioh3420"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1c.3"
|
||||
port = "4"
|
||||
chassis = "4"
|
||||
|
||||
|
||||
[device "ich9-ehci-1"]
|
||||
driver = "ich9-usb-ehci1"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1d.7"
|
||||
|
||||
[device "ich9-uhci-1"]
|
||||
driver = "ich9-usb-uhci1"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1d.0"
|
||||
masterbus = "ich9-ehci-1.0"
|
||||
firstport = "0"
|
||||
|
||||
[device "ich9-uhci-2"]
|
||||
driver = "ich9-usb-uhci2"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1d.1"
|
||||
masterbus = "ich9-ehci-1.0"
|
||||
firstport = "2"
|
||||
|
||||
[device "ich9-uhci-3"]
|
||||
driver = "ich9-usb-uhci3"
|
||||
multifunction = "on"
|
||||
bus = "pcie.0"
|
||||
addr = "1d.2"
|
||||
masterbus = "ich9-ehci-1.0"
|
||||
firstport = "4"
|
||||
|
||||
|
||||
[device "ich9-pci-bridge"]
|
||||
driver = "i82801b11-bridge"
|
||||
bus = "pcie.0"
|
||||
addr = "1e.0"
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
PCI IDs for qemu
|
||||
================
|
||||
|
||||
Red Hat, Inc. donates a part of its device ID range to qemu, to be used for
|
||||
virtual devices. The vendor IDs are 1af4 (formerly Qumranet ID) and 1b36.
|
||||
|
||||
Contact Gerd Hoffmann <kraxel@redhat.com> to get a device ID assigned
|
||||
for your devices.
|
||||
|
||||
1af4 vendor ID
|
||||
--------------
|
||||
|
||||
The 1000 -> 10ff device ID range is used as follows for virtio-pci devices.
|
||||
Note that this allocation separate from the virtio device IDs, which are
|
||||
maintained as part of the virtio specification.
|
||||
|
||||
1af4:1000 network device
|
||||
1af4:1001 block device
|
||||
1af4:1002 balloon device
|
||||
1af4:1003 console device
|
||||
1af4:1004 SCSI host bus adapter device
|
||||
1af4:1005 entropy generator device
|
||||
1af4:1009 9p filesystem device
|
||||
|
||||
1af4:10f0 Available for experimental usage without registration. Must get
|
||||
to official ID when the code leaves the test lab (i.e. when seeking
|
||||
1af4:10ff upstream merge or shipping a distro/product) to avoid conflicts.
|
||||
|
||||
1af4:1100 Used as PCI Subsystem ID for existing hardware devices emulated
|
||||
by qemu.
|
||||
|
||||
1af4:1110 ivshmem device (shared memory, docs/specs/ivshmem_device_spec.txt)
|
||||
|
||||
All other device IDs are reserved.
|
||||
|
||||
1b36 vendor ID
|
||||
--------------
|
||||
|
||||
The 0000 -> 00ff device ID range is used as follows for QEMU-specific
|
||||
PCI devices (other than virtio):
|
||||
|
||||
1b36:0001 PCI-PCI bridge
|
||||
1b36:0002 PCI serial port (16550A) adapter (docs/specs/pci-serial.txt)
|
||||
1b36:0003 PCI Dual-port 16550A adapter (docs/specs/pci-serial.txt)
|
||||
1b36:0004 PCI Quad-port 16550A adapter (docs/specs/pci-serial.txt)
|
||||
|
||||
All these devices are documented in docs/specs.
|
||||
|
||||
The 0100 device ID is used for the QXL video card device.
|
|
@ -23,7 +23,7 @@ for debugging, profiling, and observing execution.
|
|||
|
||||
4. Pretty-print the binary trace file:
|
||||
|
||||
./simpletrace.py trace-events trace-*
|
||||
./scripts/simpletrace.py trace-events trace-*
|
||||
|
||||
== Trace events ==
|
||||
|
||||
|
@ -198,7 +198,7 @@ The "simple" backend produces binary trace files that can be formatted with the
|
|||
simpletrace.py script. The script takes the "trace-events" file and the binary
|
||||
trace:
|
||||
|
||||
./simpletrace.py trace-events trace-12345
|
||||
./scripts/simpletrace.py trace-events trace-12345
|
||||
|
||||
You must ensure that the same "trace-events" file was used to build QEMU,
|
||||
otherwise trace event declarations may have changed and output will not be
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
qemu usb storage emulation
|
||||
--------------------------
|
||||
|
||||
QEMU has two emulations for usb storage devices.
|
||||
QEMU has three devices for usb storage emulation.
|
||||
|
||||
Number one emulates the classic bulk-only transport protocol which is
|
||||
used by 99% of the usb sticks on the marked today and is called
|
||||
|
@ -31,6 +31,15 @@ with tree logical units:
|
|||
-device scsi-cd,bus=uas.0,scsi-id=0,lun=5,drive=uas-cdrom
|
||||
|
||||
|
||||
Number three emulates the classic bulk-only transport protocol too.
|
||||
It's called "usb-bot". It shares most code with "usb-storage", and
|
||||
the guest will not be able to see the difference. The qemu command
|
||||
line interface is simliar to usb-uas though, i.e. no automatic scsi
|
||||
disk creation. It also features support for up to 16 LUNs. The LUN
|
||||
numbers must be continous, i.e. for three devices you must use 0+1+2.
|
||||
The 0+1+5 numbering from the "usb-uas" example isn't going to work
|
||||
with "usb-bot".
|
||||
|
||||
enjoy,
|
||||
Gerd
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
virtio balloon memory statistics
|
||||
================================
|
||||
|
||||
The virtio balloon driver supports guest memory statistics reporting. These
|
||||
statistics are available to QEMU users as QOM (QEMU Object Model) device
|
||||
properties via a polling mechanism.
|
||||
|
||||
Before querying the available stats, clients first have to enable polling.
|
||||
This is done by writing a time interval value (in seconds) to the
|
||||
guest-stats-polling-interval property. This value can be:
|
||||
|
||||
> 0 enables polling in the specified interval. If polling is already
|
||||
enabled, the polling time interval is changed to the new value
|
||||
|
||||
0 disables polling. Previous polled statistics are still valid and
|
||||
can be queried.
|
||||
|
||||
Once polling is enabled, the virtio-balloon device in QEMU will start
|
||||
polling the guest's balloon driver for new stats in the specified time
|
||||
interval.
|
||||
|
||||
To retrieve those stats, clients have to query the guest-stats property,
|
||||
which will return a dictionary containing:
|
||||
|
||||
o A key named 'stats', containing all available stats. If the guest
|
||||
doesn't support a particular stat, or if it couldn't be retrieved,
|
||||
its value will be -1. Currently, the following stats are supported:
|
||||
|
||||
- stat-swap-in
|
||||
- stat-swap-out
|
||||
- stat-major-faults
|
||||
- stat-minor-faults
|
||||
- stat-free-memory
|
||||
- stat-total-memory
|
||||
|
||||
o A key named last-update, which contains the last stats update
|
||||
timestamp in seconds. Since this timestamp is generated by the host,
|
||||
a buggy guest can't influence its value
|
||||
|
||||
It's also important to note the following:
|
||||
|
||||
- Previously polled statistics remain available even if the polling is
|
||||
later disabled
|
||||
|
||||
- As noted above, if a guest doesn't support a particular stat its value
|
||||
will always be -1. However, it's also possible that a guest temporarily
|
||||
couldn't update one or even all stats. If this happens, just wait for
|
||||
the next update
|
||||
|
||||
- Polling can be enabled even if the guest doesn't have stats support
|
||||
or the balloon driver wasn't loaded in the guest. If this is the case
|
||||
and stats are queried, an error will be returned
|
||||
|
||||
- The polling timer is only re-armed when the guest responds to the
|
||||
statistics request. This means that if a (buggy) guest doesn't ever
|
||||
respond to the request the timer will never be re-armed, which has
|
||||
the same effect as disabling polling
|
||||
|
||||
Here are a few examples. QEMU is started with '-balloon virtio', which
|
||||
generates '/machine/peripheral-anon/device[1]' as the QOM path for the
|
||||
balloon device.
|
||||
|
||||
Enable polling with 2 seconds interval:
|
||||
|
||||
{ "execute": "qom-set",
|
||||
"arguments": { "path": "/machine/peripheral-anon/device[1]",
|
||||
"property": "guest-stats-polling-interval", "value": 2 } }
|
||||
|
||||
{ "return": {} }
|
||||
|
||||
Change polling to 10 seconds:
|
||||
|
||||
{ "execute": "qom-set",
|
||||
"arguments": { "path": "/machine/peripheral-anon/device[1]",
|
||||
"property": "guest-stats-polling-interval", "value": 10 } }
|
||||
|
||||
{ "return": {} }
|
||||
|
||||
Get stats:
|
||||
|
||||
{ "execute": "qom-get",
|
||||
"arguments": { "path": "/machine/peripheral-anon/device[1]",
|
||||
"property": "guest-stats" } }
|
||||
{
|
||||
"return": {
|
||||
"stats": {
|
||||
"stat-swap-out": 0,
|
||||
"stat-free-memory": 844943360,
|
||||
"stat-minor-faults": 219028,
|
||||
"stat-major-faults": 235,
|
||||
"stat-total-memory": 1044406272,
|
||||
"stat-swap-in": 0
|
||||
},
|
||||
"last-update": 1358529861
|
||||
}
|
||||
}
|
||||
|
||||
Disable polling:
|
||||
|
||||
{ "execute": "qom-set",
|
||||
"arguments": { "path": "/machine/peripheral-anon/device[1]",
|
||||
"property": "stats-polling-interval", "value": 0 } }
|
||||
|
||||
{ "return": {} }
|
8
dump.c
8
dump.c
|
@ -271,11 +271,13 @@ static int write_elf64_note(DumpState *s)
|
|||
static int write_elf64_notes(DumpState *s)
|
||||
{
|
||||
CPUArchState *env;
|
||||
CPUState *cpu;
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
id = cpu_index(env);
|
||||
cpu = ENV_GET_CPU(env);
|
||||
id = cpu_index(cpu);
|
||||
ret = cpu_write_elf64_note(fd_write_vmcore, env, id, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write elf notes.\n");
|
||||
|
@ -321,11 +323,13 @@ static int write_elf32_note(DumpState *s)
|
|||
static int write_elf32_notes(DumpState *s)
|
||||
{
|
||||
CPUArchState *env;
|
||||
CPUState *cpu;
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
id = cpu_index(env);
|
||||
cpu = ENV_GET_CPU(env);
|
||||
id = cpu_index(cpu);
|
||||
ret = cpu_write_elf32_note(fd_write_vmcore, env, id, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write elf notes.\n");
|
||||
|
|
72
exec.c
72
exec.c
|
@ -78,7 +78,7 @@ DEFINE_TLS(CPUArchState *,cpu_single_env);
|
|||
/* 0 = Do not count executed instructions.
|
||||
1 = Precise instruction counting.
|
||||
2 = Adaptive rate instruction counting. */
|
||||
int use_icount = 0;
|
||||
int use_icount;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
|
@ -219,16 +219,16 @@ void cpu_exec_init_all(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
static int cpu_common_post_load(void *opaque, int version_id)
|
||||
{
|
||||
CPUArchState *env = opaque;
|
||||
CPUState *cpu = opaque;
|
||||
|
||||
/* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the
|
||||
version_id is increased. */
|
||||
env->interrupt_request &= ~0x01;
|
||||
tlb_flush(env, 1);
|
||||
cpu->interrupt_request &= ~0x01;
|
||||
tlb_flush(cpu->env_ptr, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -240,31 +240,35 @@ static const VMStateDescription vmstate_cpu_common = {
|
|||
.minimum_version_id_old = 1,
|
||||
.post_load = cpu_common_post_load,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_UINT32(halted, CPUArchState),
|
||||
VMSTATE_UINT32(interrupt_request, CPUArchState),
|
||||
VMSTATE_UINT32(halted, CPUState),
|
||||
VMSTATE_UINT32(interrupt_request, CPUState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
#else
|
||||
#define vmstate_cpu_common vmstate_dummy
|
||||
#endif
|
||||
|
||||
CPUArchState *qemu_get_cpu(int cpu)
|
||||
CPUState *qemu_get_cpu(int index)
|
||||
{
|
||||
CPUArchState *env = first_cpu;
|
||||
CPUState *cpu = NULL;
|
||||
|
||||
while (env) {
|
||||
if (env->cpu_index == cpu)
|
||||
cpu = ENV_GET_CPU(env);
|
||||
if (cpu->cpu_index == index) {
|
||||
break;
|
||||
}
|
||||
env = env->next_cpu;
|
||||
}
|
||||
|
||||
return env;
|
||||
return env ? cpu : NULL;
|
||||
}
|
||||
|
||||
void cpu_exec_init(CPUArchState *env)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
#endif
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
CPUArchState **penv;
|
||||
int cpu_index;
|
||||
|
||||
|
@ -278,8 +282,8 @@ void cpu_exec_init(CPUArchState *env)
|
|||
penv = &(*penv)->next_cpu;
|
||||
cpu_index++;
|
||||
}
|
||||
env->cpu_index = cpu_index;
|
||||
env->numa_node = 0;
|
||||
cpu->cpu_index = cpu_index;
|
||||
cpu->numa_node = 0;
|
||||
QTAILQ_INIT(&env->breakpoints);
|
||||
QTAILQ_INIT(&env->watchpoints);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
@ -289,11 +293,15 @@ void cpu_exec_init(CPUArchState *env)
|
|||
#if defined(CONFIG_USER_ONLY)
|
||||
cpu_list_unlock();
|
||||
#endif
|
||||
vmstate_register(NULL, cpu_index, &vmstate_cpu_common, cpu);
|
||||
#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
|
||||
vmstate_register(NULL, cpu_index, &vmstate_cpu_common, env);
|
||||
register_savevm(NULL, "cpu", cpu_index, CPU_SAVE_VERSION,
|
||||
cpu_save, cpu_load, env);
|
||||
assert(cc->vmsd == NULL);
|
||||
#endif
|
||||
if (cc->vmsd != NULL) {
|
||||
vmstate_register(NULL, cpu_index, cc->vmsd, cpu);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(TARGET_HAS_ICE)
|
||||
|
@ -484,15 +492,12 @@ void cpu_single_step(CPUArchState *env, int enabled)
|
|||
#endif
|
||||
}
|
||||
|
||||
void cpu_reset_interrupt(CPUArchState *env, int mask)
|
||||
{
|
||||
env->interrupt_request &= ~mask;
|
||||
}
|
||||
|
||||
void cpu_exit(CPUArchState *env)
|
||||
{
|
||||
env->exit_request = 1;
|
||||
cpu_unlink_tb(env);
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
|
||||
cpu->exit_request = 1;
|
||||
cpu->tcg_exit_req = 1;
|
||||
}
|
||||
|
||||
void cpu_abort(CPUArchState *env, const char *fmt, ...)
|
||||
|
@ -531,7 +536,6 @@ CPUArchState *cpu_copy(CPUArchState *env)
|
|||
{
|
||||
CPUArchState *new_env = cpu_init(env->cpu_model_str);
|
||||
CPUArchState *next_cpu = new_env->next_cpu;
|
||||
int cpu_index = new_env->cpu_index;
|
||||
#if defined(TARGET_HAS_ICE)
|
||||
CPUBreakpoint *bp;
|
||||
CPUWatchpoint *wp;
|
||||
|
@ -539,9 +543,8 @@ CPUArchState *cpu_copy(CPUArchState *env)
|
|||
|
||||
memcpy(new_env, env, sizeof(CPUArchState));
|
||||
|
||||
/* Preserve chaining and index. */
|
||||
/* Preserve chaining. */
|
||||
new_env->next_cpu = next_cpu;
|
||||
new_env->cpu_index = cpu_index;
|
||||
|
||||
/* Clone all break/watchpoints.
|
||||
Note: Once we support ptrace with hw-debug register access, make sure
|
||||
|
@ -843,6 +846,8 @@ static void *file_ram_alloc(RAMBlock *block,
|
|||
const char *path)
|
||||
{
|
||||
char *filename;
|
||||
char *sanitized_name;
|
||||
char *c;
|
||||
void *area;
|
||||
int fd;
|
||||
#ifdef MAP_POPULATE
|
||||
|
@ -864,18 +869,25 @@ static void *file_ram_alloc(RAMBlock *block,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (asprintf(&filename, "%s/qemu_back_mem.XXXXXX", path) == -1) {
|
||||
return NULL;
|
||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||
sanitized_name = g_strdup(block->mr->name);
|
||||
for (c = sanitized_name; *c != '\0'; c++) {
|
||||
if (*c == '/')
|
||||
*c = '_';
|
||||
}
|
||||
|
||||
filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path,
|
||||
sanitized_name);
|
||||
g_free(sanitized_name);
|
||||
|
||||
fd = mkstemp(filename);
|
||||
if (fd < 0) {
|
||||
perror("unable to create backing store for hugepages");
|
||||
free(filename);
|
||||
g_free(filename);
|
||||
return NULL;
|
||||
}
|
||||
unlink(filename);
|
||||
free(filename);
|
||||
g_free(filename);
|
||||
|
||||
memory = (memory+hpagesize-1) & ~(hpagesize-1);
|
||||
|
||||
|
@ -1466,7 +1478,7 @@ static void check_watchpoint(int offset, int len_mask, int flags)
|
|||
/* We re-entered the check after replacing the TB. Now raise
|
||||
* the debug interrupt so that is will trigger after the
|
||||
* current instruction. */
|
||||
cpu_interrupt(env, CPU_INTERRUPT_DEBUG);
|
||||
cpu_interrupt(ENV_GET_CPU(env), CPU_INTERRUPT_DEBUG);
|
||||
return;
|
||||
}
|
||||
vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset;
|
||||
|
|
|
@ -1271,11 +1271,18 @@ float64 int64_to_float64( int64 a STATUS_PARAM )
|
|||
|
||||
}
|
||||
|
||||
float64 uint64_to_float64( uint64 a STATUS_PARAM )
|
||||
float64 uint64_to_float64(uint64 a STATUS_PARAM)
|
||||
{
|
||||
if ( a == 0 ) return float64_zero;
|
||||
return normalizeRoundAndPackFloat64( 0, 0x43C, a STATUS_VAR );
|
||||
int exp = 0x43C;
|
||||
|
||||
if (a == 0) {
|
||||
return float64_zero;
|
||||
}
|
||||
if ((int64_t)a < 0) {
|
||||
shift64RightJamming(a, 1, &a);
|
||||
exp += 1;
|
||||
}
|
||||
return normalizeRoundAndPackFloat64(0, exp, a STATUS_VAR);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
@ -1332,6 +1339,14 @@ float128 int64_to_float128( int64 a STATUS_PARAM )
|
|||
|
||||
}
|
||||
|
||||
float128 uint64_to_float128(uint64 a STATUS_PARAM)
|
||||
{
|
||||
if (a == 0) {
|
||||
return float128_zero;
|
||||
}
|
||||
return normalizeRoundAndPackFloat128(0, 0x406E, a, 0 STATUS_VAR);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the single-precision floating-point value
|
||||
| `a' to the 32-bit two's complement integer format. The conversion is
|
||||
|
@ -2219,7 +2234,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM)
|
|||
}
|
||||
}
|
||||
/* Zero plus something non-zero : just return the something */
|
||||
return make_float32(float32_val(c) ^ (signflip << 31));
|
||||
return packFloat32(cSign ^ signflip, cExp, cSig);
|
||||
}
|
||||
|
||||
if (aExp == 0) {
|
||||
|
@ -3772,7 +3787,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM)
|
|||
}
|
||||
}
|
||||
/* Zero plus something non-zero : just return the something */
|
||||
return make_float64(float64_val(c) ^ ((uint64_t)signflip << 63));
|
||||
return packFloat64(cSign ^ signflip, cExp, cSig);
|
||||
}
|
||||
|
||||
if (aExp == 0) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
ifeq ($(CONFIG_REALLY_VIRTFS),y)
|
||||
common-obj-y = qemu-fsdev.o virtio-9p-marshal.o
|
||||
|
||||
# Toplevel always builds this; targets without virtio will put it in
|
||||
# common-obj-y
|
||||
extra-obj-y = qemu-fsdev-dummy.o
|
||||
else
|
||||
common-obj-y = qemu-fsdev-dummy.o
|
||||
endif
|
||||
common-obj-y += qemu-fsdev-opts.o
|
||||
|
||||
# Toplevel always builds this; targets without virtio will put it in
|
||||
# common-obj-y
|
||||
common-obj-$(CONFIG_ALL) += qemu-fsdev-dummy.o
|
||||
|
|
|
@ -20,10 +20,3 @@ int qemu_fsdev_add(QemuOpts *opts)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsdev_register_config(void)
|
||||
{
|
||||
qemu_add_opts(&qemu_fsdev_opts);
|
||||
qemu_add_opts(&qemu_virtfs_opts);
|
||||
}
|
||||
machine_init(fsdev_register_config);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Virtio 9p
|
||||
*
|
||||
* 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/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
static QemuOptsList qemu_fsdev_opts = {
|
||||
.name = "fsdev",
|
||||
.implied_opt_name = "fsdriver",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_fsdev_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "fsdriver",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "path",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "security_model",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "writeout",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "readonly",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
|
||||
}, {
|
||||
.name = "socket",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "sock_fd",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
|
||||
{ /*End of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static QemuOptsList qemu_virtfs_opts = {
|
||||
.name = "virtfs",
|
||||
.implied_opt_name = "fsdriver",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_virtfs_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "fsdriver",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "path",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "mount_tag",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "security_model",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "writeout",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "readonly",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
}, {
|
||||
.name = "socket",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "sock_fd",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
|
||||
{ /*End of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static void fsdev_register_config(void)
|
||||
{
|
||||
qemu_add_opts(&qemu_fsdev_opts);
|
||||
qemu_add_opts(&qemu_virtfs_opts);
|
||||
}
|
||||
machine_init(fsdev_register_config);
|
|
@ -97,11 +97,3 @@ FsDriverEntry *get_fsdev_fsentry(char *id)
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fsdev_register_config(void)
|
||||
{
|
||||
qemu_add_opts(&qemu_fsdev_opts);
|
||||
qemu_add_opts(&qemu_virtfs_opts);
|
||||
}
|
||||
machine_init(fsdev_register_config);
|
||||
|
||||
|
|
|
@ -1039,7 +1039,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
switch (c) {
|
||||
case 'p':
|
||||
rpath = strdup(optarg);
|
||||
rpath = g_strdup(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
is_daemon = false;
|
||||
|
@ -1048,7 +1048,7 @@ int main(int argc, char **argv)
|
|||
sock = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
sock_name = strdup(optarg);
|
||||
sock_name = g_strdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
own_u = atoi(optarg);
|
||||
|
|
102
gdbstub.c
102
gdbstub.c
|
@ -40,6 +40,7 @@
|
|||
#include "cpu.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
#ifndef TARGET_CPU_MEMORY_RW_DEBUG
|
||||
static inline int target_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
||||
|
@ -1535,27 +1536,34 @@ static int cpu_gdb_write_register(CPUAlphaState *env, uint8_t *mem_buf, int n)
|
|||
}
|
||||
#elif defined (TARGET_S390X)
|
||||
|
||||
#define NUM_CORE_REGS S390_NUM_TOTAL_REGS
|
||||
#define NUM_CORE_REGS S390_NUM_REGS
|
||||
|
||||
static int cpu_gdb_read_register(CPUS390XState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
uint64_t val;
|
||||
int cc_op;
|
||||
|
||||
switch (n) {
|
||||
case S390_PSWM_REGNUM: GET_REGL(env->psw.mask); break;
|
||||
case S390_PSWA_REGNUM: GET_REGL(env->psw.addr); break;
|
||||
case S390_R0_REGNUM ... S390_R15_REGNUM:
|
||||
GET_REGL(env->regs[n-S390_R0_REGNUM]); break;
|
||||
case S390_A0_REGNUM ... S390_A15_REGNUM:
|
||||
GET_REG32(env->aregs[n-S390_A0_REGNUM]); break;
|
||||
case S390_FPC_REGNUM: GET_REG32(env->fpc); break;
|
||||
case S390_F0_REGNUM ... S390_F15_REGNUM:
|
||||
/* XXX */
|
||||
break;
|
||||
case S390_PC_REGNUM: GET_REGL(env->psw.addr); break;
|
||||
case S390_CC_REGNUM:
|
||||
env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst,
|
||||
env->cc_vr);
|
||||
GET_REG32(env->cc_op);
|
||||
break;
|
||||
case S390_PSWM_REGNUM:
|
||||
cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr);
|
||||
val = deposit64(env->psw.mask, 44, 2, cc_op);
|
||||
GET_REGL(val);
|
||||
break;
|
||||
case S390_PSWA_REGNUM:
|
||||
GET_REGL(env->psw.addr);
|
||||
break;
|
||||
case S390_R0_REGNUM ... S390_R15_REGNUM:
|
||||
GET_REGL(env->regs[n-S390_R0_REGNUM]);
|
||||
break;
|
||||
case S390_A0_REGNUM ... S390_A15_REGNUM:
|
||||
GET_REG32(env->aregs[n-S390_A0_REGNUM]);
|
||||
break;
|
||||
case S390_FPC_REGNUM:
|
||||
GET_REG32(env->fpc);
|
||||
break;
|
||||
case S390_F0_REGNUM ... S390_F15_REGNUM:
|
||||
GET_REG64(env->fregs[n-S390_F0_REGNUM].ll);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1570,20 +1578,30 @@ static int cpu_gdb_write_register(CPUS390XState *env, uint8_t *mem_buf, int n)
|
|||
tmp32 = ldl_p(mem_buf);
|
||||
|
||||
switch (n) {
|
||||
case S390_PSWM_REGNUM: env->psw.mask = tmpl; break;
|
||||
case S390_PSWA_REGNUM: env->psw.addr = tmpl; break;
|
||||
case S390_R0_REGNUM ... S390_R15_REGNUM:
|
||||
env->regs[n-S390_R0_REGNUM] = tmpl; break;
|
||||
case S390_A0_REGNUM ... S390_A15_REGNUM:
|
||||
env->aregs[n-S390_A0_REGNUM] = tmp32; r=4; break;
|
||||
case S390_FPC_REGNUM: env->fpc = tmp32; r=4; break;
|
||||
case S390_F0_REGNUM ... S390_F15_REGNUM:
|
||||
/* XXX */
|
||||
break;
|
||||
case S390_PC_REGNUM: env->psw.addr = tmpl; break;
|
||||
case S390_CC_REGNUM: env->cc_op = tmp32; r=4; break;
|
||||
case S390_PSWM_REGNUM:
|
||||
env->psw.mask = tmpl;
|
||||
env->cc_op = extract64(tmpl, 44, 2);
|
||||
break;
|
||||
case S390_PSWA_REGNUM:
|
||||
env->psw.addr = tmpl;
|
||||
break;
|
||||
case S390_R0_REGNUM ... S390_R15_REGNUM:
|
||||
env->regs[n-S390_R0_REGNUM] = tmpl;
|
||||
break;
|
||||
case S390_A0_REGNUM ... S390_A15_REGNUM:
|
||||
env->aregs[n-S390_A0_REGNUM] = tmp32;
|
||||
r = 4;
|
||||
break;
|
||||
case S390_FPC_REGNUM:
|
||||
env->fpc = tmp32;
|
||||
r = 4;
|
||||
break;
|
||||
case S390_F0_REGNUM ... S390_F15_REGNUM:
|
||||
env->fregs[n-S390_F0_REGNUM].ll = tmpl;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
#elif defined (TARGET_LM32)
|
||||
|
@ -2048,9 +2066,11 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
|
|||
static CPUArchState *find_cpu(uint32_t thread_id)
|
||||
{
|
||||
CPUArchState *env;
|
||||
CPUState *cpu;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (cpu_index(env) == thread_id) {
|
||||
cpu = ENV_GET_CPU(env);
|
||||
if (cpu_index(cpu) == thread_id) {
|
||||
return env;
|
||||
}
|
||||
}
|
||||
|
@ -2078,7 +2098,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
|||
case '?':
|
||||
/* TODO: Make this return the correct value for user-mode. */
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP,
|
||||
cpu_index(s->c_cpu));
|
||||
cpu_index(ENV_GET_CPU(s->c_cpu)));
|
||||
put_packet(s, buf);
|
||||
/* Remove all the breakpoints when this query is issued,
|
||||
* because gdb is doing and initial connect and the state
|
||||
|
@ -2373,7 +2393,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
|||
} else if (strcmp(p,"sThreadInfo") == 0) {
|
||||
report_cpuinfo:
|
||||
if (s->query_cpu) {
|
||||
snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu));
|
||||
snprintf(buf, sizeof(buf), "m%x",
|
||||
cpu_index(ENV_GET_CPU(s->query_cpu)));
|
||||
put_packet(s, buf);
|
||||
s->query_cpu = s->query_cpu->next_cpu;
|
||||
} else
|
||||
|
@ -2383,10 +2404,11 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
|||
thread = strtoull(p+16, (char **)&p, 16);
|
||||
env = find_cpu(thread);
|
||||
if (env != NULL) {
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
cpu_synchronize_state(env);
|
||||
len = snprintf((char *)mem_buf, sizeof(mem_buf),
|
||||
"CPU#%d [%s]", env->cpu_index,
|
||||
env->halted ? "halted " : "running");
|
||||
"CPU#%d [%s]", cpu->cpu_index,
|
||||
cpu->halted ? "halted " : "running");
|
||||
memtohex(buf, mem_buf, len);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
|
@ -2493,6 +2515,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
|||
{
|
||||
GDBState *s = gdbserver_state;
|
||||
CPUArchState *env = s->c_cpu;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
char buf[256];
|
||||
const char *type;
|
||||
int ret;
|
||||
|
@ -2521,7 +2544,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
|||
}
|
||||
snprintf(buf, sizeof(buf),
|
||||
"T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
|
||||
GDB_SIGNAL_TRAP, cpu_index(env), type,
|
||||
GDB_SIGNAL_TRAP, cpu_index(cpu), type,
|
||||
env->watchpoint_hit->vaddr);
|
||||
env->watchpoint_hit = NULL;
|
||||
goto send_packet;
|
||||
|
@ -2554,7 +2577,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
|||
ret = GDB_SIGNAL_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(env));
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu));
|
||||
|
||||
send_packet:
|
||||
put_packet(s, buf);
|
||||
|
@ -2818,7 +2841,7 @@ static void gdb_accept(void)
|
|||
GDBState *s;
|
||||
struct sockaddr_in sockaddr;
|
||||
socklen_t len;
|
||||
int val, fd;
|
||||
int fd;
|
||||
|
||||
for(;;) {
|
||||
len = sizeof(sockaddr);
|
||||
|
@ -2835,8 +2858,7 @@ static void gdb_accept(void)
|
|||
}
|
||||
|
||||
/* set short latency */
|
||||
val = 1;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||
socket_set_nodelay(fd);
|
||||
|
||||
s = g_malloc0(sizeof(GDBState));
|
||||
s->c_cpu = first_cpu;
|
||||
|
|
|
@ -295,14 +295,14 @@ ETEXI
|
|||
.name = "log",
|
||||
.args_type = "items:s",
|
||||
.params = "item1[,...]",
|
||||
.help = "activate logging of the specified items to '/tmp/qemu.log'",
|
||||
.help = "activate logging of the specified items",
|
||||
.mhandler.cmd = do_log,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item log @var{item1}[,...]
|
||||
@findex log
|
||||
Activate logging of the specified items to @file{/tmp/qemu.log}.
|
||||
Activate logging of the specified items.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
@ -837,6 +837,44 @@ STEXI
|
|||
@item nmi @var{cpu}
|
||||
@findex nmi
|
||||
Inject an NMI on the given CPU (x86 only).
|
||||
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "ringbuf_write",
|
||||
.args_type = "device:s,data:s",
|
||||
.params = "device data",
|
||||
.help = "Write to a ring buffer character device",
|
||||
.mhandler.cmd = hmp_ringbuf_write,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item ringbuf_write @var{device} @var{data}
|
||||
@findex ringbuf_write
|
||||
Write @var{data} to ring buffer character device @var{device}.
|
||||
@var{data} must be a UTF-8 string.
|
||||
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "ringbuf_read",
|
||||
.args_type = "device:s,size:i",
|
||||
.params = "device size",
|
||||
.help = "Read from a ring buffer character device",
|
||||
.mhandler.cmd = hmp_ringbuf_read,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item ringbuf_read @var{device}
|
||||
@findex ringbuf_read
|
||||
Read and print up to @var{size} bytes from ring buffer character
|
||||
device @var{device}.
|
||||
Certain non-printable characters are printed \uXXXX, where XXXX is the
|
||||
character code in hexadecimal. Character \ is printed \\.
|
||||
Bug: can screw up when the buffer contains invalid UTF-8 sequences,
|
||||
NUL characters, after the ring buffer lost data, and when reading
|
||||
stops because the size limit is reached.
|
||||
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
@ -1131,7 +1169,7 @@ ETEXI
|
|||
{
|
||||
.name = "netdev_add",
|
||||
.args_type = "netdev:O",
|
||||
.params = "[user|tap|socket],id=str[,prop=value][,...]",
|
||||
.params = "[user|tap|socket|hubport],id=str[,prop=value][,...]",
|
||||
.help = "add host network device",
|
||||
.mhandler.cmd = hmp_netdev_add,
|
||||
},
|
||||
|
@ -1482,6 +1520,38 @@ Password is invalidated at the given time. @var{nsec} are the seconds
|
|||
passed since 1970, i.e. unix epoch.
|
||||
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "chardev-add",
|
||||
.args_type = "args:s",
|
||||
.params = "args",
|
||||
.help = "add chardev",
|
||||
.mhandler.cmd = hmp_chardev_add,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item chardev_add args
|
||||
@findex chardev_add
|
||||
|
||||
chardev_add accepts the same parameters as the -chardev command line switch.
|
||||
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "chardev-remove",
|
||||
.args_type = "id:s",
|
||||
.params = "id",
|
||||
.help = "remove chardev",
|
||||
.mhandler.cmd = hmp_chardev_remove,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item chardev_remove id
|
||||
@findex chardev_remove
|
||||
|
||||
Removes the chardev @var{id}.
|
||||
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
@ -1489,7 +1559,8 @@ ETEXI
|
|||
.args_type = "item:s?",
|
||||
.params = "[subcommand]",
|
||||
.help = "show various information about the system state",
|
||||
.mhandler.cmd = do_info,
|
||||
.mhandler.cmd = do_info_help,
|
||||
.sub_table = info_cmds,
|
||||
},
|
||||
|
||||
STEXI
|
||||
|
@ -1570,6 +1641,8 @@ show device tree
|
|||
show qdev device model list
|
||||
@item info roms
|
||||
show roms
|
||||
@item info tpm
|
||||
show the TPM device
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
|
|
173
hmp.c
173
hmp.c
|
@ -31,7 +31,7 @@ static void hmp_handle_error(Monitor *mon, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
void hmp_info_name(Monitor *mon)
|
||||
void hmp_info_name(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
NameInfo *info;
|
||||
|
||||
|
@ -42,7 +42,7 @@ void hmp_info_name(Monitor *mon)
|
|||
qapi_free_NameInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_version(Monitor *mon)
|
||||
void hmp_info_version(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
VersionInfo *info;
|
||||
|
||||
|
@ -55,7 +55,7 @@ void hmp_info_version(Monitor *mon)
|
|||
qapi_free_VersionInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_kvm(Monitor *mon)
|
||||
void hmp_info_kvm(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
KvmInfo *info;
|
||||
|
||||
|
@ -70,7 +70,7 @@ void hmp_info_kvm(Monitor *mon)
|
|||
qapi_free_KvmInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_status(Monitor *mon)
|
||||
void hmp_info_status(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
StatusInfo *info;
|
||||
|
||||
|
@ -89,7 +89,7 @@ void hmp_info_status(Monitor *mon)
|
|||
qapi_free_StatusInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_uuid(Monitor *mon)
|
||||
void hmp_info_uuid(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
UuidInfo *info;
|
||||
|
||||
|
@ -98,7 +98,7 @@ void hmp_info_uuid(Monitor *mon)
|
|||
qapi_free_UuidInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_chardev(Monitor *mon)
|
||||
void hmp_info_chardev(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
ChardevInfoList *char_info, *info;
|
||||
|
||||
|
@ -111,7 +111,7 @@ void hmp_info_chardev(Monitor *mon)
|
|||
qapi_free_ChardevInfoList(char_info);
|
||||
}
|
||||
|
||||
void hmp_info_mice(Monitor *mon)
|
||||
void hmp_info_mice(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
MouseInfoList *mice_list, *mouse;
|
||||
|
||||
|
@ -131,7 +131,7 @@ void hmp_info_mice(Monitor *mon)
|
|||
qapi_free_MouseInfoList(mice_list);
|
||||
}
|
||||
|
||||
void hmp_info_migrate(Monitor *mon)
|
||||
void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
MigrationInfo *info;
|
||||
MigrationCapabilityStatusList *caps, *cap;
|
||||
|
@ -209,7 +209,7 @@ void hmp_info_migrate(Monitor *mon)
|
|||
qapi_free_MigrationCapabilityStatusList(caps);
|
||||
}
|
||||
|
||||
void hmp_info_migrate_capabilities(Monitor *mon)
|
||||
void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
MigrationCapabilityStatusList *caps, *cap;
|
||||
|
||||
|
@ -228,13 +228,13 @@ void hmp_info_migrate_capabilities(Monitor *mon)
|
|||
qapi_free_MigrationCapabilityStatusList(caps);
|
||||
}
|
||||
|
||||
void hmp_info_migrate_cache_size(Monitor *mon)
|
||||
void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
monitor_printf(mon, "xbzrel cache size: %" PRId64 " kbytes\n",
|
||||
qmp_query_migrate_cache_size(NULL) >> 10);
|
||||
}
|
||||
|
||||
void hmp_info_cpus(Monitor *mon)
|
||||
void hmp_info_cpus(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
CpuInfoList *cpu_list, *cpu;
|
||||
|
||||
|
@ -272,7 +272,7 @@ void hmp_info_cpus(Monitor *mon)
|
|||
qapi_free_CpuInfoList(cpu_list);
|
||||
}
|
||||
|
||||
void hmp_info_block(Monitor *mon)
|
||||
void hmp_info_block(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockInfoList *block_list, *info;
|
||||
|
||||
|
@ -326,7 +326,7 @@ void hmp_info_block(Monitor *mon)
|
|||
qapi_free_BlockInfoList(block_list);
|
||||
}
|
||||
|
||||
void hmp_info_blockstats(Monitor *mon)
|
||||
void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockStatsList *stats_list, *stats;
|
||||
|
||||
|
@ -360,7 +360,7 @@ void hmp_info_blockstats(Monitor *mon)
|
|||
qapi_free_BlockStatsList(stats_list);
|
||||
}
|
||||
|
||||
void hmp_info_vnc(Monitor *mon)
|
||||
void hmp_info_vnc(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
VncInfo *info;
|
||||
Error *err = NULL;
|
||||
|
@ -406,7 +406,7 @@ out:
|
|||
qapi_free_VncInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_spice(Monitor *mon)
|
||||
void hmp_info_spice(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
SpiceChannelList *chan;
|
||||
SpiceInfo *info;
|
||||
|
@ -453,7 +453,7 @@ out:
|
|||
qapi_free_SpiceInfo(info);
|
||||
}
|
||||
|
||||
void hmp_info_balloon(Monitor *mon)
|
||||
void hmp_info_balloon(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BalloonInfo *info;
|
||||
Error *err = NULL;
|
||||
|
@ -465,29 +465,7 @@ void hmp_info_balloon(Monitor *mon)
|
|||
return;
|
||||
}
|
||||
|
||||
monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20);
|
||||
if (info->has_mem_swapped_in) {
|
||||
monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in);
|
||||
}
|
||||
if (info->has_mem_swapped_out) {
|
||||
monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out);
|
||||
}
|
||||
if (info->has_major_page_faults) {
|
||||
monitor_printf(mon, " major_page_faults=%" PRId64,
|
||||
info->major_page_faults);
|
||||
}
|
||||
if (info->has_minor_page_faults) {
|
||||
monitor_printf(mon, " minor_page_faults=%" PRId64,
|
||||
info->minor_page_faults);
|
||||
}
|
||||
if (info->has_free_mem) {
|
||||
monitor_printf(mon, " free_mem=%" PRId64, info->free_mem);
|
||||
}
|
||||
if (info->has_total_mem) {
|
||||
monitor_printf(mon, " total_mem=%" PRId64, info->total_mem);
|
||||
}
|
||||
|
||||
monitor_printf(mon, "\n");
|
||||
monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20);
|
||||
|
||||
qapi_free_BalloonInfo(info);
|
||||
}
|
||||
|
@ -570,7 +548,7 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
|
|||
}
|
||||
}
|
||||
|
||||
void hmp_info_pci(Monitor *mon)
|
||||
void hmp_info_pci(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
PciInfoList *info_list, *info;
|
||||
Error *err = NULL;
|
||||
|
@ -593,7 +571,7 @@ void hmp_info_pci(Monitor *mon)
|
|||
qapi_free_PciInfoList(info_list);
|
||||
}
|
||||
|
||||
void hmp_info_block_jobs(Monitor *mon)
|
||||
void hmp_info_block_jobs(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockJobInfoList *list;
|
||||
Error *err = NULL;
|
||||
|
@ -629,6 +607,50 @@ void hmp_info_block_jobs(Monitor *mon)
|
|||
}
|
||||
}
|
||||
|
||||
void hmp_info_tpm(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
TPMInfoList *info_list, *info;
|
||||
Error *err = NULL;
|
||||
unsigned int c = 0;
|
||||
TPMPassthroughOptions *tpo;
|
||||
|
||||
info_list = qmp_query_tpm(&err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "TPM device not supported\n");
|
||||
error_free(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_list) {
|
||||
monitor_printf(mon, "TPM device:\n");
|
||||
}
|
||||
|
||||
for (info = info_list; info; info = info->next) {
|
||||
TPMInfo *ti = info->value;
|
||||
monitor_printf(mon, " tpm%d: model=%s\n",
|
||||
c, TpmModel_lookup[ti->model]);
|
||||
|
||||
monitor_printf(mon, " \\ %s: type=%s",
|
||||
ti->id, TpmType_lookup[ti->type]);
|
||||
|
||||
switch (ti->tpm_options->kind) {
|
||||
case TPM_TYPE_OPTIONS_KIND_TPM_PASSTHROUGH_OPTIONS:
|
||||
tpo = ti->tpm_options->tpm_passthrough_options;
|
||||
monitor_printf(mon, "%s%s%s%s",
|
||||
tpo->has_path ? ",path=" : "",
|
||||
tpo->has_path ? tpo->path : "",
|
||||
tpo->has_cancel_path ? ",cancel-path=" : "",
|
||||
tpo->has_cancel_path ? tpo->cancel_path : "");
|
||||
break;
|
||||
case TPM_TYPE_OPTIONS_KIND_MAX:
|
||||
break;
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
c++;
|
||||
}
|
||||
qapi_free_TPMInfoList(info_list);
|
||||
}
|
||||
|
||||
void hmp_quit(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
monitor_suspend(mon);
|
||||
|
@ -684,6 +706,48 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict)
|
|||
hmp_handle_error(mon, &errp);
|
||||
}
|
||||
|
||||
void hmp_ringbuf_write(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *chardev = qdict_get_str(qdict, "device");
|
||||
const char *data = qdict_get_str(qdict, "data");
|
||||
Error *errp = NULL;
|
||||
|
||||
qmp_ringbuf_write(chardev, data, false, 0, &errp);
|
||||
|
||||
hmp_handle_error(mon, &errp);
|
||||
}
|
||||
|
||||
void hmp_ringbuf_read(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
uint32_t size = qdict_get_int(qdict, "size");
|
||||
const char *chardev = qdict_get_str(qdict, "device");
|
||||
char *data;
|
||||
Error *errp = NULL;
|
||||
int i;
|
||||
|
||||
data = qmp_ringbuf_read(chardev, size, false, 0, &errp);
|
||||
if (errp) {
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(errp));
|
||||
error_free(errp);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; data[i]; i++) {
|
||||
unsigned char ch = data[i];
|
||||
|
||||
if (ch == '\\') {
|
||||
monitor_printf(mon, "\\\\");
|
||||
} else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) {
|
||||
monitor_printf(mon, "\\u%04X", ch);
|
||||
} else {
|
||||
monitor_printf(mon, "%c", ch);
|
||||
}
|
||||
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void hmp_cont_cb(void *opaque, int err)
|
||||
{
|
||||
if (!err) {
|
||||
|
@ -796,7 +860,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
|
|||
|
||||
qmp_drive_mirror(device, filename, !!format, format,
|
||||
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
|
||||
true, mode, false, 0,
|
||||
true, mode, false, 0, false, 0, false, 0,
|
||||
false, 0, false, 0, &errp);
|
||||
hmp_handle_error(mon, &errp);
|
||||
}
|
||||
|
@ -880,7 +944,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
|
|||
qapi_free_MigrationCapabilityStatusList(caps);
|
||||
|
||||
if (err) {
|
||||
monitor_printf(mon, "migrate_set_parameter: %s\n",
|
||||
monitor_printf(mon, "migrate_set_capability: %s\n",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
|
@ -1336,3 +1400,26 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
|
|||
qmp_nbd_server_stop(&errp);
|
||||
hmp_handle_error(mon, &errp);
|
||||
}
|
||||
|
||||
void hmp_chardev_add(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *args = qdict_get_str(qdict, "args");
|
||||
Error *err = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
opts = qemu_opts_parse(qemu_find_opts("chardev"), args, 1);
|
||||
if (opts == NULL) {
|
||||
error_setg(&err, "Parsing chardev args failed");
|
||||
} else {
|
||||
qemu_chr_new_from_opts(opts, NULL, &err);
|
||||
}
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
|
||||
hmp_handle_error(mon, &local_err);
|
||||
}
|
||||
|
|
41
hmp.h
41
hmp.h
|
@ -18,24 +18,25 @@
|
|||
#include "qapi-types.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
||||
void hmp_info_name(Monitor *mon);
|
||||
void hmp_info_version(Monitor *mon);
|
||||
void hmp_info_kvm(Monitor *mon);
|
||||
void hmp_info_status(Monitor *mon);
|
||||
void hmp_info_uuid(Monitor *mon);
|
||||
void hmp_info_chardev(Monitor *mon);
|
||||
void hmp_info_mice(Monitor *mon);
|
||||
void hmp_info_migrate(Monitor *mon);
|
||||
void hmp_info_migrate_capabilities(Monitor *mon);
|
||||
void hmp_info_migrate_cache_size(Monitor *mon);
|
||||
void hmp_info_cpus(Monitor *mon);
|
||||
void hmp_info_block(Monitor *mon);
|
||||
void hmp_info_blockstats(Monitor *mon);
|
||||
void hmp_info_vnc(Monitor *mon);
|
||||
void hmp_info_spice(Monitor *mon);
|
||||
void hmp_info_balloon(Monitor *mon);
|
||||
void hmp_info_pci(Monitor *mon);
|
||||
void hmp_info_block_jobs(Monitor *mon);
|
||||
void hmp_info_name(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_version(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_kvm(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_status(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_uuid(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_chardev(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_mice(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_migrate(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_cpus(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_block(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_blockstats(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_vnc(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_spice(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_balloon(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_pci(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_block_jobs(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_tpm(Monitor *mon, const QDict *qdict);
|
||||
void hmp_quit(Monitor *mon, const QDict *qdict);
|
||||
void hmp_stop(Monitor *mon, const QDict *qdict);
|
||||
void hmp_system_reset(Monitor *mon, const QDict *qdict);
|
||||
|
@ -43,6 +44,8 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
|
|||
void hmp_cpu(Monitor *mon, const QDict *qdict);
|
||||
void hmp_memsave(Monitor *mon, const QDict *qdict);
|
||||
void hmp_pmemsave(Monitor *mon, const QDict *qdict);
|
||||
void hmp_ringbuf_write(Monitor *mon, const QDict *qdict);
|
||||
void hmp_ringbuf_read(Monitor *mon, const QDict *qdict);
|
||||
void hmp_cont(Monitor *mon, const QDict *qdict);
|
||||
void hmp_system_wakeup(Monitor *mon, const QDict *qdict);
|
||||
void hmp_inject_nmi(Monitor *mon, const QDict *qdict);
|
||||
|
@ -80,5 +83,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict);
|
|||
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
|
||||
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
|
||||
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
|
||||
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
|
||||
void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
#include "hw/virtio.h"
|
||||
#include "hw/pc.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "hw/virtio-pci.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "virtio-9p-device.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
|
||||
|
@ -85,11 +85,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
|
|||
}
|
||||
|
||||
s->ctx.export_flags = fse->export_flags;
|
||||
if (fse->path) {
|
||||
s->ctx.fs_root = g_strdup(fse->path);
|
||||
} else {
|
||||
s->ctx.fs_root = NULL;
|
||||
}
|
||||
s->ctx.fs_root = g_strdup(fse->path);
|
||||
s->ctx.exops.get_st_gen = NULL;
|
||||
len = strlen(conf->tag);
|
||||
if (len > MAX_TAG_LEN - 1) {
|
||||
|
@ -98,7 +94,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
s->tag = strdup(conf->tag);
|
||||
s->tag = g_strdup(conf->tag);
|
||||
s->ctx.uid = -1;
|
||||
|
||||
s->ops = fse->ops;
|
||||
|
@ -140,54 +136,3 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
|
|||
|
||||
return &s->vdev;
|
||||
}
|
||||
|
||||
static int virtio_9p_init_pci(PCIDevice *pci_dev)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
|
||||
VirtIODevice *vdev;
|
||||
|
||||
vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf);
|
||||
vdev->nvectors = proxy->nvectors;
|
||||
virtio_init_pci(proxy, vdev);
|
||||
/* make the actual value visible */
|
||||
proxy->nvectors = vdev->nvectors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Property virtio_9p_properties[] = {
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
|
||||
DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag),
|
||||
DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_9p_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = virtio_9p_init_pci;
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
|
||||
k->device_id = 0x1009;
|
||||
k->revision = VIRTIO_PCI_ABI_VERSION;
|
||||
k->class_id = 0x2;
|
||||
dc->props = virtio_9p_properties;
|
||||
dc->reset = virtio_pci_reset;
|
||||
}
|
||||
|
||||
static TypeInfo virtio_9p_info = {
|
||||
.name = "virtio-9p-pci",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(VirtIOPCIProxy),
|
||||
.class_init = virtio_9p_class_init,
|
||||
};
|
||||
|
||||
static void virtio_9p_register_types(void)
|
||||
{
|
||||
type_register_static(&virtio_9p_info);
|
||||
virtio_9p_set_fd_limit();
|
||||
}
|
||||
|
||||
type_init(virtio_9p_register_types)
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_9P_H
|
||||
#define QEMU_9P_H
|
||||
#ifndef QEMU_VIRTIO_9P_DEVICE_H
|
||||
#define QEMU_VIRTIO_9P_DEVICE_H
|
||||
|
||||
typedef struct V9fsConf
|
||||
{
|
|
@ -46,7 +46,7 @@ static const char *local_mapped_attr_path(FsContext *ctx,
|
|||
const char *path, char *buffer)
|
||||
{
|
||||
char *dir_name;
|
||||
char *tmp_path = strdup(path);
|
||||
char *tmp_path = g_strdup(path);
|
||||
char *base_name = basename(tmp_path);
|
||||
|
||||
/* NULL terminate the directory */
|
||||
|
@ -55,7 +55,7 @@ static const char *local_mapped_attr_path(FsContext *ctx,
|
|||
|
||||
snprintf(buffer, PATH_MAX, "%s/%s/%s/%s",
|
||||
ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name);
|
||||
free(tmp_path);
|
||||
g_free(tmp_path);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
|
|||
{
|
||||
int err;
|
||||
char attr_dir[PATH_MAX];
|
||||
char *tmp_path = strdup(path);
|
||||
char *tmp_path = g_strdup(path);
|
||||
|
||||
snprintf(attr_dir, PATH_MAX, "%s/%s/%s",
|
||||
ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
|
||||
|
@ -139,7 +139,7 @@ static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
|
|||
if (err < 0 && errno == EEXIST) {
|
||||
err = 0;
|
||||
}
|
||||
free(tmp_path);
|
||||
g_free(tmp_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue