* nbd and qemu-nbd fixes (Eric, Max)

* nbd refactoring (Vladimir)
 * vhost-user-scsi, take N+1 (Felipe)
 * replace memory_region_set_fd with memory_region_init_ram_from_fd (Marc-André)
 * docs/ movement (Paolo)
 * megasas TOCTOU fixes (Paolo)
 * make async_safe_run_on_cpu work on kvm/hax accelerators (Paolo)
 * Build system and poison.h improvements (Thomas)
 * -accel thread=xxx fix (Thomas)
 * move files to accel/ (Yang Zhong)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQEcBAABAgAGBQJZQli7AAoJEL/70l94x66DYAUH/icBoGSyY70G033BxbWW+jPW
 pjqobQw3YnFmiHSkXy1Xd4LKa0dyrVdmVECOAvB0embiN6pIk2UpeT4aQNHffG3f
 Y9wMRKnp6RMLpzwRYbde4rAlitn52ecGIANtFUC6RTnL7wKCuh3beWruKmkudG8V
 ZAsmX30pPFJxtQYJ0/q3c2jB6V87h2GocRLAmy1lF5/X1pXQ2fiKOYOJvV3HDY1U
 UQ+Ixuf4tTbyBwTm4Lx2tD3+olkVTUvcATqxhI+1JKu6lE0Dgb58urYDOfvVPFDl
 98s6bU53f0gpOcb1/wKOAXKdZ2tvqRtwsP8IoPDnUpk8HGxbNWdofoxsO37vVIU=
 =zeKT
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

* nbd and qemu-nbd fixes (Eric, Max)
* nbd refactoring (Vladimir)
* vhost-user-scsi, take N+1 (Felipe)
* replace memory_region_set_fd with memory_region_init_ram_from_fd (Marc-André)
* docs/ movement (Paolo)
* megasas TOCTOU fixes (Paolo)
* make async_safe_run_on_cpu work on kvm/hax accelerators (Paolo)
* Build system and poison.h improvements (Thomas)
* -accel thread=xxx fix (Thomas)
* move files to accel/ (Yang Zhong)

# gpg: Signature made Thu 15 Jun 2017 10:51:55 BST
# gpg:                using RSA key 0xBFFBD25F78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* remotes/bonzini/tags/for-upstream: (41 commits)
  vhost-user-scsi: Introduce a vhost-user-scsi sample application
  vhost-user-scsi: Introduce vhost-user-scsi host device
  qemu-doc: include version number
  docs: create interop/ subdirectory
  include/exec/poison: Mark some CONFIG defines as poisoned, too
  include/exec/poison: Add missing TARGET defines
  nbd/server: refactor nbd_trip
  nbd/server: rename rc to ret
  nbd/server: get rid of fail: return rc
  nbd/server: nbd_negotiate: fix error path
  nbd/server: remove NBDClientNewData
  nbd/server: refactor nbd_co_receive_request
  nbd/server: get rid of EAGAIN dead code
  nbd/server: refactor nbd_co_send_reply
  nbd/server: get rid of ssize_t
  nbd/server: get rid of nbd_negotiate_read and friends
  nbd: make nbd_drop public
  nbd: rename read_sync and friends
  accel: move kvm related accelerator files into accel/
  tcg: move tcg backend files into accel/tcg/
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-06-20 14:20:34 +01:00
commit 7e56accdaf
66 changed files with 1895 additions and 596 deletions

17
.gitignore vendored
View File

@ -50,6 +50,7 @@
/qemu-version.h.tmp
/module_block.h
/vscclient
/vhost-user-scsi
/fsdev/virtfs-proxy-helper
*.[1-9]
*.a
@ -99,14 +100,14 @@
/pc-bios/optionrom/kvmvapic.img
/pc-bios/s390-ccw/s390-ccw.elf
/pc-bios/s390-ccw/s390-ccw.img
/docs/qemu-ga-qapi.texi
/docs/qemu-ga-ref.html
/docs/qemu-ga-ref.info*
/docs/qemu-ga-ref.txt
/docs/qemu-qmp-qapi.texi
/docs/qemu-qmp-ref.html
/docs/qemu-qmp-ref.info*
/docs/qemu-qmp-ref.txt
/docs/interop/qemu-ga-qapi.texi
/docs/interop/qemu-ga-ref.html
/docs/interop/qemu-ga-ref.info*
/docs/interop/qemu-ga-ref.txt
/docs/interop/qemu-qmp-qapi.texi
/docs/interop/qemu-qmp-ref.html
/docs/interop/qemu-qmp-ref.info*
/docs/interop/qemu-qmp-ref.txt
/docs/version.texi
*.tps
.stgit-*

View File

@ -207,8 +207,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
ifdef BUILD_DOCS
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7
DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.txt docs/qemu-ga-ref.7
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
ifdef CONFIG_VIRTFS
DOCS+=fsdev/virtfs-proxy-helper.1
endif
@ -269,6 +269,7 @@ dummy := $(call unnest-vars,, \
ivshmem-client-obj-y \
ivshmem-server-obj-y \
libvhost-user-obj-y \
vhost-user-scsi-obj-y \
qga-vss-dll-obj-y \
block-obj-y \
block-obj-m \
@ -473,6 +474,8 @@ ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
$(call LINK, $^)
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
$(call LINK, $^)
vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y)
$(call LINK, $^)
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
$(call quiet-command,$(PYTHON) $< $@ \
@ -519,11 +522,12 @@ distclean: clean
rm -f qemu-doc.vr qemu-doc.txt
rm -f config.log
rm -f linux-headers/asm
rm -f docs/qemu-ga-qapi.texi docs/qemu-qmp-qapi.texi docs/version.texi
rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
rm -f docs/version.texi
rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi
rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
for d in $(TARGET_DIRS); do \
rm -rf $$d || exit 1 ; \
done
@ -562,13 +566,13 @@ install-doc: $(DOCS)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
ifdef CONFIG_POSIX
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
ifneq ($(TOOLS),)
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
@ -576,9 +580,9 @@ ifneq ($(TOOLS),)
endif
ifneq (,$(findstring qemu-ga,$(TOOLS)))
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
endif
endif
ifdef CONFIG_VIRTFS
@ -666,28 +670,27 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
# documentation
MAKEINFO=makeinfo
MAKEINFOFLAGS=--no-split --number-sections -I docs
TEXIFLAG=$(if $(V),,--quiet)
MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)"
TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
docs/version.texi: $(SRC_PATH)/VERSION
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
%.html: %.texi
%.html: %.texi docs/version.texi
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
--html $< -o $@,"GEN","$@")
%.info: %.texi
%.info: %.texi docs/version.texi
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
%.txt: %.texi
%.txt: %.texi docs/version.texi
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
--plaintext $< -o $@,"GEN","$@")
%.pdf: %.texi
$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I docs $< -o $@,"GEN","$@")
docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.txt docs/qemu-ga-ref.pdf docs/qemu-ga-ref.7.pod: docs/version.texi
docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.pod: docs/version.texi
%.pdf: %.texi docs/version.texi
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
@ -701,12 +704,12 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
docs/qemu-qmp-qapi.texi docs/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
docs/interop/qemu-qmp-qapi.texi docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
docs/qemu-qmp-qapi.texi: $(qapi-modules)
docs/interop/qemu-qmp-qapi.texi: $(qapi-modules)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
docs/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
@ -716,21 +719,25 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
qemu-ga.8: qemu-ga.texi
html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
txt: qemu-doc.txt docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
qemu-monitor-info.texi
docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
docs/qemu-ga-ref.texi docs/qemu-ga-qapi.texi
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7: \
docs/interop/qemu-ga-ref.texi docs/interop/qemu-ga-qapi.texi
docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
docs/qemu-qmp-ref.texi docs/qemu-qmp-qapi.texi
docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
docs/interop/qemu-qmp-ref.info docs/interop/qemu-qmp-ref.pdf \
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
ifdef CONFIG_WIN32
@ -791,9 +798,11 @@ endif # CONFIG_WIN
# Add a dependency on the generated files, so that they are always
# rebuilt before other object files
ifneq ($(wildcard config-host.mak),)
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
Makefile: $(GENERATED_FILES)
endif
endif
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \

View File

@ -52,7 +52,6 @@ common-obj-y += migration/
common-obj-y += audio/
common-obj-y += hw/
common-obj-y += accel.o
common-obj-y += replay/
@ -111,6 +110,10 @@ qga-vss-dll-obj-y = qga/
ivshmem-client-obj-y = contrib/ivshmem-client/
ivshmem-server-obj-y = contrib/ivshmem-server/
libvhost-user-obj-y = contrib/libvhost-user/
vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
vhost-user-scsi-obj-y += contrib/libvhost-user/libvhost-user.o
######################################################################
trace-events-subdirs =
@ -163,6 +166,8 @@ trace-events-subdirs += target/ppc
trace-events-subdirs += qom
trace-events-subdirs += linux-user
trace-events-subdirs += qapi
trace-events-subdirs += accel/tcg
trace-events-subdirs += accel/kvm
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)

View File

@ -88,20 +88,17 @@ all: $(PROGS) stap
#########################################################
# cpu emulator library
obj-y = exec.o translate-all.o cpu-exec.o
obj-y += translate-common.o
obj-y += cpu-exec-common.o
obj-y += exec.o
obj-y += accel/
obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
obj-y += tcg/tcg-common.o tcg/tcg-runtime.o
obj-$(CONFIG_TCG_INTERPRETER) += tci.o
obj-y += tcg/tcg-common.o
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
obj-y += fpu/softfloat.o
obj-y += target/$(TARGET_BASE_ARCH)/
obj-y += disas.o
obj-y += tcg-runtime.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o
@ -142,8 +139,7 @@ ifdef CONFIG_SOFTMMU
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
obj-y += qtest.o bootdevice.o
obj-y += hw/
obj-$(CONFIG_KVM) += kvm-all.o
obj-y += memory.o cputlb.o
obj-y += memory.o
obj-y += memory_mapping.o
obj-y += dump.o
obj-y += migration/ram.o

4
accel/Makefile.objs Normal file
View File

@ -0,0 +1,4 @@
obj-$(CONFIG_SOFTMMU) += accel.o
obj-y += kvm/
obj-y += tcg/
obj-y += stubs/

View File

@ -34,15 +34,6 @@
#include "hw/xen/xen.h"
#include "qom/object.h"
int tcg_tb_size;
static bool tcg_allowed = true;
static int tcg_init(MachineState *ms)
{
tcg_exec_init(tcg_tb_size * 1024 * 1024);
return 0;
}
static const TypeInfo accel_type = {
.name = TYPE_ACCEL,
.parent = TYPE_OBJECT,
@ -129,27 +120,9 @@ void configure_accelerator(MachineState *ms)
}
}
static void tcg_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg";
ac->init_machine = tcg_init;
ac->allowed = &tcg_allowed;
}
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
static const TypeInfo tcg_accel_type = {
.name = TYPE_TCG_ACCEL,
.parent = TYPE_ACCEL,
.class_init = tcg_accel_class_init,
};
static void register_accel_types(void)
{
type_register_static(&accel_type);
type_register_static(&tcg_accel_type);
}
type_init(register_accel_types);

1
accel/kvm/Makefile.objs Normal file
View File

@ -0,0 +1 @@
obj-$(CONFIG_KVM) += kvm-all.o

View File

