diff --git a/.gitignore b/.gitignore index 55a001e3b8..09c2363acf 100644 --- a/.gitignore +++ b/.gitignore @@ -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-* diff --git a/Makefile b/Makefile index c830d7a46c..16a0430c6c 100644 --- a/Makefile +++ b/Makefile @@ -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 $( $@,"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) \ diff --git a/Makefile.objs b/Makefile.objs index 0575802440..b2e6322ef0 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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) diff --git a/Makefile.target b/Makefile.target index ce8dfe44a8..3d49f29457 100644 --- a/Makefile.target +++ b/Makefile.target @@ -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 diff --git a/accel/Makefile.objs b/accel/Makefile.objs new file mode 100644 index 0000000000..cd5702f347 --- /dev/null +++ b/accel/Makefile.objs @@ -0,0 +1,4 @@ +obj-$(CONFIG_SOFTMMU) += accel.o +obj-y += kvm/ +obj-y += tcg/ +obj-y += stubs/ diff --git a/accel.c b/accel/accel.c similarity index 87% rename from accel.c rename to accel/accel.c index 664bb88422..7c079a5611 100644 --- a/accel.c +++ b/accel/accel.c @@ -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); diff --git a/accel/kvm/Makefile.objs b/accel/kvm/Makefile.objs new file mode 100644 index 0000000000..85351e7de7 --- /dev/null +++ b/accel/kvm/Makefile.objs @@ -0,0 +1 @@ +obj-$(CONFIG_KVM) += kvm-all.o diff --git a/kvm-all.c b/accel/kvm/kvm-all.c similarity index 99% rename from kvm-all.c rename to accel/kvm/kvm-all.c index ab8262f672..75feffa504 100644 --- a/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -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) { diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events new file mode 100644 index 0000000000..f89ba5578d --- /dev/null +++ b/accel/kvm/trace-events @@ -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" + diff --git a/accel/stubs/Makefile.objs b/accel/stubs/Makefile.objs new file mode 100644 index 0000000000..bd5794f222 --- /dev/null +++ b/accel/stubs/Makefile.objs @@ -0,0 +1 @@ +obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/kvm-stub.c b/accel/stubs/kvm-stub.c similarity index 100% rename from kvm-stub.c rename to accel/stubs/kvm-stub.c diff --git a/accel/tcg/Makefile.objs b/accel/tcg/Makefile.objs new file mode 100644 index 0000000000..f173cd5397 --- /dev/null +++ b/accel/tcg/Makefile.objs @@ -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 diff --git a/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c similarity index 100% rename from cpu-exec-common.c rename to accel/tcg/cpu-exec-common.c diff --git a/cpu-exec.c b/accel/tcg/cpu-exec.c similarity index 99% rename from cpu-exec.c rename to accel/tcg/cpu-exec.c index 5b181c18ed..3581618bc0 100644 --- a/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -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" diff --git a/cputlb.c b/accel/tcg/cputlb.c similarity index 100% rename from cputlb.c rename to accel/tcg/cputlb.c diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c new file mode 100644 index 0000000000..dba99315e3 --- /dev/null +++ b/accel/tcg/tcg-all.c @@ -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); diff --git a/accel/tcg/trace-events b/accel/tcg/trace-events new file mode 100644 index 0000000000..2de8359670 --- /dev/null +++ b/accel/tcg/trace-events @@ -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" diff --git a/translate-all.c b/accel/tcg/translate-all.c similarity index 99% rename from translate-all.c rename to accel/tcg/translate-all.c index b3ee876526..7b25a16244 100644 --- a/translate-all.c +++ b/accel/tcg/translate-all.c @@ -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" diff --git a/translate-all.h b/accel/tcg/translate-all.h similarity index 100% rename from translate-all.h rename to accel/tcg/translate-all.h diff --git a/translate-common.c b/accel/tcg/translate-common.c similarity index 100% rename from translate-common.c rename to accel/tcg/translate-common.c diff --git a/block/nbd-client.c b/block/nbd-client.c index 87d19c7253..d64e775385 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -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; } diff --git a/blockdev-nbd.c b/blockdev-nbd.c index dd0860f4a6..28f551a7b0 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -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; } diff --git a/configure b/configure index db0798d51d..ff0f8b915c 100755 --- a/configure +++ b/configure @@ -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" diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index af02a31ebe..53ef222c0b 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #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); diff --git a/contrib/vhost-user-scsi/Makefile.objs b/contrib/vhost-user-scsi/Makefile.objs new file mode 100644 index 0000000000..e83a38a85b --- /dev/null +++ b/contrib/vhost-user-scsi/Makefile.objs @@ -0,0 +1 @@ +vhost-user-scsi-obj-y = vhost-user-scsi.o diff --git a/contrib/vhost-user-scsi/vhost-user-scsi.c b/contrib/vhost-user-scsi/vhost-user-scsi.c new file mode 100644 index 0000000000..b5ae02c96f --- /dev/null +++ b/contrib/vhost-user-scsi/vhost-user-scsi.c @@ -0,0 +1,886 @@ +/* + * vhost-user-scsi sample application + * + * Copyright (c) 2016 Nutanix Inc. All rights reserved. + * + * Author: + * Felipe Franciosi + * + * 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 + +/* 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; +} diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 3bbeb62d9a..53ff10975c 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -43,3 +43,4 @@ CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_IVSHMEM=$(CONFIG_EVENTFD) CONFIG_ROCKER=y +CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX) diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 18aed56fc0..b227a36179 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -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 diff --git a/docs/specs/parallels.txt b/docs/interop/parallels.txt similarity index 100% rename from docs/specs/parallels.txt rename to docs/interop/parallels.txt diff --git a/docs/specs/qcow2.txt b/docs/interop/qcow2.txt similarity index 100% rename from docs/specs/qcow2.txt rename to docs/interop/qcow2.txt diff --git a/docs/specs/qed_spec.txt b/docs/interop/qed_spec.txt similarity index 100% rename from docs/specs/qed_spec.txt rename to docs/interop/qed_spec.txt diff --git a/docs/qemu-ga-ref.texi b/docs/interop/qemu-ga-ref.texi similarity index 100% rename from docs/qemu-ga-ref.texi rename to docs/interop/qemu-ga-ref.texi diff --git a/docs/qemu-qmp-ref.texi b/docs/interop/qemu-qmp-ref.texi similarity index 100% rename from docs/qemu-qmp-ref.texi rename to docs/interop/qemu-qmp-ref.texi diff --git a/docs/qmp-intro.txt b/docs/interop/qmp-intro.txt similarity index 100% rename from docs/qmp-intro.txt rename to docs/interop/qmp-intro.txt diff --git a/docs/qmp-spec.txt b/docs/interop/qmp-spec.txt similarity index 100% rename from docs/qmp-spec.txt rename to docs/interop/qmp-spec.txt diff --git a/docs/specs/vhost-user.txt b/docs/interop/vhost-user.txt similarity index 100% rename from docs/specs/vhost-user.txt rename to docs/interop/vhost-user.txt diff --git a/docs/vnc-ledstate-Pseudo-encoding.txt b/docs/interop/vnc-ledstate-Pseudo-encoding.txt similarity index 100% rename from docs/vnc-ledstate-Pseudo-encoding.txt rename to docs/interop/vnc-ledstate-Pseudo-encoding.txt diff --git a/exec.c b/exec.c index a93e209625..42ad1eaedd 100644 --- a/exec.c +++ b/exec.c @@ -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 diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 6367d041f0..2f0819d977 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -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; } diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs index 54d8754e9a..b188f7242b 100644 --- a/hw/scsi/Makefile.objs +++ b/hw/scsi/Makefile.objs @@ -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 diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 804122ab05..734fdaef90 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -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; } diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index e41c0314db..d434b3e99a 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -16,7 +16,6 @@ */ #include "qemu/osdep.h" -#include #include "qapi/error.h" #include "qemu/error-report.h" #include "migration/migration.h" diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c new file mode 100644 index 0000000000..500fa6a067 --- /dev/null +++ b/hw/scsi/vhost-user-scsi.c @@ -0,0 +1,205 @@ +/* + * vhost-user-scsi host device + * + * Copyright (c) 2016 Nutanix Inc. All rights reserved. + * + * Author: + * Felipe Franciosi + * + * This work is largely based on the "vhost-scsi" implementation by: + * Stefan Hajnoczi + * Nicholas Bellinger + * + * 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) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index f9b7244808..20d6a08616 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -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 diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index b095dfc6d9..69f5959623 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -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. */ diff --git a/include/block/nbd.h b/include/block/nbd.h index 416257abca..6d75d5a670 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -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); diff --git a/include/exec/memory.h b/include/exec/memory.h index 80e605a96a..37f8e78e71 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -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. diff --git a/include/exec/poison.h b/include/exec/poison.h index 3ca7929cce..5ffed4d56e 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -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 diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 140efa840c..73d1bea8b6 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -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); diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h new file mode 100644 index 0000000000..01861f78d0 --- /dev/null +++ b/include/hw/virtio/vhost-user-scsi.h @@ -0,0 +1,35 @@ +/* + * vhost-user-scsi host device + * + * Copyright (c) 2016 Nutanix Inc. All rights reserved. + * + * Author: + * Felipe Franciosi + * + * This file is largely based on "vhost-scsi.h" by: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#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 */ diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index eac2013ddd..de6ae5a9f6 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -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; }; diff --git a/memory.c b/memory.c index 0ddc4cc28d..e08fa0ae6c 100644 --- a/memory.c +++ b/memory.c @@ -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; diff --git a/nbd/client.c b/nbd/client.c index 595d99ed30..b97143fa60 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -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; } diff --git a/nbd/common.c b/nbd/common.c index bd81637ab9..6b5c1b7b02 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -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) diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index d6071640a0..39bfed177c 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -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 diff --git a/nbd/server.c b/nbd/server.c index 49b55f6ede..8a70c054a6 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -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); } diff --git a/qemu-doc.texi b/qemu-doc.texi index 965ba5929e..21079fd675 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -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 diff --git a/qemu-nbd.c b/qemu-nbd.c index 651f85ecc1..4dd3fd4732 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -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); diff --git a/rules.mak b/rules.mak index 2a2fb72e85..6e943335f3 100644 --- a/rules.mak +++ b/rules.mak @@ -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","$@") diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c index 097db5cae1..ba6117d7de 100644 --- a/target/i386/hax-all.c +++ b/target/i386/hax-all.c @@ -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) { diff --git a/tcg-runtime.c b/tcg/tcg-runtime.c similarity index 100% rename from tcg-runtime.c rename to tcg/tcg-runtime.c diff --git a/tci.c b/tcg/tci.c similarity index 100% rename from tci.c rename to tcg/tci.c diff --git a/tests/Makefile.include b/tests/Makefile.include index f42f3dfa72..77da9b7f4b 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -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 diff --git a/tests/megasas-test.c b/tests/megasas-test.c new file mode 100644 index 0000000000..ce960e7f81 --- /dev/null +++ b/tests/megasas-test.c @@ -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(); +} diff --git a/trace-events b/trace-events index fd83087e39..bae63fdb1d 100644 --- a/trace-events +++ b/trace-events @@ -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" diff --git a/vl.c b/vl.c index 32db19e3b9..59fea15488 100644 --- a/vl.c +++ b/vl.c @@ -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);