mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote branch 'origin/master' into pci
Conflicts: Makefile.objs hw/virtio.c
This commit is contained in:
@@ -4,6 +4,8 @@ config-host.*
|
||||
config-target.*
|
||||
trace.h
|
||||
trace.c
|
||||
trace-dtrace.h
|
||||
trace-dtrace.dtrace
|
||||
*-timestamp
|
||||
*-softmmu
|
||||
*-darwin-user
|
||||
|
||||
+415
-75
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
# Makefile for QEMU.
|
||||
|
||||
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
GENERATED_HEADERS += trace-dtrace.h
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
# Put the all: rule here so that config-host.mak can contain dependencies.
|
||||
@@ -36,18 +39,19 @@ endif
|
||||
|
||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory)
|
||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
|
||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS))
|
||||
|
||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
|
||||
$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@")
|
||||
|
||||
-include $(SUBDIR_DEVICES_MAK_DEP)
|
||||
|
||||
%/config-devices.mak: default-configs/%.mak
|
||||
$(call quiet-command,cat $< > $@.tmp, " GEN $@")
|
||||
$(call quiet-command,$(SHELL) $(SRC_PATH)/make_device_config.sh $@ $<, " GEN $@")
|
||||
@if test -f $@; then \
|
||||
if cmp -s $@.old $@; then \
|
||||
if ! cmp -s $@ $@.tmp; then \
|
||||
mv $@.tmp $@; \
|
||||
cp -p $@ $@.old; \
|
||||
fi; \
|
||||
mv $@.tmp $@; \
|
||||
cp -p $@ $@.old; \
|
||||
else \
|
||||
if test -f $@.old; then \
|
||||
echo "WARNING: $@ (user modified) out of date.";\
|
||||
@@ -108,7 +112,11 @@ ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
|
||||
|
||||
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
|
||||
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
trace.h: trace.h-timestamp trace-dtrace.h
|
||||
else
|
||||
trace.h: trace.h-timestamp
|
||||
endif
|
||||
trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak
|
||||
$(call quiet-command,sh $(SRC_PATH)/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h")
|
||||
@cmp -s $@ trace.h || cp $@ trace.h
|
||||
@@ -120,6 +128,20 @@ trace.c-timestamp: $(SRC_PATH)/trace-events config-host.mak
|
||||
|
||||
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 dependancy
|
||||
# rule file. So we use '.dtrace' instead
|
||||
trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp
|
||||
trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events config-host.mak
|
||||
$(call quiet-command,sh $(SRC_PATH)/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace")
|
||||
@cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace
|
||||
|
||||
trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
|
||||
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
|
||||
|
||||
simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
|
||||
|
||||
version.o: $(SRC_PATH)/version.rc config-host.mak
|
||||
@@ -129,7 +151,7 @@ version-obj-$(CONFIG_WIN32) += version.o
|
||||
######################################################################
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o: $(GENERATED_HEADERS)
|
||||
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
|
||||
|
||||
@@ -142,12 +164,14 @@ qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
|
||||
check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS)
|
||||
|
||||
check-qint: check-qint.o qint.o qemu-malloc.o $(trace-obj-y)
|
||||
check-qstring: check-qstring.o qstring.o qemu-malloc.o $(trace-obj-y)
|
||||
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o $(trace-obj-y)
|
||||
check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o $(trace-obj-y)
|
||||
check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o $(trace-obj-y)
|
||||
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o $(trace-obj-y)
|
||||
CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y)
|
||||
|
||||
check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS)
|
||||
check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS)
|
||||
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS)
|
||||
check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
|
||||
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
|
||||
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS)
|
||||
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
@@ -157,6 +181,8 @@ clean:
|
||||
rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
|
||||
rm -f qemu-img-cmds.h
|
||||
rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
|
||||
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
|
||||
rm -f trace-dtrace.h trace-dtrace.h-timestamp
|
||||
$(MAKE) -C tests clean
|
||||
for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
|
||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||
@@ -178,8 +204,9 @@ ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
|
||||
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
|
||||
openbios-sparc32 openbios-sparc64 openbios-ppc \
|
||||
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
|
||||
gpxe-eepro100-80861209.rom \
|
||||
pxe-e1000.bin \
|
||||
pxe-ne2k_pci.bin pxe-pcnet.bin \
|
||||
|
||||
+27
-11
@@ -42,6 +42,11 @@ net-nested-$(CONFIG_SLIRP) += slirp.o
|
||||
net-nested-$(CONFIG_VDE) += vde.o
|
||||
net-obj-y += $(addprefix net/, $(net-nested-y))
|
||||
|
||||
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS),yy)
|
||||
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
||||
# only pull in the actual virtio-9p device if we also enabled virtio.
|
||||
CONFIG_REALLY_VIRTFS=y
|
||||
endif
|
||||
fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o
|
||||
fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
|
||||
|
||||
@@ -102,6 +107,7 @@ common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.
|
||||
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
audio-obj-$(CONFIG_SDL) += sdlaudio.o
|
||||
audio-obj-$(CONFIG_OSS) += ossaudio.o
|
||||
audio-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
|
||||
audio-obj-$(CONFIG_ALSA) += alsaaudio.o
|
||||
audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
|
||||
@@ -158,9 +164,13 @@ user-obj-y += cutils.o cache-utils.o
|
||||
|
||||
hw-obj-y =
|
||||
hw-obj-y += vl.o loader.o
|
||||
hw-obj-y += virtio.o virtio-console.o
|
||||
hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o
|
||||
hw-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o
|
||||
hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
|
||||
hw-obj-y += fw_cfg.o
|
||||
# FIXME: Core PCI code and its direct dependencies are required by the
|
||||
# QMP query-pci command.
|
||||
hw-obj-y += pci.o pci_bridge.o msix.o msi.o
|
||||
hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
|
||||
hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
|
||||
hw-obj-y += watchdog.o
|
||||
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
|
||||
hw-obj-$(CONFIG_ECC) += ecc.o
|
||||
@@ -205,15 +215,16 @@ hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
|
||||
hw-obj-$(CONFIG_PIIX4) += piix4.o
|
||||
|
||||
# PCI watchdog devices
|
||||
hw-obj-y += wdt_i6300esb.o
|
||||
hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
|
||||
|
||||
hw-obj-y += pcie.o pcie_aer.o pcie_port.o
|
||||
hw-obj-y += msix.o msi.o
|
||||
hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
|
||||
hw-obj-$(CONFIG_PCI) += msix.o msi.o
|
||||
|
||||
# PCI network cards
|
||||
hw-obj-y += ne2000.o
|
||||
hw-obj-y += eepro100.o
|
||||
hw-obj-y += pcnet.o
|
||||
hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
|
||||
hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
|
||||
hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
|
||||
hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
|
||||
|
||||
hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
|
||||
hw-obj-$(CONFIG_LAN9118) += lan9118.o
|
||||
@@ -230,7 +241,7 @@ hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o
|
||||
hw-obj-$(CONFIG_IDE_VIA) += ide/via.o
|
||||
|
||||
# SCSI layer
|
||||
hw-obj-y += lsi53c895a.o
|
||||
hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
|
||||
hw-obj-$(CONFIG_ESP) += esp.o
|
||||
|
||||
hw-obj-y += dma-helpers.o sysbus.o isa-bus.o
|
||||
@@ -260,7 +271,8 @@ sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
|
||||
adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
|
||||
hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
|
||||
|
||||
hw-obj-$(CONFIG_VIRTFS) += virtio-9p-debug.o virtio-9p-local.o virtio-9p-xattr.o
|
||||
hw-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p-debug.o
|
||||
hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o
|
||||
hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
|
||||
|
||||
######################################################################
|
||||
@@ -285,11 +297,15 @@ libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
|
||||
######################################################################
|
||||
# trace
|
||||
|
||||
ifeq ($(TRACE_BACKEND),dtrace)
|
||||
trace-obj-y = trace-dtrace.o
|
||||
else
|
||||
trace-obj-y = trace.o
|
||||
ifeq ($(TRACE_BACKEND),simple)
|
||||
trace-obj-y += simpletrace.o
|
||||
user-obj-y += qemu-timer-common.o
|
||||
endif
|
||||
endif
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
|
||||
+33
-5
@@ -30,6 +30,7 @@ endif
|
||||
endif
|
||||
|
||||
PROGS=$(QEMU_PROG)
|
||||
STPFILES=
|
||||
|
||||
ifndef CONFIG_HAIKU
|
||||
LIBS+=-lm
|
||||
@@ -40,7 +41,27 @@ kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS)
|
||||
config-target.h: config-target.h-timestamp
|
||||
config-target.h-timestamp: config-target.mak
|
||||
|
||||
all: $(PROGS)
|
||||
ifdef CONFIG_SYSTEMTAP_TRACE
|
||||
stap: $(QEMU_PROG).stp
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
TARGET_TYPE=user
|
||||
else
|
||||
TARGET_TYPE=system
|
||||
endif
|
||||
|
||||
$(QEMU_PROG).stp:
|
||||
$(call quiet-command,sh $(SRC_PATH)/tracetool \
|
||||
--$(TRACE_BACKEND) \
|
||||
--binary $(bindir)/$(QEMU_PROG) \
|
||||
--target-arch $(TARGET_ARCH) \
|
||||
--target-type $(TARGET_TYPE) \
|
||||
--stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp")
|
||||
else
|
||||
stap:
|
||||
endif
|
||||
|
||||
all: $(PROGS) stap
|
||||
|
||||
# Dummy command so that make thinks it has done something
|
||||
@true
|
||||
@@ -167,11 +188,11 @@ ifdef CONFIG_SOFTMMU
|
||||
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
|
||||
# virtio has to be here due to weird dependency between PCI and virtio-net.
|
||||
# need to fix this properly
|
||||
obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
|
||||
obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
|
||||
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
obj-y += vhost_net.o
|
||||
obj-$(CONFIG_VHOST_NET) += vhost.o
|
||||
obj-$(CONFIG_VIRTFS) += virtio-9p.o
|
||||
obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o
|
||||
obj-y += rwhandler.o
|
||||
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
|
||||
obj-$(CONFIG_NO_KVM) += kvm-stub.o
|
||||
@@ -189,8 +210,8 @@ obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
|
||||
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
|
||||
|
||||
# PCI network cards
|
||||
obj-y += rtl8139.o
|
||||
obj-y += e1000.o
|
||||
obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
|
||||
obj-$(CONFIG_E1000_PCI) += e1000.o
|
||||
|
||||
# Inter-VM PCI shared memory
|
||||
obj-$(CONFIG_KVM) += ivshmem.o
|
||||
@@ -340,6 +361,9 @@ clean:
|
||||
rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
|
||||
rm -f *.d */*.d tcg/*.o ide/*.o
|
||||
rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c
|
||||
ifdef CONFIG_SYSTEMTAP_TRACE
|
||||
rm -f *.stp
|
||||
endif
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
@@ -348,6 +372,10 @@ ifneq ($(STRIP),)
|
||||
$(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS))
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_SYSTEMTAP_TRACE
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset"
|
||||
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset"
|
||||
endif
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
|
||||
+1
-4
@@ -19,10 +19,7 @@ o qmp-spec.txt QEMU Monitor Protocol current specification
|
||||
o qmp-commands.txt QMP supported commands (auto-generated at build-time)
|
||||
o qmp-events.txt List of available asynchronous events
|
||||
|
||||
There are also two simple Python scripts available:
|
||||
|
||||
o qmp-shell A shell
|
||||
o vm-info Show some information about the Virtual Machine
|
||||
There is also a simple Python script called 'qmp-shell' available.
|
||||
|
||||
IMPORTANT: It's strongly recommended to read the 'Stability Considerations'
|
||||
section in the qmp-commands.txt file before making any serious use of QMP.
|
||||
|
||||
+220
-34
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Simple QEMU shell on top of QMP
|
||||
# Low-level QEMU shell on top of QMP.
|
||||
#
|
||||
# Copyright (C) 2009 Red Hat Inc.
|
||||
# Copyright (C) 2009, 2010 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Luiz Capitulino <lcapitulino@redhat.com>
|
||||
@@ -14,60 +14,246 @@
|
||||
#
|
||||
# Start QEMU with:
|
||||
#
|
||||
# $ qemu [...] -monitor control,unix:./qmp,server
|
||||
# # qemu [...] -qmp unix:./qmp-sock,server
|
||||
#
|
||||
# Run the shell:
|
||||
#
|
||||
# $ qmp-shell ./qmp
|
||||
# $ qmp-shell ./qmp-sock
|
||||
#
|
||||
# Commands have the following format:
|
||||
#
|
||||
# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
|
||||
# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# (QEMU) info item=network
|
||||
# (QEMU) device_add driver=e1000 id=net1
|
||||
# {u'return': {}}
|
||||
# (QEMU)
|
||||
|
||||
import qmp
|
||||
import readline
|
||||
from sys import argv,exit
|
||||
import sys
|
||||
|
||||
def shell_help():
|
||||
print 'bye exit from the shell'
|
||||
class QMPCompleter(list):
|
||||
def complete(self, text, state):
|
||||
for cmd in self:
|
||||
if cmd.startswith(text):
|
||||
if not state:
|
||||
return cmd
|
||||
else:
|
||||
state -= 1
|
||||
|
||||
def main():
|
||||
if len(argv) != 2:
|
||||
print 'qemu-shell <unix-socket>'
|
||||
exit(1)
|
||||
class QMPShellError(Exception):
|
||||
pass
|
||||
|
||||
qemu = qmp.QEMUMonitorProtocol(argv[1])
|
||||
qemu.connect()
|
||||
qemu.send("qmp_capabilities")
|
||||
class QMPShellBadPort(QMPShellError):
|
||||
pass
|
||||
|
||||
print 'Connected!'
|
||||
# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
|
||||
# _execute_cmd()). Let's design a better one.
|
||||
class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
def __init__(self, address):
|
||||
qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
|
||||
self._greeting = None
|
||||
self._completer = None
|
||||
|
||||
while True:
|
||||
def __get_address(self, arg):
|
||||
"""
|
||||
Figure out if the argument is in the port:host form, if it's not it's
|
||||
probably a file path.
|
||||
"""
|
||||
addr = arg.split(':')
|
||||
if len(addr) == 2:
|
||||
try:
|
||||
port = int(addr[1])
|
||||
except ValueError:
|
||||
raise QMPShellBadPort
|
||||
return ( addr[0], port )
|
||||
# socket path
|
||||
return arg
|
||||
|
||||
def _fill_completion(self):
|
||||
for cmd in self.cmd('query-commands')['return']:
|
||||
self._completer.append(cmd['name'])
|
||||
|
||||
def __completer_setup(self):
|
||||
self._completer = QMPCompleter()
|
||||
self._fill_completion()
|
||||
readline.set_completer(self._completer.complete)
|
||||
readline.parse_and_bind("tab: complete")
|
||||
# XXX: default delimiters conflict with some command names (eg. query-),
|
||||
# clearing everything as it doesn't seem to matter
|
||||
readline.set_completer_delims('')
|
||||
|
||||
def __build_cmd(self, cmdline):
|
||||
"""
|
||||
Build a QMP input object from a user provided command-line in the
|
||||
following format:
|
||||
|
||||
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
|
||||
"""
|
||||
cmdargs = cmdline.split()
|
||||
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
|
||||
for arg in cmdargs[1:]:
|
||||
opt = arg.split('=')
|
||||
try:
|
||||
value = int(opt[1])
|
||||
except ValueError:
|
||||
value = opt[1]
|
||||
qmpcmd['arguments'][opt[0]] = value
|
||||
return qmpcmd
|
||||
|
||||
def _execute_cmd(self, cmdline):
|
||||
try:
|
||||
cmd = raw_input('(QEMU) ')
|
||||
qmpcmd = self.__build_cmd(cmdline)
|
||||
except:
|
||||
print 'command format: <command-name> ',
|
||||
print '[arg-name1=arg1] ... [arg-nameN=argN]'
|
||||
return True
|
||||
resp = self.cmd_obj(qmpcmd)
|
||||
if resp is None:
|
||||
print 'Disconnected'
|
||||
return False
|
||||
print resp
|
||||
return True
|
||||
|
||||
def connect(self):
|
||||
self._greeting = qmp.QEMUMonitorProtocol.connect(self)
|
||||
self.__completer_setup()
|
||||
|
||||
def show_banner(self, msg='Welcome to the QMP low-level shell!'):
|
||||
print msg
|
||||
version = self._greeting['QMP']['version']['qemu']
|
||||
print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
|
||||
|
||||
def read_exec_command(self, prompt):
|
||||
"""
|
||||
Read and execute a command.
|
||||
|
||||
@return True if execution was ok, return False if disconnected.
|
||||
"""
|
||||
try:
|
||||
cmdline = raw_input(prompt)
|
||||
except EOFError:
|
||||
print
|
||||
break
|
||||
if cmd == '':
|
||||
continue
|
||||
elif cmd == 'bye':
|
||||
break
|
||||
elif cmd == 'help':
|
||||
shell_help()
|
||||
return False
|
||||
if cmdline == '':
|
||||
for ev in self.get_events():
|
||||
print ev
|
||||
self.clear_events()
|
||||
return True
|
||||
else:
|
||||
return self._execute_cmd(cmdline)
|
||||
|
||||
class HMPShell(QMPShell):
|
||||
def __init__(self, address):
|
||||
QMPShell.__init__(self, address)
|
||||
self.__cpu_index = 0
|
||||
|
||||
def __cmd_completion(self):
|
||||
for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
|
||||
if cmd and cmd[0] != '[' and cmd[0] != '\t':
|
||||
name = cmd.split()[0] # drop help text
|
||||
if name == 'info':
|
||||
continue
|
||||
if name.find('|') != -1:
|
||||
# Command in the form 'foobar|f' or 'f|foobar', take the
|
||||
# full name
|
||||
opt = name.split('|')
|
||||
if len(opt[0]) == 1:
|
||||
name = opt[1]
|
||||
else:
|
||||
name = opt[0]
|
||||
self._completer.append(name)
|
||||
self._completer.append('help ' + name) # help completion
|
||||
|
||||
def __info_completion(self):
|
||||
for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
|
||||
if cmd:
|
||||
self._completer.append('info ' + cmd.split()[1])
|
||||
|
||||
def __other_completion(self):
|
||||
# special cases
|
||||
self._completer.append('help info')
|
||||
|
||||
def _fill_completion(self):
|
||||
self.__cmd_completion()
|
||||
self.__info_completion()
|
||||
self.__other_completion()
|
||||
|
||||
def __cmd_passthrough(self, cmdline, cpu_index = 0):
|
||||
return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
|
||||
{ 'command-line': cmdline,
|
||||
'cpu-index': cpu_index } })
|
||||
|
||||
def _execute_cmd(self, cmdline):
|
||||
if cmdline.split()[0] == "cpu":
|
||||
# trap the cpu command, it requires special setting
|
||||
try:
|
||||
resp = qemu.send(cmd)
|
||||
if resp == None:
|
||||
print 'Disconnected'
|
||||
break
|
||||
print resp
|
||||
except IndexError:
|
||||
print '-> command format: <command-name> ',
|
||||
print '[arg-name1=arg1] ... [arg-nameN=argN]'
|
||||
idx = int(cmdline.split()[1])
|
||||
if not 'return' in self.__cmd_passthrough('info version', idx):
|
||||
print 'bad CPU index'
|
||||
return True
|
||||
self.__cpu_index = idx
|
||||
except ValueError:
|
||||
print 'cpu command takes an integer argument'
|
||||
return True
|
||||
resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
|
||||
if resp is None:
|
||||
print 'Disconnected'
|
||||
return False
|
||||
assert 'return' in resp or 'error' in resp
|
||||
if 'return' in resp:
|
||||
# Success
|
||||
if len(resp['return']) > 0:
|
||||
print resp['return'],
|
||||
else:
|
||||
# Error
|
||||
print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
|
||||
return True
|
||||
|
||||
def show_banner(self):
|
||||
QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
|
||||
|
||||
def die(msg):
|
||||
sys.stderr.write('ERROR: %s\n' % msg)
|
||||
sys.exit(1)
|
||||
|
||||
def fail_cmdline(option=None):
|
||||
if option:
|
||||
sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
|
||||
sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
addr = ''
|
||||
try:
|
||||
if len(sys.argv) == 2:
|
||||
qemu = QMPShell(sys.argv[1])
|
||||
addr = sys.argv[1]
|
||||
elif len(sys.argv) == 3:
|
||||
if sys.argv[1] != '-H':
|
||||
fail_cmdline(sys.argv[1])
|
||||
qemu = HMPShell(sys.argv[2])
|
||||
addr = sys.argv[2]
|
||||
else:
|
||||
fail_cmdline()
|
||||
except QMPShellBadPort:
|
||||
die('bad port number in command-line')
|
||||
|
||||
try:
|
||||
qemu.connect()
|
||||
except qmp.QMPConnectError:
|
||||
die('Didn\'t get QMP greeting message')
|
||||
except qmp.QMPCapabilitiesError:
|
||||
die('Could not negotiate capabilities')
|
||||
except qemu.error:
|
||||
die('Could not connect to %s' % addr)
|
||||
|
||||
qemu.show_banner()
|
||||
while qemu.read_exec_command('(QEMU) '):
|
||||
pass
|
||||
qemu.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
+108
-53
@@ -1,6 +1,6 @@
|
||||
# QEMU Monitor Protocol Python class
|
||||
#
|
||||
# Copyright (C) 2009 Red Hat Inc.
|
||||
# Copyright (C) 2009, 2010 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Luiz Capitulino <lcapitulino@redhat.com>
|
||||
@@ -8,7 +8,9 @@
|
||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
# the COPYING file in the top-level directory.
|
||||
|
||||
import socket, json
|
||||
import json
|
||||
import errno
|
||||
import socket
|
||||
|
||||
class QMPError(Exception):
|
||||
pass
|
||||
@@ -16,61 +18,114 @@ class QMPError(Exception):
|
||||
class QMPConnectError(QMPError):
|
||||
pass
|
||||
|
||||
class QMPCapabilitiesError(QMPError):
|
||||
pass
|
||||
|
||||
class QEMUMonitorProtocol:
|
||||
def connect(self):
|
||||
self.sock.connect(self.filename)
|
||||
data = self.__json_read()
|
||||
if data == None:
|
||||
raise QMPConnectError
|
||||
if not data.has_key('QMP'):
|
||||
raise QMPConnectError
|
||||
return data['QMP']['capabilities']
|
||||
def __init__(self, address):
|
||||
"""
|
||||
Create a QEMUMonitorProtocol class.
|
||||
|
||||
def close(self):
|
||||
self.sock.close()
|
||||
@param address: QEMU address, can be either a unix socket path (string)
|
||||
or a tuple in the form ( address, port ) for a TCP
|
||||
connection
|
||||
@note No connection is established, this is done by the connect() method
|
||||
"""
|
||||
self.__events = []
|
||||
self.__address = address
|
||||
self.__sock = self.__get_sock()
|
||||
self.__sockfile = self.__sock.makefile()
|
||||
|
||||
def send_raw(self, line):
|
||||
self.sock.send(str(line))
|
||||
return self.__json_read()
|
||||
|
||||
def send(self, cmdline):
|
||||
cmd = self.__build_cmd(cmdline)
|
||||
self.__json_send(cmd)
|
||||
resp = self.__json_read()
|
||||
if resp == None:
|
||||
return
|
||||
elif resp.has_key('error'):
|
||||
return resp['error']
|
||||
def __get_sock(self):
|
||||
if isinstance(self.__address, tuple):
|
||||
family = socket.AF_INET
|
||||
else:
|
||||
return resp['return']
|
||||
|
||||
def __build_cmd(self, cmdline):
|
||||
cmdargs = cmdline.split()
|
||||
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
|
||||
for arg in cmdargs[1:]:
|
||||
opt = arg.split('=')
|
||||
try:
|
||||
value = int(opt[1])
|
||||
except ValueError:
|
||||
value = opt[1]
|
||||
qmpcmd['arguments'][opt[0]] = value
|
||||
return qmpcmd
|
||||
|
||||
def __json_send(self, cmd):
|
||||
# XXX: We have to send any additional char, otherwise
|
||||
# the Server won't read our input
|
||||
self.sock.send(json.dumps(cmd) + ' ')
|
||||
family = socket.AF_UNIX
|
||||
return socket.socket(family, socket.SOCK_STREAM)
|
||||
|
||||
def __json_read(self):
|
||||
try:
|
||||
while True:
|
||||
line = json.loads(self.sockfile.readline())
|
||||
if not 'event' in line:
|
||||
return line
|
||||
except ValueError:
|
||||
return
|
||||
while True:
|
||||
data = self.__sockfile.readline()
|
||||
if not data:
|
||||
return
|
||||
resp = json.loads(data)
|
||||
if 'event' in resp:
|
||||
self.__events.append(resp)
|
||||
continue
|
||||
return resp
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sockfile = self.sock.makefile()
|
||||
error = socket.error
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Connect to the QMP Monitor and perform capabilities negotiation.
|
||||
|
||||
@return QMP greeting dict
|
||||
@raise socket.error on socket connection errors
|
||||
@raise QMPConnectError if the greeting is not received
|
||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||
"""
|
||||
self.__sock.connect(self.__address)
|
||||
greeting = self.__json_read()
|
||||
if greeting is None or not greeting.has_key('QMP'):
|
||||
raise QMPConnectError
|
||||
# Greeting seems ok, negotiate capabilities
|
||||
resp = self.cmd('qmp_capabilities')
|
||||
if "return" in resp:
|
||||
return greeting
|
||||
raise QMPCapabilitiesError
|
||||
|
||||
def cmd_obj(self, qmp_cmd):
|
||||
"""
|
||||
Send a QMP command to the QMP Monitor.
|
||||
|
||||
@param qmp_cmd: QMP command to be sent as a Python dict
|
||||
@return QMP response as a Python dict or None if the connection has
|
||||
been closed
|
||||
"""
|
||||
try:
|
||||
self.__sock.sendall(json.dumps(qmp_cmd))
|
||||
except socket.error, err:
|
||||
if err[0] == errno.EPIPE:
|
||||
return
|
||||
raise socket.error(err)
|
||||
return self.__json_read()
|
||||
|
||||
def cmd(self, name, args=None, id=None):
|
||||
"""
|
||||
Build a QMP command and send it to the QMP Monitor.
|
||||
|
||||
@param name: command name (string)
|
||||
@param args: command arguments (dict)
|
||||
@param id: command id (dict, list, string or int)
|
||||
"""
|
||||
qmp_cmd = { 'execute': name }
|
||||
if args:
|
||||
qmp_cmd['arguments'] = args
|
||||
if id:
|
||||
qmp_cmd['id'] = id
|
||||
return self.cmd_obj(qmp_cmd)
|
||||
|
||||
def get_events(self):
|
||||
"""
|
||||
Get a list of available QMP events.
|
||||
"""
|
||||
self.__sock.setblocking(0)
|
||||
try:
|
||||
self.__json_read()
|
||||
except socket.error, err:
|
||||
if err[0] == errno.EAGAIN:
|
||||
# No data available
|
||||
pass
|
||||
self.__sock.setblocking(1)
|
||||
return self.__events
|
||||
|
||||
def clear_events(self):
|
||||
"""
|
||||
Clear current list of pending events.
|
||||
"""
|
||||
self.__events = []
|
||||
|
||||
def close(self):
|
||||
self.__sock.close()
|
||||
self.__sockfile.close()
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Print Virtual Machine information
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# Start QEMU with:
|
||||
#
|
||||
# $ qemu [...] -monitor control,unix:./qmp,server
|
||||
#
|
||||
# Run vm-info:
|
||||
#
|
||||
# $ vm-info ./qmp
|
||||
#
|
||||
# Luiz Capitulino <lcapitulino@redhat.com>
|
||||
|
||||
import qmp
|
||||
from sys import argv,exit
|
||||
|
||||
def main():
|
||||
if len(argv) != 2:
|
||||
print 'vm-info <unix-socket>'
|
||||
exit(1)
|
||||
|
||||
qemu = qmp.QEMUMonitorProtocol(argv[1])
|
||||
qemu.connect()
|
||||
qemu.send("qmp_capabilities")
|
||||
|
||||
for cmd in [ 'version', 'kvm', 'status', 'uuid', 'balloon' ]:
|
||||
print cmd + ': ' + str(qemu.send('query-' + cmd))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+13
-14
@@ -44,6 +44,9 @@
|
||||
that we generate the list.
|
||||
*/
|
||||
static struct audio_driver *drvtab[] = {
|
||||
#ifdef CONFIG_SPICE
|
||||
&spice_audio_driver,
|
||||
#endif
|
||||
CONFIG_AUDIO_DRIVERS
|
||||
&no_audio_driver,
|
||||
&wav_audio_driver
|
||||
@@ -1093,15 +1096,6 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
|
||||
/*
|
||||
* Timer
|
||||
*/
|
||||
static void audio_timer (void *opaque)
|
||||
{
|
||||
AudioState *s = opaque;
|
||||
|
||||
audio_run ("timer");
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
|
||||
}
|
||||
|
||||
|
||||
static int audio_is_timer_needed (void)
|
||||
{
|
||||
HWVoiceIn *hwi = NULL;
|
||||
@@ -1116,10 +1110,8 @@ static int audio_is_timer_needed (void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audio_reset_timer (void)
|
||||
static void audio_reset_timer (AudioState *s)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
|
||||
if (audio_is_timer_needed ()) {
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
|
||||
}
|
||||
@@ -1128,6 +1120,12 @@ static void audio_reset_timer (void)
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_timer (void *opaque)
|
||||
{
|
||||
audio_run ("timer");
|
||||
audio_reset_timer (opaque);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
@@ -1192,7 +1190,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
|
||||
audio_reset_timer ();
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1237,6 +1235,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
}
|
||||
sw->total_hw_samples_acquired = hw->total_samples_captured;
|
||||
@@ -1758,7 +1757,7 @@ static void audio_vm_change_state_handler (void *opaque, int running,
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
|
||||
hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
|
||||
}
|
||||
audio_reset_timer ();
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
|
||||
static void audio_atexit (void)
|
||||
|
||||
@@ -209,6 +209,7 @@ extern struct audio_driver coreaudio_audio_driver;
|
||||
extern struct audio_driver dsound_audio_driver;
|
||||
extern struct audio_driver esd_audio_driver;
|
||||
extern struct audio_driver pa_audio_driver;
|
||||
extern struct audio_driver spice_audio_driver;
|
||||
extern struct audio_driver winwave_audio_driver;
|
||||
extern struct mixeng_volume nominal_volume;
|
||||
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
*
|
||||
* maintained by Gerd Hoffmann <kraxel@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
|
||||
#define AUDIO_CAP "spice"
|
||||
#include "audio.h"
|
||||
#include "audio_int.h"
|
||||
|
||||
#define LINE_IN_SAMPLES 1024
|
||||
#define LINE_OUT_SAMPLES 1024
|
||||
|
||||
typedef struct SpiceRateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
} SpiceRateCtl;
|
||||
|
||||
typedef struct SpiceVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
SpicePlaybackInstance sin;
|
||||
SpiceRateCtl rate;
|
||||
int active;
|
||||
uint32_t *frame;
|
||||
uint32_t *fpos;
|
||||
uint32_t fsize;
|
||||
} SpiceVoiceOut;
|
||||
|
||||
typedef struct SpiceVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
SpiceRecordInstance sin;
|
||||
SpiceRateCtl rate;
|
||||
int active;
|
||||
uint32_t samples[LINE_IN_SAMPLES];
|
||||
} SpiceVoiceIn;
|
||||
|
||||
static const SpicePlaybackInterface playback_sif = {
|
||||
.base.type = SPICE_INTERFACE_PLAYBACK,
|
||||
.base.description = "playback",
|
||||
.base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
|
||||
.base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
|
||||
};
|
||||
|
||||
static const SpiceRecordInterface record_sif = {
|
||||
.base.type = SPICE_INTERFACE_RECORD,
|
||||
.base.description = "record",
|
||||
.base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
|
||||
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
||||
};
|
||||
|
||||
static void *spice_audio_init (void)
|
||||
{
|
||||
if (!using_spice) {
|
||||
return NULL;
|
||||
}
|
||||
return &spice_audio_init;
|
||||
}
|
||||
|
||||
static void spice_audio_fini (void *opaque)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void rate_start (SpiceRateCtl *rate)
|
||||
{
|
||||
memset (rate, 0, sizeof (*rate));
|
||||
rate->start_ticks = qemu_get_clock (vm_clock);
|
||||
}
|
||||
|
||||
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
|
||||
{
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
int64_t samples;
|
||||
|
||||
now = qemu_get_clock (vm_clock);
|
||||
ticks = now - rate->start_ticks;
|
||||
bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
|
||||
samples = (bytes - rate->bytes_sent) >> info->shift;
|
||||
if (samples < 0 || samples > 65536) {
|
||||
fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
|
||||
rate_start (rate);
|
||||
samples = 0;
|
||||
}
|
||||
rate->bytes_sent += samples << info->shift;
|
||||
return samples;
|
||||
}
|
||||
|
||||
/* playback */
|
||||
|
||||
static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
struct audsettings settings;
|
||||
|
||||
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||
settings.fmt = AUD_FMT_S16;
|
||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &settings);
|
||||
hw->samples = LINE_OUT_SAMPLES;
|
||||
out->active = 0;
|
||||
|
||||
out->sin.base.sif = &playback_sif.base;
|
||||
qemu_spice_add_interface (&out->sin.base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void line_out_fini (HWVoiceOut *hw)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
|
||||
spice_server_remove_interface (&out->sin.base);
|
||||
}
|
||||
|
||||
static int line_out_run (HWVoiceOut *hw, int live)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
int rpos, decr;
|
||||
int samples;
|
||||
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = rate_get_samples (&hw->info, &out->rate);
|
||||
decr = audio_MIN (live, decr);
|
||||
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int len = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
if (!out->frame) {
|
||||
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
|
||||
out->fpos = out->frame;
|
||||
}
|
||||
if (out->frame) {
|
||||
len = audio_MIN (len, out->fsize);
|
||||
hw->clip (out->fpos, hw->mix_buf + rpos, len);
|
||||
out->fsize -= len;
|
||||
out->fpos += len;
|
||||
if (out->fsize == 0) {
|
||||
spice_server_playback_put_samples (&out->sin, out->frame);
|
||||
out->frame = out->fpos = NULL;
|
||||
}
|
||||
}
|
||||
rpos = (rpos + len) % hw->samples;
|
||||
samples -= len;
|
||||
}
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (out->active) {
|
||||
break;
|
||||
}
|
||||
out->active = 1;
|
||||
rate_start (&out->rate);
|
||||
spice_server_playback_start (&out->sin);
|
||||
break;
|
||||
case VOICE_DISABLE:
|
||||
if (!out->active) {
|
||||
break;
|
||||
}
|
||||
out->active = 0;
|
||||
if (out->frame) {
|
||||
memset (out->fpos, 0, out->fsize << 2);
|
||||
spice_server_playback_put_samples (&out->sin, out->frame);
|
||||
out->frame = out->fpos = NULL;
|
||||
}
|
||||
spice_server_playback_stop (&out->sin);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* record */
|
||||
|
||||
static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
struct audsettings settings;
|
||||
|
||||
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||
settings.fmt = AUD_FMT_S16;
|
||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &settings);
|
||||
hw->samples = LINE_IN_SAMPLES;
|
||||
in->active = 0;
|
||||
|
||||
in->sin.base.sif = &record_sif.base;
|
||||
qemu_spice_add_interface (&in->sin.base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void line_in_fini (HWVoiceIn *hw)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
|
||||
spice_server_remove_interface (&in->sin.base);
|
||||
}
|
||||
|
||||
static int line_in_run (HWVoiceIn *hw)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
int num_samples;
|
||||
int ready;
|
||||
int len[2];
|
||||
uint64_t delta_samp;
|
||||
const uint32_t *samples;
|
||||
|
||||
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
delta_samp = rate_get_samples (&hw->info, &in->rate);
|
||||
num_samples = audio_MIN (num_samples, delta_samp);
|
||||
|
||||
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
|
||||
samples = in->samples;
|
||||
if (ready == 0) {
|
||||
static const uint32_t silence[LINE_IN_SAMPLES];
|
||||
samples = silence;
|
||||
ready = LINE_IN_SAMPLES;
|
||||
}
|
||||
|
||||
num_samples = audio_MIN (ready, num_samples);
|
||||
|
||||
if (hw->wpos + num_samples > hw->samples) {
|
||||
len[0] = hw->samples - hw->wpos;
|
||||
len[1] = num_samples - len[0];
|
||||
} else {
|
||||
len[0] = num_samples;
|
||||
len[1] = 0;
|
||||
}
|
||||
|
||||
hw->conv (hw->conv_buf + hw->wpos, samples, len[0], &nominal_volume);
|
||||
|
||||
if (len[1]) {
|
||||
hw->conv (hw->conv_buf, samples + len[0], len[1],
|
||||
&nominal_volume);
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + num_samples) % hw->samples;
|
||||
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (in->active) {
|
||||
break;
|
||||
}
|
||||
in->active = 1;
|
||||
rate_start (&in->rate);
|
||||
spice_server_record_start (&in->sin);
|
||||
break;
|
||||
case VOICE_DISABLE:
|
||||
if (!in->active) {
|
||||
break;
|
||||
}
|
||||
in->active = 0;
|
||||
spice_server_record_stop (&in->sin);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct audio_option audio_options[] = {
|
||||
{ /* end of list */ },
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops audio_callbacks = {
|
||||
.init_out = line_out_init,
|
||||
.fini_out = line_out_fini,
|
||||
.run_out = line_out_run,
|
||||
.write = line_out_write,
|
||||
.ctl_out = line_out_ctl,
|
||||
|
||||
.init_in = line_in_init,
|
||||
.fini_in = line_in_fini,
|
||||
.run_in = line_in_run,
|
||||
.read = line_in_read,
|
||||
.ctl_in = line_in_ctl,
|
||||
};
|
||||
|
||||
struct audio_driver spice_audio_driver = {
|
||||
.name = "spice",
|
||||
.descr = "spice audio driver",
|
||||
.options = audio_options,
|
||||
.init = spice_audio_init,
|
||||
.fini = spice_audio_fini,
|
||||
.pcm_ops = &audio_callbacks,
|
||||
.max_voices_out = 1,
|
||||
.max_voices_in = 1,
|
||||
.voice_size_out = sizeof (SpiceVoiceOut),
|
||||
.voice_size_in = sizeof (SpiceVoiceIn),
|
||||
};
|
||||
|
||||
void qemu_spice_audio_init (void)
|
||||
{
|
||||
spice_audio_driver.can_be_default = 1;
|
||||
}
|
||||
@@ -49,12 +49,14 @@ typedef struct BlkMigDevState {
|
||||
int64_t total_sectors;
|
||||
int64_t dirty;
|
||||
QSIMPLEQ_ENTRY(BlkMigDevState) entry;
|
||||
unsigned long *aio_bitmap;
|
||||
} BlkMigDevState;
|
||||
|
||||
typedef struct BlkMigBlock {
|
||||
uint8_t *buf;
|
||||
BlkMigDevState *bmds;
|
||||
int64_t sector;
|
||||
int nr_sectors;
|
||||
struct iovec iov;
|
||||
QEMUIOVector qiov;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
@@ -140,6 +142,52 @@ static inline long double compute_read_bwidth(void)
|
||||
return (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time;
|
||||
}
|
||||
|
||||
static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
|
||||
{
|
||||
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
|
||||
if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) {
|
||||
return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] &
|
||||
(1UL << (chunk % (sizeof(unsigned long) * 8))));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num,
|
||||
int nb_sectors, int set)
|
||||
{
|
||||
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 / (sizeof(unsigned long) * 8);
|
||||
bit = start % (sizeof(unsigned long) * 8);
|
||||
val = bmds->aio_bitmap[idx];
|
||||
if (set) {
|
||||
val |= 1UL << bit;
|
||||
} else {
|
||||
val &= ~(1UL << bit);
|
||||
}
|
||||
bmds->aio_bitmap[idx] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void alloc_aio_bitmap(BlkMigDevState *bmds)
|
||||
{
|
||||
BlockDriverState *bs = bmds->bs;
|
||||
int64_t bitmap_size;
|
||||
|
||||
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
|
||||
BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
|
||||
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
|
||||
|
||||
bmds->aio_bitmap = qemu_mallocz(bitmap_size);
|
||||
}
|
||||
|
||||
static void blk_mig_read_cb(void *opaque, int ret)
|
||||
{
|
||||
BlkMigBlock *blk = opaque;
|
||||
@@ -151,6 +199,7 @@ static void blk_mig_read_cb(void *opaque, int ret)
|
||||
add_avg_read_time(blk->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++;
|
||||
@@ -194,6 +243,7 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
|
||||
blk->buf = qemu_malloc(BLOCK_SIZE);
|
||||
blk->bmds = bmds;
|
||||
blk->sector = cur_sector;
|
||||
blk->nr_sectors = nr_sectors;
|
||||
|
||||
blk->iov.iov_base = blk->buf;
|
||||
blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
|
||||
@@ -248,6 +298,7 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
bmds->total_sectors = sectors;
|
||||
bmds->completed_sectors = 0;
|
||||
bmds->shared_base = block_mig_state.shared_base;
|
||||
alloc_aio_bitmap(bmds);
|
||||
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
|
||||
@@ -329,6 +380,9 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
|
||||
int nr_sectors;
|
||||
|
||||
for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
|
||||
if (bmds_aio_inflight(bmds, sector)) {
|
||||
qemu_aio_flush();
|
||||
}
|
||||
if (bdrv_get_dirty(bmds->bs, sector)) {
|
||||
|
||||
if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
|
||||
@@ -340,6 +394,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
|
||||
blk->buf = qemu_malloc(BLOCK_SIZE);
|
||||
blk->bmds = bmds;
|
||||
blk->sector = sector;
|
||||
blk->nr_sectors = nr_sectors;
|
||||
|
||||
if (is_async) {
|
||||
blk->iov.iov_base = blk->buf;
|
||||
@@ -354,6 +409,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
|
||||
goto error;
|
||||
}
|
||||
block_mig_state.submitted++;
|
||||
bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
|
||||
} else {
|
||||
if (bdrv_read(bmds->bs, sector, blk->buf,
|
||||
nr_sectors) < 0) {
|
||||
@@ -474,6 +530,7 @@ static void blk_mig_cleanup(Monitor *mon)
|
||||
|
||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
||||
qemu_free(bmds->aio_bitmap);
|
||||
qemu_free(bmds);
|
||||
}
|
||||
|
||||
|
||||
@@ -930,14 +930,14 @@ static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
|
||||
bit = start % (sizeof(unsigned long) * 8);
|
||||
val = bs->dirty_bitmap[idx];
|
||||
if (dirty) {
|
||||
if (!(val & (1 << bit))) {
|
||||
if (!(val & (1UL << bit))) {
|
||||
bs->dirty_count++;
|
||||
val |= 1 << bit;
|
||||
val |= 1UL << bit;
|
||||
}
|
||||
} else {
|
||||
if (val & (1 << bit)) {
|
||||
if (val & (1UL << bit)) {
|
||||
bs->dirty_count--;
|
||||
val &= ~(1 << bit);
|
||||
val &= ~(1UL << bit);
|
||||
}
|
||||
}
|
||||
bs->dirty_bitmap[idx] = val;
|
||||
@@ -1453,14 +1453,27 @@ const char *bdrv_get_device_name(BlockDriverState *bs)
|
||||
return bs->device_name;
|
||||
}
|
||||
|
||||
void bdrv_flush(BlockDriverState *bs)
|
||||
int bdrv_flush(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->open_flags & BDRV_O_NO_FLUSH) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bs->drv && bs->drv->bdrv_flush)
|
||||
bs->drv->bdrv_flush(bs);
|
||||
if (bs->drv && bs->drv->bdrv_flush) {
|
||||
return bs->drv->bdrv_flush(bs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some block drivers always operate in either writethrough or unsafe mode
|
||||
* and don't support bdrv_flush therefore. Usually qemu doesn't know how
|
||||
* the server works (because the behaviour is hardcoded or depends on
|
||||
* server-side configuration), so we can't ensure that everything is safe
|
||||
* on disk. Returning an error doesn't work because that would break guests
|
||||
* even if the server operates in writethrough mode.
|
||||
*
|
||||
* Let's hope the user knows what he's doing.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_flush_all(void)
|
||||
@@ -2018,12 +2031,49 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct BlockCompleteData {
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
BlockDriverState *bs;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
} BlockCompleteData;
|
||||
|
||||
static void block_complete_cb(void *opaque, int ret)
|
||||
{
|
||||
BlockCompleteData *b = opaque;
|
||||
|
||||
if (b->bs->dirty_bitmap) {
|
||||
set_dirty_bitmap(b->bs, b->sector_num, b->nb_sectors, 1);
|
||||
}
|
||||
b->cb(b->opaque, ret);
|
||||
qemu_free(b);
|
||||
}
|
||||
|
||||
static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BlockCompleteData *blkdata = qemu_mallocz(sizeof(BlockCompleteData));
|
||||
|
||||
blkdata->bs = bs;
|
||||
blkdata->cb = cb;
|
||||
blkdata->opaque = opaque;
|
||||
blkdata->sector_num = sector_num;
|
||||
blkdata->nb_sectors = nb_sectors;
|
||||
|
||||
return blkdata;
|
||||
}
|
||||
|
||||
BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
BlockDriverAIOCB *ret;
|
||||
BlockCompleteData *blk_cb_data;
|
||||
|
||||
trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
|
||||
|
||||
@@ -2035,7 +2085,10 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
return NULL;
|
||||
|
||||
if (bs->dirty_bitmap) {
|
||||
set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
|
||||
blk_cb_data = blk_dirty_cb_alloc(bs, sector_num, nb_sectors, cb,
|
||||
opaque);
|
||||
cb = &block_complete_cb;
|
||||
opaque = blk_cb_data;
|
||||
}
|
||||
|
||||
ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors,
|
||||
@@ -2672,8 +2725,8 @@ int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
|
||||
|
||||
if (bs->dirty_bitmap &&
|
||||
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
|
||||
return bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
|
||||
(1 << (chunk % (sizeof(unsigned long) * 8)));
|
||||
return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
|
||||
(1UL << (chunk % (sizeof(unsigned long) * 8))));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
|
||||
/* Ensure contents are flushed to disk. */
|
||||
void bdrv_flush(BlockDriverState *bs);
|
||||
int bdrv_flush(BlockDriverState *bs);
|
||||
void bdrv_flush_all(void);
|
||||
void bdrv_close_all(void);
|
||||
|
||||
|
||||
+2
-2
@@ -397,9 +397,9 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static void blkdebug_flush(BlockDriverState *bs)
|
||||
static int blkdebug_flush(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_flush(bs->file);
|
||||
return bdrv_flush(bs->file);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
|
||||
+4
-4
@@ -116,12 +116,12 @@ static void blkverify_close(BlockDriverState *bs)
|
||||
s->test_file = NULL;
|
||||
}
|
||||
|
||||
static void blkverify_flush(BlockDriverState *bs)
|
||||
static int blkverify_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
/* Only flush test file, the raw file is not important */
|
||||
bdrv_flush(s->test_file);
|
||||
return bdrv_flush(s->test_file);
|
||||
}
|
||||
|
||||
static int64_t blkverify_getlength(BlockDriverState *bs)
|
||||
@@ -300,8 +300,8 @@ static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
||||
{
|
||||
ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
|
||||
if (offset != -1) {
|
||||
blkverify_err(acb, "contents mismatch in sector %lld",
|
||||
acb->sector_num + (offset / BDRV_SECTOR_SIZE));
|
||||
blkverify_err(acb, "contents mismatch in sector %" PRId64,
|
||||
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -282,9 +282,9 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cow_flush(BlockDriverState *bs)
|
||||
static int cow_flush(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_flush(bs->file);
|
||||
return bdrv_flush(bs->file);
|
||||
}
|
||||
|
||||
static QEMUOptionParameter cow_create_options[] = {
|
||||
|
||||
+2
-3
@@ -54,7 +54,6 @@ typedef struct QCowHeader {
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVQcowState {
|
||||
BlockDriverState *hd;
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
@@ -910,9 +909,9 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcow_flush(BlockDriverState *bs)
|
||||
static int qcow_flush(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_flush(bs->file);
|
||||
return bdrv_flush(bs->file);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs,
|
||||
|
||||
@@ -188,6 +188,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||
ret = bdrv_pread(bs->file, l2_offset, *l2_table,
|
||||
s->l2_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
qcow2_l2_cache_reset(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user