@ -36,7 +36,7 @@
#include "exec/ram_addr.h"
#include "exec/address-spaces.h"
#include "qemu/event_notifier.h"
#include "trace-root.h"
#include "trace.h"
#include "hw/irq.h"
#include "hw/boards.h"
@ -1977,6 +1977,7 @@ int kvm_cpu_exec(CPUState *cpu)
}
qemu_mutex_unlock_iothread();
cpu_exec_start(cpu);
do {
MemTxAttrs attrs;
@ -2106,6 +2107,7 @@ int kvm_cpu_exec(CPUState *cpu)
}
} while (ret == 0);
cpu_exec_end(cpu);
qemu_mutex_lock_iothread();
if (ret < 0) {

15
accel/kvm/trace-events Normal file
View File

@ -0,0 +1,15 @@
# Trace events for debugging and performance instrumentation
# kvm-all.c
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
kvm_irqchip_commit_routes(void) ""
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
kvm_irqchip_release_virq(int virq) "virq %d"

View File

@ -0,0 +1 @@
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o

3
accel/tcg/Makefile.objs Normal file
View File

@ -0,0 +1,3 @@
obj-$(CONFIG_SOFTMMU) += tcg-all.o
obj-$(CONFIG_SOFTMMU) += cputlb.o
obj-y += cpu-exec.o cpu-exec-common.o translate-all.o translate-common.o

View File

@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "trace-root.h"
#include "trace.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg.h"

61
accel/tcg/tcg-all.c Normal file
View File

@ -0,0 +1,61 @@
/*
* QEMU System Emulator, accelerator interfaces
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (c) 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "sysemu/accel.h"
#include "sysemu/sysemu.h"
#include "qom/object.h"
int tcg_tb_size;
static bool tcg_allowed = true;
static int tcg_init(MachineState *ms)
{
tcg_exec_init(tcg_tb_size * 1024 * 1024);
return 0;
}
static void tcg_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg";
ac->init_machine = tcg_init;
ac->allowed = &tcg_allowed;
}
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
static const TypeInfo tcg_accel_type = {
.name = TYPE_TCG_ACCEL,
.parent = TYPE_ACCEL,
.class_init = tcg_accel_class_init,
};
static void register_accel_types(void)
{
type_register_static(&tcg_accel_type);
}
type_init(register_accel_types);

10
accel/tcg/trace-events Normal file
View File

@ -0,0 +1,10 @@
# Trace events for debugging and performance instrumentation
# TCG related tracing (mostly disabled by default)
# cpu-exec.c
disable exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
disable exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
disable exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=%x"
# translate-all.c
translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"

View File

@ -25,7 +25,7 @@
#include "qemu-common.h"
#define NO_CPU_IO_DEFS
#include "cpu.h"
#include "trace-root.h"
#include "trace.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg.h"

View File

@ -144,8 +144,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
qio_channel_set_cork(s->ioc, true);
rc = nbd_send_request(s->ioc, request);
if (rc >= 0) {
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
false, NULL);
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, false,
NULL);
if (ret != request->len) {
rc = -EIO;
}
@ -173,8 +173,8 @@ static void nbd_co_receive_reply(NBDClientSession *s,
reply->error = EIO;
} else {
if (qiov && reply->error == 0) {
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
true, NULL);
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, true,
NULL);
if (ret != request->len) {
reply->error = EIO;
}

View File

@ -27,6 +27,10 @@ typedef struct NBDServerData {
static NBDServerData *nbd_server;
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
{
nbd_client_put(client);
}
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
gpointer opaque)
@ -46,7 +50,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
nbd_client_new(NULL, cioc,
nbd_server->tlscreds, NULL,
nbd_client_put);
nbd_blockdev_client_closed);
object_unref(OBJECT(cioc));
return TRUE;
}

4
configure vendored
View File

@ -407,7 +407,7 @@ QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS"
QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/include"
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/accel/tcg -I\$(SRC_PATH)/include"
if test "$debug_info" = "yes"; then
CFLAGS="-g $CFLAGS"
LDFLAGS="-g $LDFLAGS"
@ -6374,7 +6374,7 @@ fi
# build tree in object directory in case the source is not in the current directory
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
DIRS="$DIRS docs fsdev"
DIRS="$DIRS docs docs/interop fsdev"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS qapi-generated"

View File

@ -17,6 +17,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/poll.h>
#include <linux/vhost.h>
#include "standard-headers/linux/virtio_ring.h"
@ -192,11 +193,11 @@ typedef struct VuVirtq {
} VuVirtq;
enum VuWatchCondtion {
VU_WATCH_IN = 1 << 0,
VU_WATCH_OUT = 1 << 1,
VU_WATCH_PRI = 1 << 2,
VU_WATCH_ERR = 1 << 3,
VU_WATCH_HUP = 1 << 4,
VU_WATCH_IN = POLLIN,
VU_WATCH_OUT = POLLOUT,
VU_WATCH_PRI = POLLPRI,
VU_WATCH_ERR = POLLERR,
VU_WATCH_HUP = POLLHUP,
};
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);

View File

@ -0,0 +1 @@
vhost-user-scsi-obj-y = vhost-user-scsi.o

View File

@ -0,0 +1,886 @@
/*
* vhost-user-scsi sample application
*
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
*
* Author:
* Felipe Franciosi <felipe@nutanix.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 only.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "contrib/libvhost-user/libvhost-user.h"
#include "hw/virtio/virtio-scsi.h"
#include "iscsi/iscsi.h"
#include <glib.h>
/* Small compat shim from glib 2.32 */
#ifndef G_SOURCE_CONTINUE
#define G_SOURCE_CONTINUE TRUE
#endif
#ifndef G_SOURCE_REMOVE
#define G_SOURCE_REMOVE FALSE
#endif
/* #define VUS_DEBUG 1 */
/** Log helpers **/
#define PPRE \
struct timespec ts; \
char timebuf[64]; \
struct tm tm; \
(void)clock_gettime(CLOCK_REALTIME, &ts); \
(void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
#define PEXT(lvl, msg, ...) do { \
PPRE; \
fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n", \
timebuf, ts.tv_nsec / 1000, \
__FILE__, __func__, __LINE__, ## __VA_ARGS__); \
} while (0)
#define PNOR(lvl, msg, ...) do { \
PPRE; \
fprintf(stderr, "%s.%06ld " lvl ": " msg "\n", \
timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__); \
} while (0)
#ifdef VUS_DEBUG
#define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
#define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
#define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
#else
#define PDBG(msg, ...) { }
#define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
#define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
#endif
/** vhost-user-scsi specific definitions **/
/* Only 1 LUN and device supported today */
#define VUS_MAX_LUNS 1
#define VUS_MAX_DEVS 1
#define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
typedef struct iscsi_lun {
struct iscsi_context *iscsi_ctx;
int iscsi_lun;
} iscsi_lun_t;
typedef struct vhost_scsi_dev {
VuDev vu_dev;
int server_sock;
GMainLoop *loop;
GTree *fdmap; /* fd -> gsource context id */
iscsi_lun_t luns[VUS_MAX_LUNS];
} vhost_scsi_dev_t;
static vhost_scsi_dev_t *vhost_scsi_devs[VUS_MAX_DEVS];
/** glib event loop integration for libvhost-user and misc callbacks **/
QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
typedef struct vus_gsrc {
GSource parent;
vhost_scsi_dev_t *vdev_scsi;
GPollFD gfd;
vu_watch_cb vu_cb;
} vus_gsrc_t;
static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
{
return (b > a) - (b < a);
}
static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
{
assert(timeout);
*timeout = -1;
return FALSE;
}
static gboolean vus_gsrc_check(GSource *src)
{
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
assert(vus_src);
return vus_src->gfd.revents & vus_src->gfd.events;
}
static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
{
vhost_scsi_dev_t *vdev_scsi;
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
assert(vus_src);
assert(!(vus_src->vu_cb && cb));
vdev_scsi = vus_src->vdev_scsi;
assert(vdev_scsi);
if (cb) {
return cb(data);
}
if (vus_src->vu_cb) {
vus_src->vu_cb(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
}
return G_SOURCE_CONTINUE;
}
static GSourceFuncs vus_gsrc_funcs = {
vus_gsrc_prepare,
vus_gsrc_check,
vus_gsrc_dispatch,
NULL
};
static int vus_gsrc_new(vhost_scsi_dev_t *vdev_scsi, int fd, GIOCondition cond,
vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
{
GSource *vus_gsrc;
vus_gsrc_t *vus_src;
guint id;
assert(vdev_scsi);
assert(fd >= 0);
assert(vu_cb || gsrc_cb);
assert(!(vu_cb && gsrc_cb));
vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
if (!vus_gsrc) {
PERR("Error creating GSource for new watch");
return -1;
}
vus_src = (vus_gsrc_t *)vus_gsrc;
vus_src->vdev_scsi = vdev_scsi;
vus_src->gfd.fd = fd;
vus_src->gfd.events = cond;
vus_src->vu_cb = vu_cb;
g_source_add_poll(vus_gsrc, &vus_src->gfd);
g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
id = g_source_attach(vus_gsrc, NULL);
assert(id);
g_source_unref(vus_gsrc);
g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
(gpointer)(uintptr_t)id);
return 0;
}
/* from libiscsi's scsi-lowlevel.h **
*
* nb. We can't directly include scsi-lowlevel.h due to a namespace conflict:
* QEMU's scsi.h also defines "SCSI_XFER_NONE".
*/
#define SCSI_CDB_MAX_SIZE 16
struct scsi_iovector {
struct scsi_iovec *iov;
int niov;
int nalloc;
size_t offset;
int consumed;
};
struct scsi_allocated_memory {
struct scsi_allocated_memory *next;
char buf[0];
};
struct scsi_data {
int size;
unsigned char *data;
};
enum scsi_sense_key {
SCSI_SENSE_NO_SENSE = 0x00,
SCSI_SENSE_RECOVERED_ERROR = 0x01,
SCSI_SENSE_NOT_READY = 0x02,
SCSI_SENSE_MEDIUM_ERROR = 0x03,
SCSI_SENSE_HARDWARE_ERROR = 0x04,
SCSI_SENSE_ILLEGAL_REQUEST = 0x05,
SCSI_SENSE_UNIT_ATTENTION = 0x06,
SCSI_SENSE_DATA_PROTECTION = 0x07,
SCSI_SENSE_BLANK_CHECK = 0x08,
SCSI_SENSE_VENDOR_SPECIFIC = 0x09,
SCSI_SENSE_COPY_ABORTED = 0x0a,
SCSI_SENSE_COMMAND_ABORTED = 0x0b,
SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
SCSI_SENSE_OVERFLOW_COMMAND = 0x0d,
SCSI_SENSE_MISCOMPARE = 0x0e
};
struct scsi_sense {
unsigned char error_type;
enum scsi_sense_key key;
int ascq;
unsigned sense_specific:1;
unsigned ill_param_in_cdb:1;
unsigned bit_pointer_valid:1;
unsigned char bit_pointer;
uint16_t field_pointer;
};
enum scsi_residual {
SCSI_RESIDUAL_NO_RESIDUAL = 0,
SCSI_RESIDUAL_UNDERFLOW,
SCSI_RESIDUAL_OVERFLOW
};
struct scsi_task {
int status;
int cdb_size;
int xfer_dir;
int expxferlen;
unsigned char cdb[SCSI_CDB_MAX_SIZE];
enum scsi_residual residual_status;
size_t residual;
struct scsi_sense sense;
struct scsi_data datain;
struct scsi_allocated_memory *mem;
void *ptr;
uint32_t itt;
uint32_t cmdsn;
uint32_t lun;
struct scsi_iovector iovector_in;
struct scsi_iovector iovector_out;
};
/** libiscsi integration **/
static int iscsi_add_lun(iscsi_lun_t *lun, char *iscsi_uri)
{
struct iscsi_url *iscsi_url;
struct iscsi_context *iscsi_ctx;
int ret = 0;
assert(lun);
assert(iscsi_uri);
iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
if (!iscsi_ctx) {
PERR("Unable to create iSCSI context");
return -1;
}
iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
if (!iscsi_url) {
PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
goto fail;
}
iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
goto fail;
}
lun->iscsi_ctx = iscsi_ctx;
lun->iscsi_lun = iscsi_url->lun;
PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
out:
if (iscsi_url) {
iscsi_destroy_url(iscsi_url);
}
return ret;
fail:
(void)iscsi_destroy_context(iscsi_ctx);
ret = -1;
goto out;
}
static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
int xfer_len) {
struct scsi_task *task;
assert(cdb_len > 0);
assert(cdb);
task = calloc(1, sizeof(struct scsi_task));
if (!task) {
PERR("Error allocating task: %s", strerror(errno));
return NULL;
}
memcpy(task->cdb, cdb, cdb_len);
task->cdb_size = cdb_len;
task->xfer_dir = dir;
task->expxferlen = xfer_len;
return task;
}
static int get_cdb_len(uint8_t *cdb)
{
assert(cdb);
switch (cdb[0] >> 5) {
case 0: return 6;
case 1: /* fall through */
case 2: return 10;
case 4: return 16;
case 5: return 12;
}
PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
return -1;
}
static int handle_cmd_sync(struct iscsi_context *ctx,
VirtIOSCSICmdReq *req,
struct iovec *out, unsigned int out_len,
VirtIOSCSICmdResp *rsp,
struct iovec *in, unsigned int in_len) {
struct scsi_task *task;
uint32_t dir;
uint32_t len;
int cdb_len;
int i;
assert(ctx);
assert(req);
assert(rsp);
if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
/* Ignore anything different than target=0, lun=0 */
PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
req->lun[1], req->lun[3]);
rsp->status = SCSI_STATUS_CHECK_CONDITION;
memset(rsp->sense, 0, sizeof(rsp->sense));
rsp->sense_len = 18;
rsp->sense[0] = 0x70;
rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
rsp->sense[7] = 10;
rsp->sense[12] = 0x24;
return 0;
}
cdb_len = get_cdb_len(req->cdb);
if (cdb_len == -1) {
return -1;
}
len = 0;
if (!out_len && !in_len) {
dir = SCSI_XFER_NONE;
} else if (out_len) {
dir = SCSI_XFER_TO_DEV;
for (i = 0; i < out_len; i++) {
len += out[i].iov_len;
}
} else {
dir = SCSI_XFER_FROM_DEV;
for (i = 0; i < in_len; i++) {
len += in[i].iov_len;
}
}
task = scsi_task_new(cdb_len, req->cdb, dir, len);
if (!task) {
PERR("Unable to create iscsi task");
return -1;
}
if (dir == SCSI_XFER_TO_DEV) {
task->iovector_out.iov = (struct scsi_iovec *)out;
task->iovector_out.niov = out_len;
} else if (dir == SCSI_XFER_FROM_DEV) {
task->iovector_in.iov = (struct scsi_iovec *)in;
task->iovector_in.niov = in_len;
}
PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
cdb_len, dir, task);
if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
PERR("Error serving SCSI command");
free(task);
return -1;
}
memset(rsp, 0, sizeof(*rsp));
rsp->status = task->status;
rsp->resid = task->residual;
if (task->status == SCSI_STATUS_CHECK_CONDITION) {
rsp->response = VIRTIO_SCSI_S_FAILURE;
rsp->sense_len = task->datain.size - 2;
memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
}
free(task);
PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
rsp->status, rsp->resid, rsp->response, rsp->sense_len);
return 0;
}
/** libvhost-user callbacks **/
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev);
static void vus_panic_cb(VuDev *vu_dev, const char *buf)
{
vhost_scsi_dev_t *vdev_scsi;
assert(vu_dev);
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
if (buf) {
PERR("vu_panic: %s", buf);
}
if (vdev_scsi) {
assert(vdev_scsi->loop);
g_main_loop_quit(vdev_scsi->loop);
}
}
static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
void *pvt) {
vhost_scsi_dev_t *vdev_scsi;
guint id;
assert(vu_dev);
assert(fd >= 0);
assert(cb);
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
if (!vdev_scsi) {
vus_panic_cb(vu_dev, NULL);
return;
}
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
(gpointer)(uintptr_t)fd);
if (id) {
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
assert(vus_src);
g_source_destroy(vus_src);
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
}
if (vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt)) {
vus_panic_cb(vu_dev, NULL);
}
}
static void vus_del_watch_cb(VuDev *vu_dev, int fd)
{
vhost_scsi_dev_t *vdev_scsi;
guint id;
assert(vu_dev);
assert(fd >= 0);
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
if (!vdev_scsi) {
vus_panic_cb(vu_dev, NULL);
return;
}
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
(gpointer)(uintptr_t)fd);
if (id) {
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
assert(vus_src);
g_source_destroy(vus_src);
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
}
}
static void vus_proc_ctl(VuDev *vu_dev, int idx)
{
/* Control VQ not implemented */
}
static void vus_proc_evt(VuDev *vu_dev, int idx)
{
/* Event VQ not implemented */
}
static void vus_proc_req(VuDev *vu_dev, int idx)
{
vhost_scsi_dev_t *vdev_scsi;
VuVirtq *vq;
assert(vu_dev);
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
if (!vdev_scsi) {
vus_panic_cb(vu_dev, NULL);
return;
}
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
PERR("VQ Index out of range: %d", idx);
vus_panic_cb(vu_dev, NULL);
return;
}
vq = vu_get_queue(vu_dev, idx);
if (!vq) {
PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
vus_panic_cb(vu_dev, NULL);
return;
}
PDBG("Got kicked on vq[%d]@%p", idx, vq);
while (1) {
VuVirtqElement *elem;
VirtIOSCSICmdReq *req;
VirtIOSCSICmdResp *rsp;
elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
if (!elem) {
PDBG("No more elements pending on vq[%d]@%p", idx, vq);
break;
}
PDBG("Popped elem@%p", elem);
assert(!((elem->out_num > 1) && (elem->in_num > 1)));
assert((elem->out_num > 0) && (elem->in_num > 0));
if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
PERR("Invalid virtio-scsi req header");
vus_panic_cb(vu_dev, NULL);
break;
}
req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
PERR("Invalid virtio-scsi rsp header");
vus_panic_cb(vu_dev, NULL);
break;
}
rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
if (handle_cmd_sync(vdev_scsi->luns[0].iscsi_ctx,
req, &elem->out_sg[1], elem->out_num - 1,
rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
vus_panic_cb(vu_dev, NULL);
break;
}
vu_queue_push(vu_dev, vq, elem, 0);
vu_queue_notify(vu_dev, vq);
free(elem);
}
}
static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
{
VuVirtq *vq;
assert(vu_dev);
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
PERR("VQ Index out of range: %d", idx);
vus_panic_cb(vu_dev, NULL);
return;
}
vq = vu_get_queue(vu_dev, idx);
switch (idx) {
case 0:
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_ctl : NULL);
break;
case 1:
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_evt : NULL);
break;
default:
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
}
}
static const VuDevIface vus_iface = {
.queue_set_started = vus_queue_set_started,
};
static gboolean vus_vhost_cb(gpointer data)
{
VuDev *vu_dev = (VuDev *)data;
assert(vu_dev);
if (!vu_dispatch(vu_dev) != 0) {
PERR("Error processing vhost message");
vus_panic_cb(vu_dev, NULL);
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
/** misc helpers **/
static int unix_sock_new(char *unix_fn)
{
int sock;
struct sockaddr_un un;
size_t len;
assert(unix_fn);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock <= 0) {
perror("socket");
return -1;
}
un.sun_family = AF_UNIX;
(void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
len = sizeof(un.sun_family) + strlen(un.sun_path);
(void)unlink(unix_fn);
if (bind(sock, (struct sockaddr *)&un, len) < 0) {
perror("bind");
goto fail;
}
if (listen(sock, 1) < 0) {
perror("listen");
goto fail;
}
return sock;
fail:
(void)close(sock);
return -1;
}
/** vhost-user-scsi **/
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev)
{
int i;
assert(vu_dev);
for (i = 0; i < VUS_MAX_DEVS; i++) {
if (&vhost_scsi_devs[i]->vu_dev == vu_dev) {
return vhost_scsi_devs[i];
}
}
PERR("Unknown VuDev %p", vu_dev);
return NULL;
}
static void vdev_scsi_deinit(vhost_scsi_dev_t *vdev_scsi)
{
if (!vdev_scsi) {
return;
}
if (vdev_scsi->server_sock >= 0) {
struct sockaddr_storage ss;
socklen_t sslen = sizeof(ss);
if (getsockname(vdev_scsi->server_sock, (struct sockaddr *)&ss,
&sslen) == 0) {
struct sockaddr_un *su = (struct sockaddr_un *)&ss;
(void)unlink(su->sun_path);
}
(void)close(vdev_scsi->server_sock);
vdev_scsi->server_sock = -1;
}
if (vdev_scsi->loop) {
g_main_loop_unref(vdev_scsi->loop);
vdev_scsi->loop = NULL;
}
}
static vhost_scsi_dev_t *vdev_scsi_new(char *unix_fn)
{
vhost_scsi_dev_t *vdev_scsi = NULL;
assert(unix_fn);
vdev_scsi = calloc(1, sizeof(vhost_scsi_dev_t));
if (!vdev_scsi) {
PERR("calloc: %s", strerror(errno));
return NULL;
}
vdev_scsi->server_sock = unix_sock_new(unix_fn);
if (vdev_scsi->server_sock < 0) {
goto err;
}
vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
if (!vdev_scsi->loop) {
PERR("Error creating glib event loop");
goto err;
}
vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
if (!vdev_scsi->fdmap) {
PERR("Error creating glib tree for fdmap");
goto err;
}
return vdev_scsi;
err:
vdev_scsi_deinit(vdev_scsi);
free(vdev_scsi);
return NULL;
}
static int vdev_scsi_add_iscsi_lun(vhost_scsi_dev_t *vdev_scsi,
char *iscsi_uri, uint32_t lun) {
assert(vdev_scsi);
assert(iscsi_uri);
assert(lun < VUS_MAX_LUNS);
if (vdev_scsi->luns[lun].iscsi_ctx) {
PERR("Lun %d already configured", lun);
return -1;
}
if (iscsi_add_lun(&vdev_scsi->luns[lun], iscsi_uri) != 0) {
return -1;
}
return 0;
}
static int vdev_scsi_run(vhost_scsi_dev_t *vdev_scsi)
{
int cli_sock;
int ret = 0;
assert(vdev_scsi);
assert(vdev_scsi->server_sock >= 0);
assert(vdev_scsi->loop);
cli_sock = accept(vdev_scsi->server_sock, (void *)0, (void *)0);
if (cli_sock < 0) {
perror("accept");
return -1;
}
vu_init(&vdev_scsi->vu_dev,
cli_sock,
vus_panic_cb,
vus_add_watch_cb,
vus_del_watch_cb,
&vus_iface);
if (vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
&vdev_scsi->vu_dev)) {
goto fail;
}
g_main_loop_run(vdev_scsi->loop);
out:
vu_deinit(&vdev_scsi->vu_dev);
return ret;
fail:
ret = -1;
goto out;
}
int main(int argc, char **argv)
{
vhost_scsi_dev_t *vdev_scsi = NULL;
char *unix_fn = NULL;
char *iscsi_uri = NULL;
int opt, err = EXIT_SUCCESS;
while ((opt = getopt(argc, argv, "u:i:")) != -1) {
switch (opt) {
case 'h':
goto help;
case 'u':
unix_fn = strdup(optarg);
break;
case 'i':
iscsi_uri = strdup(optarg);
break;
default:
goto help;
}
}
if (!unix_fn || !iscsi_uri) {
goto help;
}
vdev_scsi = vdev_scsi_new(unix_fn);
if (!vdev_scsi) {
goto err;
}
vhost_scsi_devs[0] = vdev_scsi;
if (vdev_scsi_add_iscsi_lun(vdev_scsi, iscsi_uri, 0) != 0) {
goto err;
}
if (vdev_scsi_run(vdev_scsi) != 0) {
goto err;
}
out:
if (vdev_scsi) {
vdev_scsi_deinit(vdev_scsi);
free(vdev_scsi);
}
if (unix_fn) {
free(unix_fn);
}
if (iscsi_uri) {
free(iscsi_uri);
}
return err;
err:
err = EXIT_FAILURE;
goto out;
help:
fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
argv[0]);
fprintf(stderr, " -u path to unix socket\n");
fprintf(stderr, " -i iscsi uri for lun 0\n");
fprintf(stderr, " -h print help and quit\n");
goto err;
}

View File

@ -43,3 +43,4 @@ CONFIG_VGA=y
CONFIG_VGA_PCI=y
CONFIG_IVSHMEM=$(CONFIG_EVENTFD)
CONFIG_ROCKER=y
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)

View File

@ -1,5 +1,6 @@
CONFIG_PCI=y
CONFIG_VIRTIO_PCI=y
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
CONFIG_VIRTIO=y
CONFIG_SCLPCONSOLE=y
CONFIG_TERMINAL3270=y

116
exec.c
View File

@ -1482,25 +1482,17 @@ static int64_t get_file_size(int fd)
return size;
}
static void *file_ram_alloc(RAMBlock *block,
ram_addr_t memory,
const char *path,
Error **errp)
static int file_ram_open(const char *path,
const char *region_name,
bool *created,
Error **errp)
{
bool unlink_on_error = false;
char *filename;
char *sanitized_name;
char *c;
void *area = MAP_FAILED;
int fd = -1;
int64_t file_size;
if (kvm_enabled() && !kvm_has_sync_mmu()) {
error_setg(errp,
"host lacks kvm mmu notifiers, -mem-path unsupported");
return NULL;
}
*created = false;
for (;;) {
fd = open(path, O_RDWR);
if (fd >= 0) {
@ -1511,13 +1503,13 @@ static void *file_ram_alloc(RAMBlock *block,
/* @path names a file that doesn't exist, create it */
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
if (fd >= 0) {
unlink_on_error = true;
*created = true;
break;
}
} else if (errno == EISDIR) {
/* @path names a directory, create a file there */
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
sanitized_name = g_strdup(memory_region_name(block->mr));
sanitized_name = g_strdup(region_name);
for (c = sanitized_name; *c != '\0'; c++) {
if (*c == '/') {
*c = '_';
@ -1540,7 +1532,7 @@ static void *file_ram_alloc(RAMBlock *block,
error_setg_errno(errp, errno,
"can't open backing store %s for guest RAM",
path);
goto error;
return -1;
}
/*
* Try again on EINTR and EEXIST. The latter happens when
@ -1548,6 +1540,17 @@ static void *file_ram_alloc(RAMBlock *block,
*/
}
return fd;
}
static void *file_ram_alloc(RAMBlock *block,
ram_addr_t memory,
int fd,
bool truncate,
Error **errp)
{
void *area;
block->page_size = qemu_fd_getpagesize(fd);
block->mr->align = block->page_size;
#if defined(__s390x__)
@ -1556,20 +1559,11 @@ static void *file_ram_alloc(RAMBlock *block,
}
#endif
file_size = get_file_size(fd);
if (memory < block->page_size) {
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
"or larger than page size 0x%zx",
memory, block->page_size);
goto error;
}
if (file_size > 0 && file_size < memory) {
error_setg(errp, "backing store %s size 0x%" PRIx64
" does not match 'size' option 0x" RAM_ADDR_FMT,
path, file_size, memory);
goto error;
return NULL;
}
memory = ROUND_UP(memory, block->page_size);
@ -1588,7 +1582,7 @@ static void *file_ram_alloc(RAMBlock *block,
* those labels. Therefore, extending the non-empty backend file
* is disabled as well.
*/
if (!file_size && ftruncate(fd, memory)) {
if (truncate && ftruncate(fd, memory)) {
perror("ftruncate");
}
@ -1597,30 +1591,19 @@ static void *file_ram_alloc(RAMBlock *block,
if (area == MAP_FAILED) {
error_setg_errno(errp, errno,
"unable to map backing store for guest RAM");
goto error;
return NULL;
}
if (mem_prealloc) {
os_mem_prealloc(fd, area, memory, smp_cpus, errp);
if (errp && *errp) {
goto error;
qemu_ram_munmap(area, memory);
return NULL;
}
}
block->fd = fd;
return area;
error:
if (area != MAP_FAILED) {
qemu_ram_munmap(area, memory);
}
if (unlink_on_error) {
unlink(path);
}
if (fd != -1) {
close(fd);
}
return NULL;
}
#endif
@ -1931,18 +1914,25 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
}
#ifdef __linux__
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
bool share, const char *mem_path,
Error **errp)
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
bool share, int fd,
Error **errp)
{
RAMBlock *new_block;
Error *local_err = NULL;
int64_t file_size;
if (xen_enabled()) {
error_setg(errp, "-mem-path not supported with Xen");
return NULL;
}
if (kvm_enabled() && !kvm_has_sync_mmu()) {
error_setg(errp,
"host lacks kvm mmu notifiers, -mem-path unsupported");
return NULL;
}
if (phys_mem_alloc != qemu_anon_ram_alloc) {
/*
* file_ram_alloc() needs to allocate just like
@ -1955,13 +1945,20 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
}
size = HOST_PAGE_ALIGN(size);
file_size = get_file_size(fd);
if (file_size > 0 && file_size < size) {
error_setg(errp, "backing store %s size 0x%" PRIx64
" does not match 'size' option 0x" RAM_ADDR_FMT,
mem_path, file_size, size);
return NULL;
}
new_block = g_malloc0(sizeof(*new_block));
new_block->mr = mr;
new_block->used_length = size;
new_block->max_length = size;
new_block->flags = share ? RAM_SHARED : 0;
new_block->host = file_ram_alloc(new_block, size,
mem_path, errp);
new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp);
if (!new_block->host) {
g_free(new_block);
return NULL;
@ -1974,6 +1971,33 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
return NULL;
}
return new_block;
}
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
bool share, const char *mem_path,
Error **errp)
{
int fd;
bool created;
RAMBlock *block;
fd = file_ram_open(mem_path, memory_region_name(mr), &created, errp);
if (fd < 0) {
return NULL;
}
block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp);
if (!block) {
if (created) {
unlink(mem_path);
}
close(fd);
return NULL;
}
return block;
}
#endif

View File

@ -491,9 +491,9 @@ static void setup_interrupt(IVShmemState *s, int vector, Error **errp)
static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
{
Error *local_err = NULL;
struct stat buf;
size_t size;
void *ptr;
if (s->ivshmem_bar2) {
error_setg(errp, "server sent unexpected shared memory message");
@ -522,15 +522,13 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
}
/* mmap the region and map into the BAR2 */
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
error_setg_errno(errp, errno, "Failed to mmap shared memory");
close(fd);
memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s),
"ivshmem.bar2", size, true, fd, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
"ivshmem.bar2", size, ptr);
memory_region_set_fd(&s->server_bar2, fd);
s->ivshmem_bar2 = &s->server_bar2;
}

View File

@ -11,4 +11,5 @@ obj-$(CONFIG_PSERIES) += spapr_vscsi.o
ifeq ($(CONFIG_VIRTIO),y)
obj-y += virtio-scsi.o virtio-scsi-dataplane.o
obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.o
obj-$(CONFIG_VHOST_USER_SCSI) += vhost-scsi-common.o vhost-user-scsi.o
endif

View File

@ -63,6 +63,7 @@ typedef struct MegasasCmd {
hwaddr pa;
hwaddr pa_size;
uint32_t dcmd_opcode;
union mfi_frame *frame;
SCSIRequest *req;
QEMUSGList qsg;
@ -309,9 +310,11 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
PCIDevice *pcid = PCI_DEVICE(cmd->state);
uint32_t pa_hi = 0, pa_lo;
hwaddr pa;
int frame_sense_len;
if (sense_len > cmd->frame->header.sense_len) {
sense_len = cmd->frame->header.sense_len;
frame_sense_len = cmd->frame->header.sense_len;
if (sense_len > frame_sense_len) {
sense_len = frame_sense_len;
}
if (sense_len) {
pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo);
@ -511,6 +514,7 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
cmd->context &= (uint64_t)0xFFFFFFFF;
}
cmd->count = count;
cmd->dcmd_opcode = -1;
s->busy++;
if (s->consumer_pa) {
@ -605,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s)
static void megasas_abort_command(MegasasCmd *cmd)
{
/* Never abort internal commands. */
if (cmd->dcmd_opcode != -1) {
return;
}
if (cmd->req != NULL) {
scsi_req_cancel(cmd->req);
}
@ -673,15 +680,16 @@ out:
static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
{
dma_addr_t iov_pa, iov_size;
int iov_count;
cmd->flags = le16_to_cpu(cmd->frame->header.flags);
if (!cmd->frame->header.sge_count) {
iov_count = cmd->frame->header.sge_count;
if (!iov_count) {
trace_megasas_dcmd_zero_sge(cmd->index);
cmd->iov_size = 0;
return 0;
} else if (cmd->frame->header.sge_count > 1) {
trace_megasas_dcmd_invalid_sge(cmd->index,
cmd->frame->header.sge_count);
} else if (iov_count > 1) {
trace_megasas_dcmd_invalid_sge(cmd->index, iov_count);
cmd->iov_size = 0;
return -EINVAL;
}
@ -1012,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
uint64_t pd_size;
uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
uint8_t cmdbuf[6];
SCSIRequest *req;
size_t len, resid;
if (!cmd->iov_buf) {
@ -1021,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
info->vpd_page83[0] = 0x7f;
megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
if (!req) {
cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
if (!cmd->req) {
trace_megasas_dcmd_req_alloc_failed(cmd->index,
"PD get info std inquiry");
g_free(cmd->iov_buf);
@ -1031,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
}
trace_megasas_dcmd_internal_submit(cmd->index,
"PD get info std inquiry", lun);
len = scsi_req_enqueue(req);
len = scsi_req_enqueue(cmd->req);
if (len > 0) {
cmd->iov_size = len;
scsi_req_continue(req);
scsi_req_continue(cmd->req);
}
return MFI_STAT_INVALID_STATUS;
} else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
if (!req) {
cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
if (!cmd->req) {
trace_megasas_dcmd_req_alloc_failed(cmd->index,
"PD get info vpd inquiry");
return MFI_STAT_FLASH_ALLOC_FAIL;
}
trace_megasas_dcmd_internal_submit(cmd->index,
"PD get info vpd inquiry", lun);
len = scsi_req_enqueue(req);
len = scsi_req_enqueue(cmd->req);
if (len > 0) {
cmd->iov_size = len;
scsi_req_continue(req);
scsi_req_continue(cmd->req);
}
return MFI_STAT_INVALID_STATUS;
}
@ -1212,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
struct mfi_ld_info *info = cmd->iov_buf;
size_t dcmd_size = sizeof(struct mfi_ld_info);
uint8_t cdb[6];
SCSIRequest *req;
ssize_t len, resid;
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
uint64_t ld_size;
@ -1221,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
cmd->iov_buf = g_malloc0(dcmd_size);
info = cmd->iov_buf;
megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
if (!req) {
cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
if (!cmd->req) {
trace_megasas_dcmd_req_alloc_failed(cmd->index,
"LD get info vpd inquiry");
g_free(cmd->iov_buf);
@ -1231,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
}
trace_megasas_dcmd_internal_submit(cmd->index,
"LD get info vpd inquiry", lun);
len = scsi_req_enqueue(req);
len = scsi_req_enqueue(cmd->req);
if (len > 0) {
cmd->iov_size = len;
scsi_req_continue(req);
scsi_req_continue(cmd->req);
}
return MFI_STAT_INVALID_STATUS;
}
@ -1559,22 +1565,21 @@ static const struct dcmd_cmd_tbl_t {
static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
{
int opcode;
int retval = 0;
size_t len;
const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
trace_megasas_handle_dcmd(cmd->index, opcode);
cmd->dcmd_opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
trace_megasas_handle_dcmd(cmd->index, cmd->dcmd_opcode);
if (megasas_map_dcmd(s, cmd) < 0) {
return MFI_STAT_MEMORY_NOT_AVAILABLE;
}
while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) {
while (cmdptr->opcode != -1 && cmdptr->opcode != cmd->dcmd_opcode) {
cmdptr++;
}
len = cmd->iov_size;
if (cmdptr->opcode == -1) {
trace_megasas_dcmd_unhandled(cmd->index, opcode, len);
trace_megasas_dcmd_unhandled(cmd->index, cmd->dcmd_opcode, len);
retval = megasas_dcmd_dummy(s, cmd);
} else {
trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len);
@ -1587,15 +1592,14 @@ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
}
static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
SCSIRequest *req)
SCSIRequest *req, size_t resid)
{
int opcode;
int retval = MFI_STAT_OK;
int lun = req->lun;
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun);
switch (opcode) {
trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun);
cmd->iov_size -= resid;
switch (cmd->dcmd_opcode) {
case MFI_DCMD_PD_GET_INFO:
retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
break;
@ -1603,7 +1607,7 @@ static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
retval = megasas_ld_get_info_submit(req->dev, lun, cmd);
break;
default:
trace_megasas_dcmd_internal_invalid(cmd->index, opcode);
trace_megasas_dcmd_internal_invalid(cmd->index, cmd->dcmd_opcode);
retval = MFI_STAT_INVALID_DCMD;
break;
}
@ -1647,43 +1651,42 @@ static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write)
}
static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
bool is_logical)
int frame_cmd)
{
uint8_t *cdb;
int target_id, lun_id, cdb_len;
bool is_write;
struct SCSIDevice *sdev = NULL;
bool is_logical = (frame_cmd == MFI_CMD_LD_SCSI_IO);
cdb = cmd->frame->pass.cdb;
target_id = cmd->frame->header.target_id;
lun_id = cmd->frame->header.lun_id;
cdb_len = cmd->frame->header.cdb_len;
if (is_logical) {
if (cmd->frame->header.target_id >= MFI_MAX_LD ||
cmd->frame->header.lun_id != 0) {
if (target_id >= MFI_MAX_LD || lun_id != 0) {
trace_megasas_scsi_target_not_present(
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
cmd->frame->header.target_id, cmd->frame->header.lun_id);
mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id);
return MFI_STAT_DEVICE_NOT_FOUND;
}
}
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
cmd->frame->header.lun_id);
sdev = scsi_device_find(&s->bus, 0, target_id, lun_id);
cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd],
is_logical, cmd->frame->header.target_id,
cmd->frame->header.lun_id, sdev, cmd->iov_size);
trace_megasas_handle_scsi(mfi_frame_desc[frame_cmd], is_logical,
target_id, lun_id, sdev, cmd->iov_size);
if (!sdev || (megasas_is_jbod(s) && is_logical)) {
trace_megasas_scsi_target_not_present(
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
cmd->frame->header.target_id, cmd->frame->header.lun_id);
mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id);
return MFI_STAT_DEVICE_NOT_FOUND;
}
if (cmd->frame->header.cdb_len > 16) {
if (cdb_len > 16) {
trace_megasas_scsi_invalid_cdb_len(
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
cmd->frame->header.target_id, cmd->frame->header.lun_id,
cmd->frame->header.cdb_len);
mfi_frame_desc[frame_cmd], is_logical,
target_id, lun_id, cdb_len);
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
cmd->frame->header.scsi_status = CHECK_CONDITION;
s->event_count++;
@ -1697,12 +1700,10 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
return MFI_STAT_SCSI_DONE_WITH_ERROR;
}
cmd->req = scsi_req_new(sdev, cmd->index,
cmd->frame->header.lun_id, cdb, cmd);
cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cmd);
if (!cmd->req) {
trace_megasas_scsi_req_alloc_failed(
mfi_frame_desc[cmd->frame->header.frame_cmd],
cmd->frame->header.target_id, cmd->frame->header.lun_id);
mfi_frame_desc[frame_cmd], target_id, lun_id);
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
cmd->frame->header.scsi_status = BUSY;
s->event_count++;
@ -1723,43 +1724,41 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
return MFI_STAT_INVALID_STATUS;
}
static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd)
{
uint32_t lba_count, lba_start_hi, lba_start_lo;
uint64_t lba_start;
bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE);
bool is_write = (frame_cmd == MFI_CMD_LD_WRITE);
uint8_t cdb[16];
int len;
struct SCSIDevice *sdev = NULL;
int target_id, lun_id, cdb_len;
lba_count = le32_to_cpu(cmd->frame->io.header.data_len);
lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo);
lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
if (cmd->frame->header.target_id < MFI_MAX_LD &&
cmd->frame->header.lun_id == 0) {
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
cmd->frame->header.lun_id);
target_id = cmd->frame->header.target_id;
lun_id = cmd->frame->header.lun_id;
cdb_len = cmd->frame->header.cdb_len;
if (target_id < MFI_MAX_LD && lun_id == 0) {
sdev = scsi_device_find(&s->bus, 0, target_id, lun_id);
}
trace_megasas_handle_io(cmd->index,
mfi_frame_desc[cmd->frame->header.frame_cmd],
cmd->frame->header.target_id,
cmd->frame->header.lun_id,
mfi_frame_desc[frame_cmd], target_id, lun_id,
(unsigned long)lba_start, (unsigned long)lba_count);
if (!sdev) {
trace_megasas_io_target_not_present(cmd->index,
mfi_frame_desc[cmd->frame->header.frame_cmd],
cmd->frame->header.target_id, cmd->frame->header.lun_id);
mfi_frame_desc[frame_cmd], target_id, lun_id);
return MFI_STAT_DEVICE_NOT_FOUND;
}
if (cmd->frame->header.cdb_len > 16) {
if (cdb_len > 16) {
trace_megasas_scsi_invalid_cdb_len(
mfi_frame_desc[cmd->frame->header.frame_cmd], 1,
cmd->frame->header.target_id, cmd->frame->header.lun_id,
cmd->frame->header.cdb_len);
mfi_frame_desc[frame_cmd], 1, target_id, lun_id, cdb_len);
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
cmd->frame->header.scsi_status = CHECK_CONDITION;
s->event_count++;
@ -1776,11 +1775,10 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
megasas_encode_lba(cdb, lba_start, lba_count, is_write);
cmd->req = scsi_req_new(sdev, cmd->index,
cmd->frame->header.lun_id, cdb, cmd);
lun_id, cdb, cmd);
if (!cmd->req) {
trace_megasas_scsi_req_alloc_failed(
mfi_frame_desc[cmd->frame->header.frame_cmd],
cmd->frame->header.target_id, cmd->frame->header.lun_id);
mfi_frame_desc[frame_cmd], target_id, lun_id);
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
cmd->frame->header.scsi_status = BUSY;
s->event_count++;
@ -1797,23 +1795,11 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
return MFI_STAT_INVALID_STATUS;
}
static int megasas_finish_internal_command(MegasasCmd *cmd,
SCSIRequest *req, size_t resid)
{
int retval = MFI_STAT_INVALID_CMD;
if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
cmd->iov_size -= resid;
retval = megasas_finish_internal_dcmd(cmd, req);
}
return retval;
}
static QEMUSGList *megasas_get_sg_list(SCSIRequest *req)
{
MegasasCmd *cmd = req->hba_private;
if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
if (cmd->dcmd_opcode != -1) {
return NULL;
} else {
return &cmd->qsg;
@ -1824,18 +1810,16 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
{
MegasasCmd *cmd = req->hba_private;
uint8_t *buf;
uint32_t opcode;
trace_megasas_io_complete(cmd->index, len);
if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) {
if (cmd->dcmd_opcode != -1) {
scsi_req_continue(req);
return;
}
buf = scsi_req_get_buf(req);
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
if (cmd->dcmd_opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
struct mfi_pd_info *info = cmd->iov_buf;
if (info->inquiry_data[0] == 0x7f) {
@ -1846,7 +1830,7 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
memcpy(info->vpd_page83, buf, len);
}
scsi_req_continue(req);
} else if (opcode == MFI_DCMD_LD_GET_INFO) {
} else if (cmd->dcmd_opcode == MFI_DCMD_LD_GET_INFO) {
struct mfi_ld_info *info = cmd->iov_buf;
if (cmd->iov_buf) {
@ -1868,11 +1852,11 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status,
return;
}
if (cmd->req == NULL) {
if (cmd->dcmd_opcode != -1) {
/*
* Internal command complete
*/
cmd_status = megasas_finish_internal_command(cmd, req, resid);
cmd_status = megasas_finish_internal_dcmd(cmd, req, resid);
if (cmd_status == MFI_STAT_INVALID_STATUS) {
return;
}
@ -1943,6 +1927,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
{
uint8_t frame_status = MFI_STAT_INVALID_CMD;
uint64_t frame_context;
int frame_cmd;
MegasasCmd *cmd;
/*
@ -1961,7 +1946,8 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
s->event_count++;
return;
}
switch (cmd->frame->header.frame_cmd) {
frame_cmd = cmd->frame->header.frame_cmd;
switch (frame_cmd) {
case MFI_CMD_INIT:
frame_status = megasas_init_firmware(s, cmd);
break;
@ -1972,18 +1958,15 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
frame_status = megasas_handle_abort(s, cmd);
break;
case MFI_CMD_PD_SCSI_IO:
frame_status = megasas_handle_scsi(s, cmd, 0);
break;
case MFI_CMD_LD_SCSI_IO:
frame_status = megasas_handle_scsi(s, cmd, 1);
frame_status = megasas_handle_scsi(s, cmd, frame_cmd);
break;
case MFI_CMD_LD_READ:
case MFI_CMD_LD_WRITE:
frame_status = megasas_handle_io(s, cmd);
frame_status = megasas_handle_io(s, cmd, frame_cmd);
break;
default:
trace_megasas_unhandled_frame_cmd(cmd->index,
cmd->frame->header.frame_cmd);
trace_megasas_unhandled_frame_cmd(cmd->index, frame_cmd);
s->event_count++;
break;
}

View File

@ -16,7 +16,6 @@
*/
#include "qemu/osdep.h"
#include <linux/vhost.h>
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "migration/migration.h"

205
hw/scsi/vhost-user-scsi.c Normal file
View File

@ -0,0 +1,205 @@
/*
* vhost-user-scsi host device
*
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
*
* Author:
* Felipe Franciosi <felipe@nutanix.com>
*
* This work is largely based on the "vhost-scsi" implementation by:
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
* Nicholas Bellinger <nab@risingtidesystems.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/typedefs.h"
#include "qom/object.h"
#include "hw/fw-path-provider.h"
#include "hw/qdev-core.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-backend.h"
#include "hw/virtio/vhost-user-scsi.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-access.h"
#include "chardev/char-fe.h"
/* Features supported by the host application */
static const int user_feature_bits[] = {
VIRTIO_F_NOTIFY_ON_EMPTY,
VIRTIO_RING_F_INDIRECT_DESC,
VIRTIO_RING_F_EVENT_IDX,
VIRTIO_SCSI_F_HOTPLUG,
VHOST_INVALID_FEATURE_BIT
};
static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserSCSI *s = (VHostUserSCSI *)vdev;
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running;
if (vsc->dev.started == start) {
return;
}
if (start) {
int ret;
ret = vhost_scsi_common_start(vsc);
if (ret < 0) {
error_report("unable to start vhost-user-scsi: %s", strerror(-ret));
exit(1);
}
} else {
vhost_scsi_common_stop(vsc);
}
}
static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
}
static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
{
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
Error *err = NULL;
int ret;
if (!vs->conf.chardev.chr) {
error_setg(errp, "vhost-user-scsi: missing chardev");
return;
}
virtio_scsi_common_realize(dev, vhost_dummy_handle_output,
vhost_dummy_handle_output,
vhost_dummy_handle_output, &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
vsc->dev.nvqs = 2 + vs->conf.num_queues;
vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs);
vsc->dev.vq_index = 0;
vsc->dev.backend_features = 0;
ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev,
VHOST_BACKEND_TYPE_USER, 0);
if (ret < 0) {
error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s",
strerror(-ret));
return;
}
/* Channel and lun both are 0 for bootable vhost-user-scsi disk */
vsc->channel = 0;
vsc->lun = 0;
vsc->target = vs->conf.boot_tpgt;
}
static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
/* This will stop the vhost backend. */
vhost_user_scsi_set_status(vdev, 0);
vhost_dev_cleanup(&vsc->dev);
g_free(vsc->dev.vqs);
virtio_scsi_common_unrealize(dev, errp);
}
static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev,
uint64_t features, Error **errp)
{
VHostUserSCSI *s = VHOST_USER_SCSI(vdev);
/* Turn on predefined features supported by this device */
features |= s->host_features;
return vhost_scsi_common_get_features(vdev, features, errp);
}
static Property vhost_user_scsi_properties[] = {
DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev),
DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1),
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
0xFFFF),
DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features,
VIRTIO_SCSI_F_HOTPLUG,
true),
DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features,
VIRTIO_SCSI_F_CHANGE,
true),
DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_vhost_scsi = {
.name = "virtio-scsi",
.minimum_version_id = 1,
.version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_VIRTIO_DEVICE,
VMSTATE_END_OF_LIST()
},
};
static void vhost_user_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
dc->props = vhost_user_scsi_properties;
dc->vmsd = &vmstate_vhost_scsi;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->realize = vhost_user_scsi_realize;
vdc->unrealize = vhost_user_scsi_unrealize;
vdc->get_features = vhost_user_scsi_get_features;
vdc->set_config = vhost_scsi_common_set_config;
vdc->set_status = vhost_user_scsi_set_status;
fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
}
static void vhost_user_scsi_instance_init(Object *obj)
{
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
vsc->feature_bits = user_feature_bits;
/* Add the bootindex property for this object */
device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
DEVICE(vsc), NULL);
}
static const TypeInfo vhost_user_scsi_info = {
.name = TYPE_VHOST_USER_SCSI,
.parent = TYPE_VHOST_SCSI_COMMON,
.instance_size = sizeof(VHostUserSCSI),
.class_init = vhost_user_scsi_class_init,
.instance_init = vhost_user_scsi_instance_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_FW_PATH_PROVIDER },
{ }
},
};
static void virtio_register_types(void)
{
type_register_static(&vhost_user_scsi_info);
}
type_init(virtio_register_types)

View File

@ -2135,6 +2135,61 @@ static const TypeInfo vhost_scsi_pci_info = {
};
#endif
#ifdef CONFIG_LINUX
/* vhost-user-scsi-pci */
static Property vhost_user_scsi_pci_properties[] = {
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
DEV_NVECTORS_UNSPECIFIED),
DEFINE_PROP_END_OF_LIST(),
};
static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
vpci_dev->nvectors = vs->conf.num_queues + 3;
}
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
k->realize = vhost_user_scsi_pci_realize;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->props = vhost_user_scsi_pci_properties;
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
pcidev_k->revision = 0x00;
pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
}
static void vhost_user_scsi_pci_instance_init(Object *obj)
{
VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj);
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
TYPE_VHOST_USER_SCSI);
object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
"bootindex", &error_abort);
}
static const TypeInfo vhost_user_scsi_pci_info = {
.name = TYPE_VHOST_USER_SCSI_PCI,
.parent = TYPE_VIRTIO_PCI,
.instance_size = sizeof(VHostUserSCSIPCI),
.instance_init = vhost_user_scsi_pci_instance_init,
.class_init = vhost_user_scsi_pci_class_init,
};
#endif
/* vhost-vsock-pci */
#ifdef CONFIG_VHOST_VSOCK
@ -2612,6 +2667,9 @@ static void virtio_pci_register_types(void)
#ifdef CONFIG_VHOST_SCSI
type_register_static(&vhost_scsi_pci_info);
#endif
#ifdef CONFIG_LINUX
type_register_static(&vhost_user_scsi_pci_info);
#endif
#ifdef CONFIG_VHOST_VSOCK
type_register_static(&vhost_vsock_pci_info);
#endif

View File

@ -26,6 +26,7 @@
#include "hw/virtio/virtio-input.h"
#include "hw/virtio/virtio-gpu.h"
#include "hw/virtio/virtio-crypto.h"
#include "hw/virtio/vhost-user-scsi.h"
#ifdef CONFIG_VIRTFS
#include "hw/9pfs/virtio-9p.h"
@ -44,6 +45,7 @@ typedef struct VirtIOBalloonPCI VirtIOBalloonPCI;
typedef struct VirtIOSerialPCI VirtIOSerialPCI;
typedef struct VirtIONetPCI VirtIONetPCI;
typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
typedef struct VirtIOInputPCI VirtIOInputPCI;
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
@ -230,6 +232,15 @@ struct VHostSCSIPCI {
};
#endif
#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci"
#define VHOST_USER_SCSI_PCI(obj) \
OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI)
struct VHostUserSCSIPCI {
VirtIOPCIProxy parent_obj;
VHostUserSCSI vdev;
};
/*
* virtio-blk-pci: This extends VirtioPCIProxy.
*/

View File

@ -123,12 +123,8 @@ enum {
* aren't overflowing some other buffer. */
#define NBD_MAX_NAME_SIZE 256
ssize_t nbd_wr_syncv(QIOChannel *ioc,
struct iovec *iov,
size_t niov,
size_t length,
bool do_read,
Error **errp);
ssize_t nbd_rwv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length,
bool do_read, Error **errp);
int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
QCryptoTLSCreds *tlscreds, const char *hostname,
QIOChannel **outioc,
@ -162,7 +158,7 @@ void nbd_client_new(NBDExport *exp,
QIOChannelSocket *sioc,
QCryptoTLSCreds *tlscreds,
const char *tlsaclname,
void (*close)(NBDClient *));
void (*close_fn)(NBDClient *, bool));
void nbd_client_get(NBDClient *client);
void nbd_client_put(NBDClient *client);

View File

@ -456,6 +456,26 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
bool share,
const char *path,
Error **errp);
/**
* memory_region_init_ram_from_fd: Initialize RAM memory region with a
* mmap-ed backend.
*
* @mr: the #MemoryRegion to be initialized.
* @owner: the object that tracks the region's reference count
* @name: the name of the region.
* @size: size of the region.
* @share: %true if memory must be mmaped with the MAP_SHARED flag
* @fd: the fd to mmap.
* @errp: pointer to Error*, to store an error if it happens.
*/
void memory_region_init_ram_from_fd(MemoryRegion *mr,
struct Object *owner,
const char *name,
uint64_t size,
bool share,
int fd,
Error **errp);
#endif
/**
@ -804,17 +824,6 @@ static inline bool memory_region_is_rom(MemoryRegion *mr)
*/
int memory_region_get_fd(MemoryRegion *mr);
/**
* memory_region_set_fd: Mark a RAM memory region as backed by a
* file descriptor.
*
* This function is typically used after memory_region_init_ram_ptr().
*
* @mr: the memory region being queried.
* @fd: the file descriptor that backs @mr.
*/
void memory_region_set_fd(MemoryRegion *mr, int fd);
/**
* memory_region_from_host: Convert a pointer into a RAM memory region
* and an offset within it.

View File

@ -12,17 +12,28 @@
#pragma GCC poison TARGET_CRIS
#pragma GCC poison TARGET_LM32
#pragma GCC poison TARGET_M68K
#pragma GCC poison TARGET_MICROBLAZE
#pragma GCC poison TARGET_MIPS
#pragma GCC poison TARGET_ABI_MIPSO32
#pragma GCC poison TARGET_MIPS64
#pragma GCC poison TARGET_ABI_MIPSN64
#pragma GCC poison TARGET_MOXIE
#pragma GCC poison TARGET_NIOS2
#pragma GCC poison TARGET_OPENRISC
#pragma GCC poison TARGET_PPC
#pragma GCC poison TARGET_PPCEMB
#pragma GCC poison TARGET_PPC64
#pragma GCC poison TARGET_ABI32
#pragma GCC poison TARGET_S390X
#pragma GCC poison TARGET_SH4
#pragma GCC poison TARGET_SPARC
#pragma GCC poison TARGET_SPARC64
#pragma GCC poison TARGET_TRICORE
#pragma GCC poison TARGET_UNICORE32
#pragma GCC poison TARGET_XTENSA
#pragma GCC poison TARGET_NAME
#pragma GCC poison TARGET_SUPPORTS_MTTCG
#pragma GCC poison TARGET_WORDS_BIGENDIAN
#pragma GCC poison BSWAP_NEEDED
@ -50,5 +61,25 @@
#pragma GCC poison CPU_INTERRUPT_TGT_INT_1
#pragma GCC poison CPU_INTERRUPT_TGT_INT_2
#pragma GCC poison CONFIG_ALPHA_DIS
#pragma GCC poison CONFIG_ARM_A64_DIS
#pragma GCC poison CONFIG_ARM_DIS
#pragma GCC poison CONFIG_CRIS_DIS
#pragma GCC poison CONFIG_I386_DIS
#pragma GCC poison CONFIG_LM32_DIS
#pragma GCC poison CONFIG_M68K_DIS
#pragma GCC poison CONFIG_MICROBLAZE_DIS
#pragma GCC poison CONFIG_MIPS_DIS
#pragma GCC poison CONFIG_MOXIE_DIS
#pragma GCC poison CONFIG_NIOS2_DIS
#pragma GCC poison CONFIG_PPC_DIS
#pragma GCC poison CONFIG_S390_DIS
#pragma GCC poison CONFIG_SH4_DIS
#pragma GCC poison CONFIG_SPARC_DIS
#pragma GCC poison CONFIG_XTENSA_DIS
#pragma GCC poison CONFIG_LINUX_USER
#pragma GCC poison CONFIG_VHOST_NET
#endif
#endif

View File

@ -65,6 +65,9 @@ unsigned long last_ram_page(void);
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
bool share, const char *mem_path,
Error **errp);
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
bool share, int fd,
Error **errp);
RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
MemoryRegion *mr, Error **errp);
RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp);

View File

@ -0,0 +1,35 @@
/*
* vhost-user-scsi host device
*
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
*
* Author:
* Felipe Franciosi <felipe@nutanix.com>
*
* This file is largely based on "vhost-scsi.h" by:
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#ifndef VHOST_USER_SCSI_H
#define VHOST_USER_SCSI_H
#include "qemu-common.h"
#include "hw/qdev.h"
#include "hw/virtio/virtio-scsi.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-scsi-common.h"
#define TYPE_VHOST_USER_SCSI "vhost-user-scsi"
#define VHOST_USER_SCSI(obj) \
OBJECT_CHECK(VHostUserSCSI, (obj), TYPE_VHOST_USER_SCSI)
typedef struct VHostUserSCSI {
VHostSCSICommon parent_obj;
uint64_t host_features;
} VHostUserSCSI;
#endif /* VHOST_USER_SCSI_H */

View File

@ -21,6 +21,7 @@
#include "hw/virtio/virtio.h"
#include "hw/pci/pci.h"
#include "hw/scsi/scsi.h"
#include "chardev/char-fe.h"
#include "sysemu/iothread.h"
#define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common"
@ -53,6 +54,7 @@ struct VirtIOSCSIConf {
char *vhostfd;
char *wwpn;
#endif
CharBackend chardev;
uint32_t boot_tpgt;
IOThread *iothread;
};

View File

@ -1397,6 +1397,22 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
mr->ram_block = qemu_ram_alloc_from_file(size, mr, share, path, errp);
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
}
void memory_region_init_ram_from_fd(MemoryRegion *mr,
struct Object *owner,
const char *name,
uint64_t size,
bool share,
int fd,
Error **errp)
{
memory_region_init(mr, owner, name, size);
mr->ram = true;
mr->terminates = true;
mr->destructor = memory_region_destructor_ram;
mr->ram_block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp);
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
}
#endif
void memory_region_init_ram_ptr(MemoryRegion *mr,
@ -1835,16 +1851,6 @@ int memory_region_get_fd(MemoryRegion *mr)
return fd;
}
void memory_region_set_fd(MemoryRegion *mr, int fd)
{
rcu_read_lock();
while (mr->alias) {
mr = mr->alias;
}
mr->ram_block->fd = fd;
rcu_read_unlock();
}
void *memory_region_get_ram_ptr(MemoryRegion *mr)
{
void *ptr;

View File

@ -86,32 +86,6 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
*/
/* Discard length bytes from channel. Return -errno on failure and 0 on
* success*/
static int drop_sync(QIOChannel *ioc, size_t size, Error **errp)
{
ssize_t ret = 0;
char small[1024];
char *buffer;
buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
while (size > 0) {
ssize_t count = MIN(65536, size);
ret = read_sync(ioc, buffer, MIN(65536, size), errp);
if (ret < 0) {
goto cleanup;
}
size -= count;
}
cleanup:
if (buffer != small) {
g_free(buffer);
}
return ret;
}
/* Send an option request.
*
* The request is for option @opt, with @data containing @len bytes of
@ -135,12 +109,12 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
stl_be_p(&req.option, opt);
stl_be_p(&req.length, len);
if (write_sync(ioc, &req, sizeof(req), errp) < 0) {
if (nbd_write(ioc, &req, sizeof(req), errp) < 0) {
error_prepend(errp, "Failed to send option request header");
return -1;
}
if (len && write_sync(ioc, (char *) data, len, errp) < 0) {
if (len && nbd_write(ioc, (char *) data, len, errp) < 0) {
error_prepend(errp, "Failed to send option request data");
return -1;
}
@ -169,7 +143,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
nbd_opt_reply *reply, Error **errp)
{
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
if (read_sync(ioc, reply, sizeof(*reply), errp) < 0) {
if (nbd_read(ioc, reply, sizeof(*reply), errp) < 0) {
error_prepend(errp, "failed to read option reply");
nbd_send_opt_abort(ioc);
return -1;
@ -218,7 +192,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
goto cleanup;
}
msg = g_malloc(reply->length + 1);
if (read_sync(ioc, msg, reply->length, errp) < 0) {
if (nbd_read(ioc, msg, reply->length, errp) < 0) {
error_prepend(errp, "failed to read option error message");
goto cleanup;
}
@ -320,7 +294,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
nbd_send_opt_abort(ioc);
return -1;
}
if (read_sync(ioc, &namelen, sizeof(namelen), errp) < 0) {
if (nbd_read(ioc, &namelen, sizeof(namelen), errp) < 0) {
error_prepend(errp, "failed to read option name length");
nbd_send_opt_abort(ioc);
return -1;
@ -333,7 +307,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
return -1;
}
if (namelen != strlen(want)) {
if (drop_sync(ioc, len, errp) < 0) {
if (nbd_drop(ioc, len, errp) < 0) {
error_prepend(errp, "failed to skip export name with wrong length");
nbd_send_opt_abort(ioc);
return -1;
@ -342,14 +316,14 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
}
assert(namelen < sizeof(name));
if (read_sync(ioc, name, namelen, errp) < 0) {
if (nbd_read(ioc, name, namelen, errp) < 0) {
error_prepend(errp, "failed to read export name");
nbd_send_opt_abort(ioc);
return -1;
}
name[namelen] = '\0';
len -= namelen;
if (drop_sync(ioc, len, errp) < 0) {
if (nbd_drop(ioc, len, errp) < 0) {
error_prepend(errp, "failed to read export description");
nbd_send_opt_abort(ioc);
return -1;
@ -476,7 +450,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
goto fail;
}
if (read_sync(ioc, buf, 8, errp) < 0) {
if (nbd_read(ioc, buf, 8, errp) < 0) {
error_prepend(errp, "Failed to read data");
goto fail;
}
@ -502,7 +476,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
goto fail;
}
if (read_sync(ioc, &magic, sizeof(magic), errp) < 0) {
if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) {
error_prepend(errp, "Failed to read magic");
goto fail;
}
@ -514,7 +488,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
uint16_t globalflags;
bool fixedNewStyle = false;
if (read_sync(ioc, &globalflags, sizeof(globalflags), errp) < 0) {
if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) {
error_prepend(errp, "Failed to read server flags");
goto fail;
}
@ -532,7 +506,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
}
/* client requested flags */
clientflags = cpu_to_be32(clientflags);
if (write_sync(ioc, &clientflags, sizeof(clientflags), errp) < 0) {
if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) {
error_prepend(errp, "Failed to send clientflags field");
goto fail;
}
@ -570,13 +544,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
}
/* Read the response */
if (read_sync(ioc, &s, sizeof(s), errp) < 0) {
if (nbd_read(ioc, &s, sizeof(s), errp) < 0) {
error_prepend(errp, "Failed to read export length");
goto fail;
}
*size = be64_to_cpu(s);
if (read_sync(ioc, flags, sizeof(*flags), errp) < 0) {
if (nbd_read(ioc, flags, sizeof(*flags), errp) < 0) {
error_prepend(errp, "Failed to read export flags");
goto fail;
}
@ -593,14 +567,14 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
goto fail;
}
if (read_sync(ioc, &s, sizeof(s), errp) < 0) {
if (nbd_read(ioc, &s, sizeof(s), errp) < 0) {
error_prepend(errp, "Failed to read export length");
goto fail;
}
*size = be64_to_cpu(s);
TRACE("Size is %" PRIu64, *size);
if (read_sync(ioc, &oldflags, sizeof(oldflags), errp) < 0) {
if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) {
error_prepend(errp, "Failed to read export flags");
goto fail;
}
@ -616,7 +590,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
}
TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags);
if (zeroes && drop_sync(ioc, 124, errp) < 0) {
if (zeroes && nbd_drop(ioc, 124, errp) < 0) {
error_prepend(errp, "Failed to read reserved block");
goto fail;
}
@ -759,7 +733,7 @@ ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request)
stq_be_p(buf + 16, request->from);
stl_be_p(buf + 24, request->len);
return write_sync(ioc, buf, sizeof(buf), NULL);
return nbd_write(ioc, buf, sizeof(buf), NULL);
}
ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
@ -768,7 +742,7 @@ ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
uint32_t magic;
ssize_t ret;
ret = read_sync_eof(ioc, buf, sizeof(buf), errp);
ret = nbd_read_eof(ioc, buf, sizeof(buf), errp);
if (ret <= 0) {
return ret;
}

View File

@ -24,12 +24,8 @@
* The function may be called from coroutine or from non-coroutine context.
* When called from non-coroutine context @ioc must be in blocking mode.
*/
ssize_t nbd_wr_syncv(QIOChannel *ioc,
struct iovec *iov,
size_t niov,
size_t length,
bool do_read,
Error **errp)
ssize_t nbd_rwv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length,
bool do_read, Error **errp)
{
ssize_t done = 0;
struct iovec *local_iov = g_new(struct iovec, niov);
@ -69,6 +65,32 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
return done;
}
/* Discard length bytes from channel. Return -errno on failure and 0 on
* success */
int nbd_drop(QIOChannel *ioc, size_t size, Error **errp)
{
ssize_t ret = 0;
char small[1024];
char *buffer;
buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
while (size > 0) {
ssize_t count = MIN(65536, size);
ret = nbd_read(ioc, buffer, MIN(65536, size), errp);
if (ret < 0) {
goto cleanup;
}
size -= count;
}
cleanup:
if (buffer != small) {
g_free(buffer);
}
return ret;
}
void nbd_tls_handshake(QIOTask *task,
void *opaque)

View File

@ -94,14 +94,14 @@
#define NBD_ENOSPC 28
#define NBD_ESHUTDOWN 108
/* read_sync_eof
/* nbd_read_eof
* Tries to read @size bytes from @ioc. Returns number of bytes actually read.
* May return a value >= 0 and < size only on EOF, i.e. when iteratively called
* qio_channel_readv() returns 0. So, there are no needs to call read_sync_eof
* qio_channel_readv() returns 0. So, there is no need to call nbd_read_eof
* iteratively.
*/
static inline ssize_t read_sync_eof(QIOChannel *ioc, void *buffer, size_t size,
Error **errp)
static inline ssize_t nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size,
Error **errp)
{
struct iovec iov = { .iov_base = buffer, .iov_len = size };
/* Sockets are kept in blocking mode in the negotiation phase. After
@ -109,16 +109,16 @@ static inline ssize_t read_sync_eof(QIOChannel *ioc, void *buffer, size_t size,
* our request/reply. Synchronization is done with recv_coroutine, so
* that this is coroutine-safe.
*/
return nbd_wr_syncv(ioc, &iov, 1, size, true, errp);
return nbd_rwv(ioc, &iov, 1, size, true, errp);
}
/* read_sync
/* nbd_read
* Reads @size bytes from @ioc. Returns 0 on success.
*/
static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size,
Error **errp)
static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size,
Error **errp)
{
ssize_t ret = read_sync_eof(ioc, buffer, size, errp);
ssize_t ret = nbd_read_eof(ioc, buffer, size, errp);
if (ret >= 0 && ret != size) {
ret = -EINVAL;
@ -128,15 +128,15 @@ static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size,
return ret < 0 ? ret : 0;
}
/* write_sync
/* nbd_write
* Writes @size bytes to @ioc. Returns 0 on success.
*/
static inline int write_sync(QIOChannel *ioc, const void *buffer, size_t size,
Error **errp)
static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size,
Error **errp)
{
struct iovec iov = { .iov_base = (void *) buffer, .iov_len = size };
ssize_t ret = nbd_wr_syncv(ioc, &iov, 1, size, false, errp);
ssize_t ret = nbd_rwv(ioc, &iov, 1, size, false, errp);
assert(ret < 0 || ret == size);
@ -153,4 +153,6 @@ struct NBDTLSHandshakeData {
void nbd_tls_handshake(QIOTask *task,
void *opaque);
int nbd_drop(QIOChannel *ioc, size_t size, Error **errp);
#endif

View File

@ -81,7 +81,7 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
struct NBDClient {
int refcount;
void (*close)(NBDClient *client);
void (*close_fn)(NBDClient *client, bool negotiated);
bool no_zeroes;
NBDExport *exp;
@ -104,69 +104,6 @@ struct NBDClient {
static void nbd_client_receive_next_request(NBDClient *client);
static gboolean nbd_negotiate_continue(QIOChannel *ioc,
GIOCondition condition,
void *opaque)
{
qemu_coroutine_enter(opaque);
return TRUE;
}
static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
{
ssize_t ret;
guint watch;
assert(qemu_in_coroutine());
/* Negotiation are always in main loop. */
watch = qio_channel_add_watch(ioc,
G_IO_IN,
nbd_negotiate_continue,
qemu_coroutine_self(),
NULL);
ret = read_sync(ioc, buffer, size, NULL);
g_source_remove(watch);
return ret;
}
static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size)
{
ssize_t ret;
guint watch;
assert(qemu_in_coroutine());
/* Negotiation are always in main loop. */
watch = qio_channel_add_watch(ioc,
G_IO_OUT,
nbd_negotiate_continue,
qemu_coroutine_self(),
NULL);
ret = write_sync(ioc, buffer, size, NULL);
g_source_remove(watch);
return ret;
}
static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
{
ssize_t ret;
uint8_t *buffer = g_malloc(MIN(65536, size));
while (size > 0) {
size_t count = MIN(65536, size);
ret = nbd_negotiate_read(ioc, buffer, count);
if (ret < 0) {
g_free(buffer);
return ret;
}
size -= count;
}
g_free(buffer);
return 0;
}
/* Basic flow for negotiation
Server Client
@ -205,22 +142,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
type, opt, len);
magic = cpu_to_be64(NBD_REP_MAGIC);
if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) {
if (nbd_write(ioc, &magic, sizeof(magic), NULL) < 0) {
LOG("write failed (rep magic)");
return -EINVAL;
}
opt = cpu_to_be32(opt);
if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) {
if (nbd_write(ioc, &opt, sizeof(opt), NULL) < 0) {
LOG("write failed (rep opt)");
return -EINVAL;
}
type = cpu_to_be32(type);
if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) {
if (nbd_write(ioc, &type, sizeof(type), NULL) < 0) {
LOG("write failed (rep type)");
return -EINVAL;
}
len = cpu_to_be32(len);
if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
if (nbd_write(ioc, &len, sizeof(len), NULL) < 0) {
LOG("write failed (rep data length)");
return -EINVAL;
}
@ -255,7 +192,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
if (ret < 0) {
goto out;
}
if (nbd_negotiate_write(ioc, msg, len) < 0) {
if (nbd_write(ioc, msg, len, NULL) < 0) {
LOG("write failed (error message)");
ret = -EIO;
} else {
@ -274,27 +211,27 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
uint32_t len;
const char *name = exp->name ? exp->name : "";
const char *desc = exp->description ? exp->description : "";
int rc;
int ret;
TRACE("Advertising export name '%s' description '%s'", name, desc);
name_len = strlen(name);
desc_len = strlen(desc);
len = name_len + desc_len + sizeof(len);
rc = nbd_negotiate_send_rep_len(ioc, NBD_REP_SERVER, NBD_OPT_LIST, len);
if (rc < 0) {
return rc;
ret = nbd_negotiate_send_rep_len(ioc, NBD_REP_SERVER, NBD_OPT_LIST, len);
if (ret < 0) {
return ret;
}
len = cpu_to_be32(name_len);
if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
if (nbd_write(ioc, &len, sizeof(len), NULL) < 0) {
LOG("write failed (name length)");
return -EINVAL;
}
if (nbd_negotiate_write(ioc, name, name_len) < 0) {
if (nbd_write(ioc, name, name_len, NULL) < 0) {
LOG("write failed (name buffer)");
return -EINVAL;
}
if (nbd_negotiate_write(ioc, desc, desc_len) < 0) {
if (nbd_write(ioc, desc, desc_len, NULL) < 0) {
LOG("write failed (description buffer)");
return -EINVAL;
}
@ -308,7 +245,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
NBDExport *exp;
if (length) {
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
if (nbd_drop(client->ioc, length, NULL) < 0) {
return -EIO;
}
return nbd_negotiate_send_rep_err(client->ioc,
@ -328,7 +265,6 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
{
int rc = -EINVAL;
char name[NBD_MAX_NAME_SIZE + 1];
/* Client sends:
@ -337,11 +273,11 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
TRACE("Checking length");
if (length >= sizeof(name)) {
LOG("Bad length received");
goto fail;
return -EINVAL;
}
if (nbd_negotiate_read(client->ioc, name, length) < 0) {
if (nbd_read(client->ioc, name, length, NULL) < 0) {
LOG("read failed");
goto fail;
return -EINVAL;
}
name[length] = '\0';
@ -350,14 +286,13 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
client->exp = nbd_export_find(name);
if (!client->exp) {
LOG("export not found");
goto fail;
return -EINVAL;
}
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
nbd_export_get(client->exp);
rc = 0;
fail:
return rc;
return 0;
}
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
@ -372,7 +307,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
TRACE("Setting up TLS");
ioc = client->ioc;
if (length) {
if (nbd_negotiate_drop_sync(ioc, length) < 0) {
if (nbd_drop(ioc, length, NULL) < 0) {
return NULL;
}
nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS,
@ -436,7 +371,7 @@ static int nbd_negotiate_options(NBDClient *client)
... Rest of request
*/
if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) {
if (nbd_read(client->ioc, &flags, sizeof(flags), NULL) < 0) {
LOG("read failed");
return -EIO;
}
@ -462,7 +397,7 @@ static int nbd_negotiate_options(NBDClient *client)
uint32_t clientflags, length;
uint64_t magic;
if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) {
if (nbd_read(client->ioc, &magic, sizeof(magic), NULL) < 0) {
LOG("read failed");
return -EINVAL;
}
@ -472,15 +407,15 @@ static int nbd_negotiate_options(NBDClient *client)
return -EINVAL;
}
if (nbd_negotiate_read(client->ioc, &clientflags,
sizeof(clientflags)) < 0)
if (nbd_read(client->ioc, &clientflags,
sizeof(clientflags), NULL) < 0)
{
LOG("read failed");
return -EINVAL;
}
clientflags = be32_to_cpu(clientflags);
if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) {
if (nbd_read(client->ioc, &length, sizeof(length), NULL) < 0) {
LOG("read failed");
return -EINVAL;
}
@ -510,7 +445,7 @@ static int nbd_negotiate_options(NBDClient *client)
return -EINVAL;
default:
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
if (nbd_drop(client->ioc, length, NULL) < 0) {
return -EIO;
}
ret = nbd_negotiate_send_rep_err(client->ioc,
@ -548,7 +483,7 @@ static int nbd_negotiate_options(NBDClient *client)
return nbd_negotiate_handle_export_name(client, length);
case NBD_OPT_STARTTLS:
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
if (nbd_drop(client->ioc, length, NULL) < 0) {
return -EIO;
}
if (client->tlscreds) {
@ -567,7 +502,7 @@ static int nbd_negotiate_options(NBDClient *client)
}
break;
default:
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
if (nbd_drop(client->ioc, length, NULL) < 0) {
return -EIO;
}
ret = nbd_negotiate_send_rep_err(client->ioc,
@ -598,16 +533,10 @@ static int nbd_negotiate_options(NBDClient *client)
}
}
typedef struct {
NBDClient *client;
Coroutine *co;
} NBDClientNewData;
static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
static coroutine_fn int nbd_negotiate(NBDClient *client)
{
NBDClient *client = data->client;
char buf[8 + 8 + 8 + 128];
int rc;
int ret;
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
NBD_FLAG_SEND_WRITE_ZEROES);
@ -633,7 +562,6 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
*/
qio_channel_set_blocking(client->ioc, false, NULL);
rc = -EINVAL;
TRACE("Beginning negotiation.");
memset(buf, 0, sizeof(buf));
@ -654,21 +582,21 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
if (oldStyle) {
if (client->tlscreds) {
TRACE("TLS cannot be enabled with oldstyle protocol");
goto fail;
return -EINVAL;
}
if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) {
if (nbd_write(client->ioc, buf, sizeof(buf), NULL) < 0) {
LOG("write failed");
goto fail;
return -EINVAL;
}
} else {
if (nbd_negotiate_write(client->ioc, buf, 18) < 0) {
if (nbd_write(client->ioc, buf, 18, NULL) < 0) {
LOG("write failed");
goto fail;
return -EINVAL;
}
rc = nbd_negotiate_options(client);
if (rc != 0) {
ret = nbd_negotiate_options(client);
if (ret != 0) {
LOG("option negotiation failed");
goto fail;
return ret;
}
TRACE("advertising size %" PRIu64 " and flags %x",
@ -676,25 +604,25 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
stq_be_p(buf + 18, client->exp->size);
stw_be_p(buf + 26, client->exp->nbdflags | myflags);
len = client->no_zeroes ? 10 : sizeof(buf) - 18;
if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) {
ret = nbd_write(client->ioc, buf + 18, len, NULL);
if (ret < 0) {
LOG("write failed");
goto fail;
return ret;
}
}
TRACE("Negotiation succeeded.");
rc = 0;
fail:
return rc;
return 0;
}
static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
static int nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
{
uint8_t buf[NBD_REQUEST_SIZE];
uint32_t magic;
ssize_t ret;
int ret;
ret = read_sync(ioc, buf, sizeof(buf), NULL);
ret = nbd_read(ioc, buf, sizeof(buf), NULL);
if (ret < 0) {
return ret;
}
@ -726,7 +654,7 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
return 0;
}
static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply)
static int nbd_send_reply(QIOChannel *ioc, NBDReply *reply)
{
uint8_t buf[NBD_REPLY_SIZE];
@ -745,7 +673,7 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply)
stl_be_p(buf + 4, reply->error);
stq_be_p(buf + 8, reply->handle);
return write_sync(ioc, buf, sizeof(buf), NULL);
return nbd_write(ioc, buf, sizeof(buf), NULL);
}
#define MAX_NBD_REQUESTS 16
@ -778,7 +706,7 @@ void nbd_client_put(NBDClient *client)
}
}
static void client_close(NBDClient *client)
static void client_close(NBDClient *client, bool negotiated)
{
if (client->closing) {
return;
@ -793,8 +721,8 @@ static void client_close(NBDClient *client)
NULL);
/* Also tell the client, so that they release their reference. */
if (client->close) {
client->close(client);
if (client->close_fn) {
client->close_fn(client, negotiated);
}
}
@ -975,7 +903,7 @@ void nbd_export_close(NBDExport *exp)
nbd_export_get(exp);
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
client_close(client);
client_close(client, true);
}
nbd_export_set_name(exp, NULL);
nbd_export_set_description(exp, NULL);
@ -1032,25 +960,24 @@ void nbd_export_close_all(void)
}
}
static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply,
int len)
static int nbd_co_send_reply(NBDRequestData *req, NBDReply *reply, int len)
{
NBDClient *client = req->client;
ssize_t rc, ret;
int ret;
g_assert(qemu_in_coroutine());
qemu_co_mutex_lock(&client->send_lock);
client->send_coroutine = qemu_coroutine_self();
if (!len) {
rc = nbd_send_reply(client->ioc, reply);
ret = nbd_send_reply(client->ioc, reply);
} else {
qio_channel_set_cork(client->ioc, true);
rc = nbd_send_reply(client->ioc, reply);
if (rc >= 0) {
ret = write_sync(client->ioc, req->data, len, NULL);
ret = nbd_send_reply(client->ioc, reply);
if (ret == 0) {
ret = nbd_write(client->ioc, req->data, len, NULL);
if (ret < 0) {
rc = -EIO;
ret = -EIO;
}
}
qio_channel_set_cork(client->ioc, false);
@ -1058,28 +985,23 @@ static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply,
client->send_coroutine = NULL;
qemu_co_mutex_unlock(&client->send_lock);
return rc;
return ret;
}
/* Collect a client request. Return 0 if request looks valid, -EAGAIN
* to keep trying the collection, -EIO to drop connection right away,
* and any other negative value to report an error to the client
* (although the caller may still need to disconnect after reporting
* the error). */
static ssize_t nbd_co_receive_request(NBDRequestData *req,
NBDRequest *request)
/* nbd_co_receive_request
* Collect a client request. Return 0 if request looks valid, -EIO to drop
* connection right away, and any other negative value to report an error to
* the client (although the caller may still need to disconnect after reporting
* the error).
*/
static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request)
{
NBDClient *client = req->client;
ssize_t rc;
g_assert(qemu_in_coroutine());
assert(client->recv_coroutine == qemu_coroutine_self());
rc = nbd_receive_request(client->ioc, request);
if (rc < 0) {
if (rc != -EAGAIN) {
rc = -EIO;
}
goto out;
if (nbd_receive_request(client->ioc, request) < 0) {
return -EIO;
}
TRACE("Decoding type");
@ -1093,8 +1015,7 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
/* Special case: we're going to disconnect without a reply,
* whether or not flags, from, or len are bogus */
TRACE("Request type is DISCONNECT");
rc = -EIO;
goto out;
return -EIO;
}
/* Check for sanity in the parameters, part 1. Defer as many
@ -1102,31 +1023,27 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
* payload, so we can try and keep the connection alive. */
if ((request->from + request->len) < request->from) {
LOG("integer overflow detected, you're probably being attacked");
rc = -EINVAL;
goto out;
return -EINVAL;
}
if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) {
if (request->len > NBD_MAX_BUFFER_SIZE) {
LOG("len (%" PRIu32" ) is larger than max len (%u)",
request->len, NBD_MAX_BUFFER_SIZE);
rc = -EINVAL;
goto out;
return -EINVAL;
}
req->data = blk_try_blockalign(client->exp->blk, request->len);
if (req->data == NULL) {
rc = -ENOMEM;
goto out;
return -ENOMEM;
}
}
if (request->type == NBD_CMD_WRITE) {
TRACE("Reading %" PRIu32 " byte(s)", request->len);
if (read_sync(client->ioc, req->data, request->len, NULL) < 0) {
if (nbd_read(client->ioc, req->data, request->len, NULL) < 0) {
LOG("reading from socket failed");
rc = -EIO;
goto out;
return -EIO;
}
req->complete = true;
}
@ -1136,28 +1053,19 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
LOG("operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
", Size: %" PRIu64, request->from, request->len,
(uint64_t)client->exp->size);
rc = request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL;
goto out;
return request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL;
}
if (request->flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) {
LOG("unsupported flags (got 0x%x)", request->flags);
rc = -EINVAL;
goto out;
return -EINVAL;
}
if (request->type != NBD_CMD_WRITE_ZEROES &&
(request->flags & NBD_CMD_FLAG_NO_HOLE)) {
LOG("unexpected flags (got 0x%x)", request->flags);
rc = -EINVAL;
goto out;
return -EINVAL;
}
rc = 0;
out:
client->recv_coroutine = NULL;
nbd_client_receive_next_request(client);
return rc;
return 0;
}
/* Owns a reference to the NBDClient passed as opaque. */
@ -1168,8 +1076,9 @@ static coroutine_fn void nbd_trip(void *opaque)
NBDRequestData *req;
NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
NBDReply reply;
ssize_t ret;
int ret;
int flags;
int reply_data_len = 0;
TRACE("Reading request.");
if (client->closing) {
@ -1179,11 +1088,10 @@ static coroutine_fn void nbd_trip(void *opaque)
req = nbd_request_get(client);
ret = nbd_co_receive_request(req, &request);
if (ret == -EAGAIN) {
goto done;
}
client->recv_coroutine = NULL;
nbd_client_receive_next_request(client);
if (ret == -EIO) {
goto out;
goto disconnect;
}
reply.handle = request.handle;
@ -1191,7 +1099,7 @@ static coroutine_fn void nbd_trip(void *opaque)
if (ret < 0) {
reply.error = -ret;
goto error_reply;
goto reply;
}
if (client->closing) {
@ -1212,7 +1120,7 @@ static coroutine_fn void nbd_trip(void *opaque)
if (ret < 0) {
LOG("flush failed");
reply.error = -ret;
goto error_reply;
break;
}
}
@ -1221,12 +1129,12 @@ static coroutine_fn void nbd_trip(void *opaque)
if (ret < 0) {
LOG("reading from file failed");
reply.error = -ret;
goto error_reply;
break;
}
reply_data_len = request.len;
TRACE("Read %" PRIu32" byte(s)", request.len);
if (nbd_co_send_reply(req, &reply, request.len) < 0)
goto out;
break;
case NBD_CMD_WRITE:
TRACE("Request type is WRITE");
@ -1234,7 +1142,7 @@ static coroutine_fn void nbd_trip(void *opaque)
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
TRACE("Server is read-only, return error");
reply.error = EROFS;
goto error_reply;
break;
}
TRACE("Writing to device");
@ -1248,21 +1156,16 @@ static coroutine_fn void nbd_trip(void *opaque)
if (ret < 0) {
LOG("writing to file failed");
reply.error = -ret;
goto error_reply;
}
if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
}
break;
case NBD_CMD_WRITE_ZEROES:
TRACE("Request type is WRITE_ZEROES");
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
TRACE("Server is read-only, return error");
reply.error = EROFS;
goto error_reply;
break;
}
TRACE("Writing to device");
@ -1279,14 +1182,9 @@ static coroutine_fn void nbd_trip(void *opaque)
if (ret < 0) {
LOG("writing to file failed");
reply.error = -ret;
goto error_reply;
}
if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
}
break;
case NBD_CMD_DISC:
/* unreachable, thanks to special case in nbd_co_receive_request() */
abort();
@ -1299,9 +1197,7 @@ static coroutine_fn void nbd_trip(void *opaque)
LOG("flush failed");
reply.error = -ret;
}
if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
}
break;
case NBD_CMD_TRIM:
TRACE("Request type is TRIM");
@ -1311,21 +1207,19 @@ static coroutine_fn void nbd_trip(void *opaque)
LOG("discard failed");
reply.error = -ret;
}
if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
}
break;
default:
LOG("invalid request type (%" PRIu32 ") received", request.type);
reply.error = EINVAL;
error_reply:
/* We must disconnect after NBD_CMD_WRITE if we did not
* read the payload.
*/
if (nbd_co_send_reply(req, &reply, 0) < 0 || !req->complete) {
goto out;
}
break;
}
reply:
/* We must disconnect after NBD_CMD_WRITE if we did not
* read the payload.
*/
if (nbd_co_send_reply(req, &reply, reply_data_len) < 0 || !req->complete) {
goto disconnect;
}
TRACE("Request/Reply complete");
@ -1335,9 +1229,9 @@ done:
nbd_client_put(client);
return;
out:
disconnect:
nbd_request_put(req);
client_close(client);
client_close(client, true);
nbd_client_put(client);
}
@ -1352,8 +1246,7 @@ static void nbd_client_receive_next_request(NBDClient *client)
static coroutine_fn void nbd_co_client_start(void *opaque)
{
NBDClientNewData *data = opaque;
NBDClient *client = data->client;
NBDClient *client = opaque;
NBDExport *exp = client->exp;
if (exp) {
@ -1362,25 +1255,28 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
}
qemu_co_mutex_init(&client->send_lock);
if (nbd_negotiate(data)) {
client_close(client);
goto out;
if (nbd_negotiate(client)) {
client_close(client, false);
return;
}
nbd_client_receive_next_request(client);
out:
g_free(data);
}
/*
* Create a new client listener on the given export @exp, using the
* given channel @sioc. Begin servicing it in a coroutine. When the
* connection closes, call @close_fn with an indication of whether the
* client completed negotiation.
*/
void nbd_client_new(NBDExport *exp,
QIOChannelSocket *sioc,
QCryptoTLSCreds *tlscreds,
const char *tlsaclname,
void (*close_fn)(NBDClient *))
void (*close_fn)(NBDClient *, bool))
{
NBDClient *client;
NBDClientNewData *data = g_new(NBDClientNewData, 1);
Coroutine *co;
client = g_malloc0(sizeof(NBDClient));
client->refcount = 1;
@ -1394,9 +1290,8 @@ void nbd_client_new(NBDExport *exp,
object_ref(OBJECT(client->sioc));
client->ioc = QIO_CHANNEL(sioc);
object_ref(OBJECT(client->ioc));
client->close = close_fn;
client->close_fn = close_fn;
data->client = client;
data->co = qemu_coroutine_create(nbd_co_client_start, data);
qemu_coroutine_enter(data->co);
co = qemu_coroutine_create(nbd_co_client_start, client);
qemu_coroutine_enter(co);
}

View File

@ -1,11 +1,12 @@
\input texinfo @c -*- texinfo -*-
@c %**start of header
@setfilename qemu-doc.info
@include version.texi
@documentlanguage en
@documentencoding UTF-8
@settitle QEMU Emulator User Documentation
@settitle QEMU version @value{VERSION} User Documentation
@exampleindent 0
@paragraphindent 0
@c %**end of header
@ -19,7 +20,7 @@
@iftex
@titlepage
@sp 7
@center @titlefont{QEMU Emulator}
@center @titlefont{QEMU version @value{VERSION}}
@sp 1
@center @titlefont{User Documentation}
@sp 3

View File

@ -336,10 +336,10 @@ static void nbd_export_closed(NBDExport *exp)
static void nbd_update_server_watch(void);
static void nbd_client_closed(NBDClient *client)
static void nbd_client_closed(NBDClient *client, bool negotiated)
{
nb_fds--;
if (nb_fds == 0 && !persistent && state == RUNNING) {
if (negotiated && nb_fds == 0 && !persistent && state == RUNNING) {
state = TERMINATE;
}
nbd_update_server_watch();
@ -581,6 +581,10 @@ int main(int argc, char **argv)
sa_sigterm.sa_handler = termsig_handler;
sigaction(SIGTERM, &sa_sigterm, NULL);
#ifdef CONFIG_POSIX
signal(SIGPIPE, SIG_IGN);
#endif
module_call_init(MODULE_INIT_TRACE);
qcrypto_init(&error_fatal);

View File

@ -377,7 +377,7 @@ define unnest-vars
endef
TEXI2MAN = $(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl -I docs $< $@.pod && \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $(TEXI2PODFLAGS) $< $@.pod && \
$(POD2MAN) --section=$(subst .,,$(suffix $@)) --center=" " --release=" " $@.pod > $@, \
"GEN","$@")

View File

@ -514,9 +514,10 @@ static int hax_vcpu_hax_exec(CPUArchState *env)
hax_vcpu_interrupt(env);
qemu_mutex_unlock_iothread();
cpu_exec_start(cpu);
hax_ret = hax_vcpu_run(vcpu);
cpu_exec_end(cpu);
qemu_mutex_lock_iothread();
current_cpu = cpu;
/* Simply continue the vcpu_run if system call interrupted */
if (hax_ret == -EINTR || hax_ret == -EAGAIN) {

View File

View File

@ -205,6 +205,8 @@ check-qtest-pci-y += tests/intel-hda-test$(EXESUF)
gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c
check-qtest-pci-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
gcov-files-pci-y += hw/misc/ivshmem.c
check-qtest-pci-y += tests/megasas-test$(EXESUF)
gcov-files-pci-y += hw/scsi/megasas.c
check-qtest-i386-y = tests/endianness-test$(EXESUF)
check-qtest-i386-y += tests/fdc-test$(EXESUF)
@ -755,6 +757,7 @@ tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y)
tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y)
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
tests/megasas-test$(EXESUF): tests/megasas-test.o $(libqos-spapr-obj-y) $(libqos-pc-obj-y)
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y)
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o

86
tests/megasas-test.c Normal file
View File

@ -0,0 +1,86 @@
/*
* QTest testcase for LSI MegaRAID
*
* Copyright (c) 2017 Red Hat Inc.
*
* 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/osdep.h"
#include "libqtest.h"
#include "qemu/bswap.h"
#include "libqos/libqos-pc.h"
#include "libqos/libqos-spapr.h"
static QOSState *qmegasas_start(const char *extra_opts)
{
const char *arch = qtest_get_arch();
const char *cmd = "-drive id=hd0,if=none,file=null-co://,format=raw "
"-device megasas,id=scsi0,addr=04.0 "
"-device scsi-hd,bus=scsi0.0,drive=hd0 %s";
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
return qtest_pc_boot(cmd, extra_opts ? : "");
}
g_printerr("virtio-scsi tests are only available on x86 or ppc64\n");
exit(EXIT_FAILURE);
}
static void qmegasas_stop(QOSState *qs)
{
qtest_shutdown(qs);
}
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
QOSState *qs;
qs = qmegasas_start(NULL);
qmegasas_stop(qs);
}
/* This used to cause a NULL pointer dereference. */
static void megasas_pd_get_info_fuzz(void)
{
QPCIDevice *dev;
QOSState *qs;
QPCIBar bar;
uint32_t context[256];
uint64_t context_pa;
int i;
qs = qmegasas_start(NULL);
dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0));
g_assert(dev != NULL);
qpci_device_enable(dev);
bar = qpci_iomap(dev, 0, NULL);
memset(context, 0, sizeof(context));
context[0] = cpu_to_le32(0x05050505);
context[1] = cpu_to_le32(0x01010101);
for (i = 2; i < ARRAY_SIZE(context); i++) {
context[i] = cpu_to_le32(0x41414141);
}
context[6] = cpu_to_le32(0x02020000);
context[7] = cpu_to_le32(0);
context_pa = qmalloc(qs, sizeof(context));
memwrite(context_pa, context, sizeof(context));
qpci_io_writel(dev, bar, 0x40, context_pa);
g_free(dev);
qmegasas_stop(qs);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/megasas/pci/nop", pci_nop);
qtest_add_func("/megasas/dcmd/pd-get-info/fuzz", megasas_pd_get_info_fuzz);
return g_test_run();
}

View File

@ -55,28 +55,6 @@ dma_complete(void *dbs, int ret, void *cb) "dbs=%p ret=%d cb=%p"
dma_blk_cb(void *dbs, int ret) "dbs=%p ret=%d"
dma_map_wait(void *dbs) "dbs=%p"
# kvm-all.c
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
kvm_irqchip_commit_routes(void) ""
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
kvm_irqchip_release_virq(int virq) "virq %d"
# TCG related tracing (mostly disabled by default)
# cpu-exec.c
disable exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
disable exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
disable exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=%x"
# translate-all.c
translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
# memory.c
memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr %#"PRIx64" value %#"PRIx64" size %u"
memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr %#"PRIx64" value %#"PRIx64" size %u"

13
vl.c
View File

@ -3757,21 +3757,18 @@ int main(int argc, char **argv, char **envp)
qdev_prop_register_global(&kvm_pit_lost_tick_policy);
break;
}
case QEMU_OPTION_accel: {
QemuOpts *accel_opts;
case QEMU_OPTION_accel:
accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"),
optarg, true);
optarg = qemu_opt_get(accel_opts, "accel");
if (!optarg || is_help_option(optarg)) {
error_printf("Possible accelerators: kvm, xen, hax, tcg\n");
exit(1);
exit(0);
}
accel_opts = qemu_opts_create(qemu_find_opts("machine"), NULL,
false, &error_abort);
qemu_opt_set(accel_opts, "accel", optarg, &error_abort);
opts = qemu_opts_create(qemu_find_opts("machine"), NULL,
false, &error_abort);
qemu_opt_set(opts, "accel", optarg, &error_abort);
break;
}
case QEMU_OPTION_usb:
olist = qemu_find_opts("machine");
qemu_opts_parse_noisily(olist, "usb=on", false);