mirror of https://github.com/xqemu/xqemu.git
* nbd and qemu-nbd fixes (Eric, Max)
* nbd refactoring (Vladimir) * vhost-user-scsi, take N+1 (Felipe) * replace memory_region_set_fd with memory_region_init_ram_from_fd (Marc-André) * docs/ movement (Paolo) * megasas TOCTOU fixes (Paolo) * make async_safe_run_on_cpu work on kvm/hax accelerators (Paolo) * Build system and poison.h improvements (Thomas) * -accel thread=xxx fix (Thomas) * move files to accel/ (Yang Zhong) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJZQli7AAoJEL/70l94x66DYAUH/icBoGSyY70G033BxbWW+jPW pjqobQw3YnFmiHSkXy1Xd4LKa0dyrVdmVECOAvB0embiN6pIk2UpeT4aQNHffG3f Y9wMRKnp6RMLpzwRYbde4rAlitn52ecGIANtFUC6RTnL7wKCuh3beWruKmkudG8V ZAsmX30pPFJxtQYJ0/q3c2jB6V87h2GocRLAmy1lF5/X1pXQ2fiKOYOJvV3HDY1U UQ+Ixuf4tTbyBwTm4Lx2tD3+olkVTUvcATqxhI+1JKu6lE0Dgb58urYDOfvVPFDl 98s6bU53f0gpOcb1/wKOAXKdZ2tvqRtwsP8IoPDnUpk8HGxbNWdofoxsO37vVIU= =zeKT -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * nbd and qemu-nbd fixes (Eric, Max) * nbd refactoring (Vladimir) * vhost-user-scsi, take N+1 (Felipe) * replace memory_region_set_fd with memory_region_init_ram_from_fd (Marc-André) * docs/ movement (Paolo) * megasas TOCTOU fixes (Paolo) * make async_safe_run_on_cpu work on kvm/hax accelerators (Paolo) * Build system and poison.h improvements (Thomas) * -accel thread=xxx fix (Thomas) * move files to accel/ (Yang Zhong) # gpg: Signature made Thu 15 Jun 2017 10:51:55 BST # gpg: using RSA key 0xBFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (41 commits) vhost-user-scsi: Introduce a vhost-user-scsi sample application vhost-user-scsi: Introduce vhost-user-scsi host device qemu-doc: include version number docs: create interop/ subdirectory include/exec/poison: Mark some CONFIG defines as poisoned, too include/exec/poison: Add missing TARGET defines nbd/server: refactor nbd_trip nbd/server: rename rc to ret nbd/server: get rid of fail: return rc nbd/server: nbd_negotiate: fix error path nbd/server: remove NBDClientNewData nbd/server: refactor nbd_co_receive_request nbd/server: get rid of EAGAIN dead code nbd/server: refactor nbd_co_send_reply nbd/server: get rid of ssize_t nbd/server: get rid of nbd_negotiate_read and friends nbd: make nbd_drop public nbd: rename read_sync and friends accel: move kvm related accelerator files into accel/ tcg: move tcg backend files into accel/tcg/ ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
7e56accdaf
|
@ -50,6 +50,7 @@
|
||||||
/qemu-version.h.tmp
|
/qemu-version.h.tmp
|
||||||
/module_block.h
|
/module_block.h
|
||||||
/vscclient
|
/vscclient
|
||||||
|
/vhost-user-scsi
|
||||||
/fsdev/virtfs-proxy-helper
|
/fsdev/virtfs-proxy-helper
|
||||||
*.[1-9]
|
*.[1-9]
|
||||||
*.a
|
*.a
|
||||||
|
@ -99,14 +100,14 @@
|
||||||
/pc-bios/optionrom/kvmvapic.img
|
/pc-bios/optionrom/kvmvapic.img
|
||||||
/pc-bios/s390-ccw/s390-ccw.elf
|
/pc-bios/s390-ccw/s390-ccw.elf
|
||||||
/pc-bios/s390-ccw/s390-ccw.img
|
/pc-bios/s390-ccw/s390-ccw.img
|
||||||
/docs/qemu-ga-qapi.texi
|
/docs/interop/qemu-ga-qapi.texi
|
||||||
/docs/qemu-ga-ref.html
|
/docs/interop/qemu-ga-ref.html
|
||||||
/docs/qemu-ga-ref.info*
|
/docs/interop/qemu-ga-ref.info*
|
||||||
/docs/qemu-ga-ref.txt
|
/docs/interop/qemu-ga-ref.txt
|
||||||
/docs/qemu-qmp-qapi.texi
|
/docs/interop/qemu-qmp-qapi.texi
|
||||||
/docs/qemu-qmp-ref.html
|
/docs/interop/qemu-qmp-ref.html
|
||||||
/docs/qemu-qmp-ref.info*
|
/docs/interop/qemu-qmp-ref.info*
|
||||||
/docs/qemu-qmp-ref.txt
|
/docs/interop/qemu-qmp-ref.txt
|
||||||
/docs/version.texi
|
/docs/version.texi
|
||||||
*.tps
|
*.tps
|
||||||
.stgit-*
|
.stgit-*
|
||||||
|
|
77
Makefile
77
Makefile
|
@ -207,8 +207,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
||||||
|
|
||||||
ifdef BUILD_DOCS
|
ifdef BUILD_DOCS
|
||||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
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/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||||
DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.txt docs/qemu-ga-ref.7
|
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||||
endif
|
endif
|
||||||
|
@ -269,6 +269,7 @@ dummy := $(call unnest-vars,, \
|
||||||
ivshmem-client-obj-y \
|
ivshmem-client-obj-y \
|
||||||
ivshmem-server-obj-y \
|
ivshmem-server-obj-y \
|
||||||
libvhost-user-obj-y \
|
libvhost-user-obj-y \
|
||||||
|
vhost-user-scsi-obj-y \
|
||||||
qga-vss-dll-obj-y \
|
qga-vss-dll-obj-y \
|
||||||
block-obj-y \
|
block-obj-y \
|
||||||
block-obj-m \
|
block-obj-m \
|
||||||
|
@ -473,6 +474,8 @@ ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
|
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
|
||||||
$(call LINK, $^)
|
$(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
|
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
||||||
$(call quiet-command,$(PYTHON) $< $@ \
|
$(call quiet-command,$(PYTHON) $< $@ \
|
||||||
|
@ -519,11 +522,12 @@ distclean: clean
|
||||||
rm -f qemu-doc.vr qemu-doc.txt
|
rm -f qemu-doc.vr qemu-doc.txt
|
||||||
rm -f config.log
|
rm -f config.log
|
||||||
rm -f linux-headers/asm
|
rm -f linux-headers/asm
|
||||||
rm -f docs/qemu-ga-qapi.texi docs/qemu-qmp-qapi.texi docs/version.texi
|
rm -f docs/version.texi
|
||||||
rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
|
rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi
|
||||||
rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7
|
||||||
rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||||
rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
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 \
|
for d in $(TARGET_DIRS); do \
|
||||||
rm -rf $$d || exit 1 ; \
|
rm -rf $$d || exit 1 ; \
|
||||||
done
|
done
|
||||||
|
@ -562,13 +566,13 @@ install-doc: $(DOCS)
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
ifdef CONFIG_POSIX
|
ifdef CONFIG_POSIX
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
$(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),)
|
ifneq ($(TOOLS),)
|
||||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||||
|
@ -576,9 +580,9 @@ ifneq ($(TOOLS),)
|
||||||
endif
|
endif
|
||||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||||
$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
|
@ -666,28 +670,27 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
|
||||||
|
|
||||||
# documentation
|
# documentation
|
||||||
MAKEINFO=makeinfo
|
MAKEINFO=makeinfo
|
||||||
MAKEINFOFLAGS=--no-split --number-sections -I docs
|
MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
|
||||||
TEXIFLAG=$(if $(V),,--quiet)
|
MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
|
||||||
|
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)"
|
||||||
|
TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
|
||||||
|
|
||||||
docs/version.texi: $(SRC_PATH)/VERSION
|
docs/version.texi: $(SRC_PATH)/VERSION
|
||||||
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
|
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
|
||||||
|
|
||||||
%.html: %.texi
|
%.html: %.texi docs/version.texi
|
||||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||||
--html $< -o $@,"GEN","$@")
|
--html $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
%.info: %.texi
|
%.info: %.texi docs/version.texi
|
||||||
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
|
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
%.txt: %.texi
|
%.txt: %.texi docs/version.texi
|
||||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||||
--plaintext $< -o $@,"GEN","$@")
|
--plaintext $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
%.pdf: %.texi
|
%.pdf: %.texi docs/version.texi
|
||||||
$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I docs $< -o $@,"GEN","$@")
|
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -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
|
|
||||||
|
|
||||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
$(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
|
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
$(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","$@")
|
$(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","$@")
|
$(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
|
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-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||||
qemu-ga.8: qemu-ga.texi
|
qemu-ga.8: qemu-ga.texi
|
||||||
|
|
||||||
html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||||
info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
|
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||||
pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||||
txt: qemu-doc.txt docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
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-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-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||||
qemu-monitor-info.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/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||||
docs/qemu-ga-ref.texi docs/qemu-ga-qapi.texi
|
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/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
|
||||||
docs/qemu-qmp-ref.texi docs/qemu-qmp-qapi.texi
|
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
|
ifdef CONFIG_WIN32
|
||||||
|
@ -791,9 +798,11 @@ endif # CONFIG_WIN
|
||||||
|
|
||||||
# Add a dependency on the generated files, so that they are always
|
# Add a dependency on the generated files, so that they are always
|
||||||
# rebuilt before other object files
|
# rebuilt before other object files
|
||||||
|
ifneq ($(wildcard config-host.mak),)
|
||||||
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||||
Makefile: $(GENERATED_FILES)
|
Makefile: $(GENERATED_FILES)
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
|
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
|
||||||
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
|
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
|
||||||
|
|
|
@ -52,7 +52,6 @@ common-obj-y += migration/
|
||||||
|
|
||||||
common-obj-y += audio/
|
common-obj-y += audio/
|
||||||
common-obj-y += hw/
|
common-obj-y += hw/
|
||||||
common-obj-y += accel.o
|
|
||||||
|
|
||||||
common-obj-y += replay/
|
common-obj-y += replay/
|
||||||
|
|
||||||
|
@ -111,6 +110,10 @@ qga-vss-dll-obj-y = qga/
|
||||||
ivshmem-client-obj-y = contrib/ivshmem-client/
|
ivshmem-client-obj-y = contrib/ivshmem-client/
|
||||||
ivshmem-server-obj-y = contrib/ivshmem-server/
|
ivshmem-server-obj-y = contrib/ivshmem-server/
|
||||||
libvhost-user-obj-y = contrib/libvhost-user/
|
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 =
|
trace-events-subdirs =
|
||||||
|
@ -163,6 +166,8 @@ trace-events-subdirs += target/ppc
|
||||||
trace-events-subdirs += qom
|
trace-events-subdirs += qom
|
||||||
trace-events-subdirs += linux-user
|
trace-events-subdirs += linux-user
|
||||||
trace-events-subdirs += qapi
|
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)
|
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||||
|
|
||||||
|
|
|
@ -88,20 +88,17 @@ all: $(PROGS) stap
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# cpu emulator library
|
# cpu emulator library
|
||||||
obj-y = exec.o translate-all.o cpu-exec.o
|
obj-y += exec.o
|
||||||
obj-y += translate-common.o
|
obj-y += accel/
|
||||||
obj-y += cpu-exec-common.o
|
|
||||||
obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
|
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-$(CONFIG_TCG_INTERPRETER) += tci.o
|
||||||
obj-y += tcg/tcg-common.o
|
|
||||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||||
obj-y += fpu/softfloat.o
|
obj-y += fpu/softfloat.o
|
||||||
obj-y += target/$(TARGET_BASE_ARCH)/
|
obj-y += target/$(TARGET_BASE_ARCH)/
|
||||||
obj-y += disas.o
|
obj-y += disas.o
|
||||||
obj-y += tcg-runtime.o
|
|
||||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||||
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.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/decContext.o
|
||||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.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 += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||||
obj-y += qtest.o bootdevice.o
|
obj-y += qtest.o bootdevice.o
|
||||||
obj-y += hw/
|
obj-y += hw/
|
||||||
obj-$(CONFIG_KVM) += kvm-all.o
|
obj-y += memory.o
|
||||||
obj-y += memory.o cputlb.o
|
|
||||||
obj-y += memory_mapping.o
|
obj-y += memory_mapping.o
|
||||||
obj-y += dump.o
|
obj-y += dump.o
|
||||||
obj-y += migration/ram.o
|
obj-y += migration/ram.o
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||||
|
obj-y += kvm/
|
||||||
|
obj-y += tcg/
|
||||||
|
obj-y += stubs/
|
|
@ -34,15 +34,6 @@
|
||||||
#include "hw/xen/xen.h"
|
#include "hw/xen/xen.h"
|
||||||
#include "qom/object.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 = {
|
static const TypeInfo accel_type = {
|
||||||
.name = TYPE_ACCEL,
|
.name = TYPE_ACCEL,
|
||||||
.parent = TYPE_OBJECT,
|
.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)
|
static void register_accel_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&accel_type);
|
type_register_static(&accel_type);
|
||||||
type_register_static(&tcg_accel_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(register_accel_types);
|
type_init(register_accel_types);
|
|
@ -0,0 +1 @@
|
||||||
|
obj-$(CONFIG_KVM) += kvm-all.o
|
|
@ -36,7 +36,7 @@
|
||||||
#include "exec/ram_addr.h"
|
#include "exec/ram_addr.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "qemu/event_notifier.h"
|
#include "qemu/event_notifier.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
|
@ -1977,6 +1977,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
cpu_exec_start(cpu);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
MemTxAttrs attrs;
|
MemTxAttrs attrs;
|
||||||
|
@ -2106,6 +2107,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||||
}
|
}
|
||||||
} while (ret == 0);
|
} while (ret == 0);
|
||||||
|
|
||||||
|
cpu_exec_end(cpu);
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
|
@ -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"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
|
@ -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
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "disas/disas.h"
|
#include "disas/disas.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "tcg.h"
|
#include "tcg.h"
|
|
@ -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);
|
|
@ -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"
|
|
@ -25,7 +25,7 @@
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#define NO_CPU_IO_DEFS
|
#define NO_CPU_IO_DEFS
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "disas/disas.h"
|
#include "disas/disas.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "tcg.h"
|
#include "tcg.h"
|
|
@ -144,8 +144,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||||
qio_channel_set_cork(s->ioc, true);
|
qio_channel_set_cork(s->ioc, true);
|
||||||
rc = nbd_send_request(s->ioc, request);
|
rc = nbd_send_request(s->ioc, request);
|
||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
|
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, false,
|
||||||
false, NULL);
|
NULL);
|
||||||
if (ret != request->len) {
|
if (ret != request->len) {
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
}
|
}
|
||||||
|
@ -173,8 +173,8 @@ static void nbd_co_receive_reply(NBDClientSession *s,
|
||||||
reply->error = EIO;
|
reply->error = EIO;
|
||||||
} else {
|
} else {
|
||||||
if (qiov && reply->error == 0) {
|
if (qiov && reply->error == 0) {
|
||||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
|
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, true,
|
||||||
true, NULL);
|
NULL);
|
||||||
if (ret != request->len) {
|
if (ret != request->len) {
|
||||||
reply->error = EIO;
|
reply->error = EIO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,10 @@ typedef struct NBDServerData {
|
||||||
|
|
||||||
static NBDServerData *nbd_server;
|
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,
|
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||||
gpointer opaque)
|
gpointer opaque)
|
||||||
|
@ -46,7 +50,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||||
nbd_client_new(NULL, cioc,
|
nbd_client_new(NULL, cioc,
|
||||||
nbd_server->tlscreds, NULL,
|
nbd_server->tlscreds, NULL,
|
||||||
nbd_client_put);
|
nbd_blockdev_client_closed);
|
||||||
object_unref(OBJECT(cioc));
|
object_unref(OBJECT(cioc));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
|
||||||
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $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_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
|
if test "$debug_info" = "yes"; then
|
||||||
CFLAGS="-g $CFLAGS"
|
CFLAGS="-g $CFLAGS"
|
||||||
LDFLAGS="-g $LDFLAGS"
|
LDFLAGS="-g $LDFLAGS"
|
||||||
|
@ -6374,7 +6374,7 @@ fi
|
||||||
|
|
||||||
# build tree in object directory in case the source is not in the current directory
|
# 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="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 pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||||
DIRS="$DIRS qapi-generated"
|
DIRS="$DIRS qapi-generated"
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
#include <linux/vhost.h>
|
#include <linux/vhost.h>
|
||||||
#include "standard-headers/linux/virtio_ring.h"
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
|
||||||
|
@ -192,11 +193,11 @@ typedef struct VuVirtq {
|
||||||
} VuVirtq;
|
} VuVirtq;
|
||||||
|
|
||||||
enum VuWatchCondtion {
|
enum VuWatchCondtion {
|
||||||
VU_WATCH_IN = 1 << 0,
|
VU_WATCH_IN = POLLIN,
|
||||||
VU_WATCH_OUT = 1 << 1,
|
VU_WATCH_OUT = POLLOUT,
|
||||||
VU_WATCH_PRI = 1 << 2,
|
VU_WATCH_PRI = POLLPRI,
|
||||||
VU_WATCH_ERR = 1 << 3,
|
VU_WATCH_ERR = POLLERR,
|
||||||
VU_WATCH_HUP = 1 << 4,
|
VU_WATCH_HUP = POLLHUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);
|
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
vhost-user-scsi-obj-y = vhost-user-scsi.o
|
|
@ -0,0 +1,886 @@
|
||||||
|
/*
|
||||||
|
* vhost-user-scsi sample application
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Felipe Franciosi <felipe@nutanix.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 only.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "contrib/libvhost-user/libvhost-user.h"
|
||||||
|
#include "hw/virtio/virtio-scsi.h"
|
||||||
|
#include "iscsi/iscsi.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
/* Small compat shim from glib 2.32 */
|
||||||
|
#ifndef G_SOURCE_CONTINUE
|
||||||
|
#define G_SOURCE_CONTINUE TRUE
|
||||||
|
#endif
|
||||||
|
#ifndef G_SOURCE_REMOVE
|
||||||
|
#define G_SOURCE_REMOVE FALSE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* #define VUS_DEBUG 1 */
|
||||||
|
|
||||||
|
/** Log helpers **/
|
||||||
|
|
||||||
|
#define PPRE \
|
||||||
|
struct timespec ts; \
|
||||||
|
char timebuf[64]; \
|
||||||
|
struct tm tm; \
|
||||||
|
(void)clock_gettime(CLOCK_REALTIME, &ts); \
|
||||||
|
(void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
|
||||||
|
|
||||||
|
#define PEXT(lvl, msg, ...) do { \
|
||||||
|
PPRE; \
|
||||||
|
fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n", \
|
||||||
|
timebuf, ts.tv_nsec / 1000, \
|
||||||
|
__FILE__, __func__, __LINE__, ## __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define PNOR(lvl, msg, ...) do { \
|
||||||
|
PPRE; \
|
||||||
|
fprintf(stderr, "%s.%06ld " lvl ": " msg "\n", \
|
||||||
|
timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef VUS_DEBUG
|
||||||
|
#define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
|
||||||
|
#define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
|
||||||
|
#define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PDBG(msg, ...) { }
|
||||||
|
#define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
|
||||||
|
#define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** vhost-user-scsi specific definitions **/
|
||||||
|
|
||||||
|
/* Only 1 LUN and device supported today */
|
||||||
|
#define VUS_MAX_LUNS 1
|
||||||
|
#define VUS_MAX_DEVS 1
|
||||||
|
|
||||||
|
#define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
|
||||||
|
|
||||||
|
typedef struct iscsi_lun {
|
||||||
|
struct iscsi_context *iscsi_ctx;
|
||||||
|
int iscsi_lun;
|
||||||
|
} iscsi_lun_t;
|
||||||
|
|
||||||
|
typedef struct vhost_scsi_dev {
|
||||||
|
VuDev vu_dev;
|
||||||
|
int server_sock;
|
||||||
|
GMainLoop *loop;
|
||||||
|
GTree *fdmap; /* fd -> gsource context id */
|
||||||
|
iscsi_lun_t luns[VUS_MAX_LUNS];
|
||||||
|
} vhost_scsi_dev_t;
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vhost_scsi_devs[VUS_MAX_DEVS];
|
||||||
|
|
||||||
|
/** glib event loop integration for libvhost-user and misc callbacks **/
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
|
||||||
|
|
||||||
|
typedef struct vus_gsrc {
|
||||||
|
GSource parent;
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
GPollFD gfd;
|
||||||
|
vu_watch_cb vu_cb;
|
||||||
|
} vus_gsrc_t;
|
||||||
|
|
||||||
|
static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
return (b > a) - (b < a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
|
||||||
|
{
|
||||||
|
assert(timeout);
|
||||||
|
|
||||||
|
*timeout = -1;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean vus_gsrc_check(GSource *src)
|
||||||
|
{
|
||||||
|
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
|
||||||
|
|
||||||
|
assert(vus_src);
|
||||||
|
|
||||||
|
return vus_src->gfd.revents & vus_src->gfd.events;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
|
||||||
|
|
||||||
|
assert(vus_src);
|
||||||
|
assert(!(vus_src->vu_cb && cb));
|
||||||
|
|
||||||
|
vdev_scsi = vus_src->vdev_scsi;
|
||||||
|
|
||||||
|
assert(vdev_scsi);
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
return cb(data);
|
||||||
|
}
|
||||||
|
if (vus_src->vu_cb) {
|
||||||
|
vus_src->vu_cb(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
|
||||||
|
}
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs vus_gsrc_funcs = {
|
||||||
|
vus_gsrc_prepare,
|
||||||
|
vus_gsrc_check,
|
||||||
|
vus_gsrc_dispatch,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int vus_gsrc_new(vhost_scsi_dev_t *vdev_scsi, int fd, GIOCondition cond,
|
||||||
|
vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
|
||||||
|
{
|
||||||
|
GSource *vus_gsrc;
|
||||||
|
vus_gsrc_t *vus_src;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
assert(vdev_scsi);
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(vu_cb || gsrc_cb);
|
||||||
|
assert(!(vu_cb && gsrc_cb));
|
||||||
|
|
||||||
|
vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
|
||||||
|
if (!vus_gsrc) {
|
||||||
|
PERR("Error creating GSource for new watch");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vus_src = (vus_gsrc_t *)vus_gsrc;
|
||||||
|
|
||||||
|
vus_src->vdev_scsi = vdev_scsi;
|
||||||
|
vus_src->gfd.fd = fd;
|
||||||
|
vus_src->gfd.events = cond;
|
||||||
|
vus_src->vu_cb = vu_cb;
|
||||||
|
|
||||||
|
g_source_add_poll(vus_gsrc, &vus_src->gfd);
|
||||||
|
g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
|
||||||
|
id = g_source_attach(vus_gsrc, NULL);
|
||||||
|
assert(id);
|
||||||
|
g_source_unref(vus_gsrc);
|
||||||
|
|
||||||
|
g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
|
||||||
|
(gpointer)(uintptr_t)id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* from libiscsi's scsi-lowlevel.h **
|
||||||
|
*
|
||||||
|
* nb. We can't directly include scsi-lowlevel.h due to a namespace conflict:
|
||||||
|
* QEMU's scsi.h also defines "SCSI_XFER_NONE".
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SCSI_CDB_MAX_SIZE 16
|
||||||
|
|
||||||
|
struct scsi_iovector {
|
||||||
|
struct scsi_iovec *iov;
|
||||||
|
int niov;
|
||||||
|
int nalloc;
|
||||||
|
size_t offset;
|
||||||
|
int consumed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_allocated_memory {
|
||||||
|
struct scsi_allocated_memory *next;
|
||||||
|
char buf[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_data {
|
||||||
|
int size;
|
||||||
|
unsigned char *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum scsi_sense_key {
|
||||||
|
SCSI_SENSE_NO_SENSE = 0x00,
|
||||||
|
SCSI_SENSE_RECOVERED_ERROR = 0x01,
|
||||||
|
SCSI_SENSE_NOT_READY = 0x02,
|
||||||
|
SCSI_SENSE_MEDIUM_ERROR = 0x03,
|
||||||
|
SCSI_SENSE_HARDWARE_ERROR = 0x04,
|
||||||
|
SCSI_SENSE_ILLEGAL_REQUEST = 0x05,
|
||||||
|
SCSI_SENSE_UNIT_ATTENTION = 0x06,
|
||||||
|
SCSI_SENSE_DATA_PROTECTION = 0x07,
|
||||||
|
SCSI_SENSE_BLANK_CHECK = 0x08,
|
||||||
|
SCSI_SENSE_VENDOR_SPECIFIC = 0x09,
|
||||||
|
SCSI_SENSE_COPY_ABORTED = 0x0a,
|
||||||
|
SCSI_SENSE_COMMAND_ABORTED = 0x0b,
|
||||||
|
SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
|
||||||
|
SCSI_SENSE_OVERFLOW_COMMAND = 0x0d,
|
||||||
|
SCSI_SENSE_MISCOMPARE = 0x0e
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_sense {
|
||||||
|
unsigned char error_type;
|
||||||
|
enum scsi_sense_key key;
|
||||||
|
int ascq;
|
||||||
|
unsigned sense_specific:1;
|
||||||
|
unsigned ill_param_in_cdb:1;
|
||||||
|
unsigned bit_pointer_valid:1;
|
||||||
|
unsigned char bit_pointer;
|
||||||
|
uint16_t field_pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum scsi_residual {
|
||||||
|
SCSI_RESIDUAL_NO_RESIDUAL = 0,
|
||||||
|
SCSI_RESIDUAL_UNDERFLOW,
|
||||||
|
SCSI_RESIDUAL_OVERFLOW
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_task {
|
||||||
|
int status;
|
||||||
|
int cdb_size;
|
||||||
|
int xfer_dir;
|
||||||
|
int expxferlen;
|
||||||
|
unsigned char cdb[SCSI_CDB_MAX_SIZE];
|
||||||
|
enum scsi_residual residual_status;
|
||||||
|
size_t residual;
|
||||||
|
struct scsi_sense sense;
|
||||||
|
struct scsi_data datain;
|
||||||
|
struct scsi_allocated_memory *mem;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
uint32_t itt;
|
||||||
|
uint32_t cmdsn;
|
||||||
|
uint32_t lun;
|
||||||
|
|
||||||
|
struct scsi_iovector iovector_in;
|
||||||
|
struct scsi_iovector iovector_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** libiscsi integration **/
|
||||||
|
|
||||||
|
static int iscsi_add_lun(iscsi_lun_t *lun, char *iscsi_uri)
|
||||||
|
{
|
||||||
|
struct iscsi_url *iscsi_url;
|
||||||
|
struct iscsi_context *iscsi_ctx;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
assert(lun);
|
||||||
|
assert(iscsi_uri);
|
||||||
|
|
||||||
|
iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
|
||||||
|
if (!iscsi_ctx) {
|
||||||
|
PERR("Unable to create iSCSI context");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
|
||||||
|
if (!iscsi_url) {
|
||||||
|
PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
|
||||||
|
iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||||
|
if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
|
||||||
|
PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
lun->iscsi_ctx = iscsi_ctx;
|
||||||
|
lun->iscsi_lun = iscsi_url->lun;
|
||||||
|
|
||||||
|
PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (iscsi_url) {
|
||||||
|
iscsi_destroy_url(iscsi_url);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
(void)iscsi_destroy_context(iscsi_ctx);
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
|
||||||
|
int xfer_len) {
|
||||||
|
struct scsi_task *task;
|
||||||
|
|
||||||
|
assert(cdb_len > 0);
|
||||||
|
assert(cdb);
|
||||||
|
|
||||||
|
task = calloc(1, sizeof(struct scsi_task));
|
||||||
|
if (!task) {
|
||||||
|
PERR("Error allocating task: %s", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(task->cdb, cdb, cdb_len);
|
||||||
|
task->cdb_size = cdb_len;
|
||||||
|
task->xfer_dir = dir;
|
||||||
|
task->expxferlen = xfer_len;
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_cdb_len(uint8_t *cdb)
|
||||||
|
{
|
||||||
|
assert(cdb);
|
||||||
|
|
||||||
|
switch (cdb[0] >> 5) {
|
||||||
|
case 0: return 6;
|
||||||
|
case 1: /* fall through */
|
||||||
|
case 2: return 10;
|
||||||
|
case 4: return 16;
|
||||||
|
case 5: return 12;
|
||||||
|
}
|
||||||
|
PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_cmd_sync(struct iscsi_context *ctx,
|
||||||
|
VirtIOSCSICmdReq *req,
|
||||||
|
struct iovec *out, unsigned int out_len,
|
||||||
|
VirtIOSCSICmdResp *rsp,
|
||||||
|
struct iovec *in, unsigned int in_len) {
|
||||||
|
struct scsi_task *task;
|
||||||
|
uint32_t dir;
|
||||||
|
uint32_t len;
|
||||||
|
int cdb_len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(ctx);
|
||||||
|
assert(req);
|
||||||
|
assert(rsp);
|
||||||
|
|
||||||
|
if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
|
||||||
|
/* Ignore anything different than target=0, lun=0 */
|
||||||
|
PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
|
||||||
|
req->lun[1], req->lun[3]);
|
||||||
|
rsp->status = SCSI_STATUS_CHECK_CONDITION;
|
||||||
|
memset(rsp->sense, 0, sizeof(rsp->sense));
|
||||||
|
rsp->sense_len = 18;
|
||||||
|
rsp->sense[0] = 0x70;
|
||||||
|
rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
|
||||||
|
rsp->sense[7] = 10;
|
||||||
|
rsp->sense[12] = 0x24;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdb_len = get_cdb_len(req->cdb);
|
||||||
|
if (cdb_len == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
if (!out_len && !in_len) {
|
||||||
|
dir = SCSI_XFER_NONE;
|
||||||
|
} else if (out_len) {
|
||||||
|
dir = SCSI_XFER_TO_DEV;
|
||||||
|
for (i = 0; i < out_len; i++) {
|
||||||
|
len += out[i].iov_len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dir = SCSI_XFER_FROM_DEV;
|
||||||
|
for (i = 0; i < in_len; i++) {
|
||||||
|
len += in[i].iov_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task = scsi_task_new(cdb_len, req->cdb, dir, len);
|
||||||
|
if (!task) {
|
||||||
|
PERR("Unable to create iscsi task");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == SCSI_XFER_TO_DEV) {
|
||||||
|
task->iovector_out.iov = (struct scsi_iovec *)out;
|
||||||
|
task->iovector_out.niov = out_len;
|
||||||
|
} else if (dir == SCSI_XFER_FROM_DEV) {
|
||||||
|
task->iovector_in.iov = (struct scsi_iovec *)in;
|
||||||
|
task->iovector_in.niov = in_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
|
||||||
|
cdb_len, dir, task);
|
||||||
|
if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
|
||||||
|
PERR("Error serving SCSI command");
|
||||||
|
free(task);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(rsp, 0, sizeof(*rsp));
|
||||||
|
|
||||||
|
rsp->status = task->status;
|
||||||
|
rsp->resid = task->residual;
|
||||||
|
|
||||||
|
if (task->status == SCSI_STATUS_CHECK_CONDITION) {
|
||||||
|
rsp->response = VIRTIO_SCSI_S_FAILURE;
|
||||||
|
rsp->sense_len = task->datain.size - 2;
|
||||||
|
memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(task);
|
||||||
|
|
||||||
|
PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
|
||||||
|
rsp->status, rsp->resid, rsp->response, rsp->sense_len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** libvhost-user callbacks **/
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev);
|
||||||
|
|
||||||
|
static void vus_panic_cb(VuDev *vu_dev, const char *buf)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
PERR("vu_panic: %s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi) {
|
||||||
|
assert(vdev_scsi->loop);
|
||||||
|
g_main_loop_quit(vdev_scsi->loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
|
||||||
|
void *pvt) {
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(cb);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
|
||||||
|
(gpointer)(uintptr_t)fd);
|
||||||
|
if (id) {
|
||||||
|
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
|
||||||
|
assert(vus_src);
|
||||||
|
g_source_destroy(vus_src);
|
||||||
|
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt)) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_del_watch_cb(VuDev *vu_dev, int fd)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
assert(fd >= 0);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
|
||||||
|
(gpointer)(uintptr_t)fd);
|
||||||
|
if (id) {
|
||||||
|
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
|
||||||
|
assert(vus_src);
|
||||||
|
g_source_destroy(vus_src);
|
||||||
|
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_proc_ctl(VuDev *vu_dev, int idx)
|
||||||
|
{
|
||||||
|
/* Control VQ not implemented */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_proc_evt(VuDev *vu_dev, int idx)
|
||||||
|
{
|
||||||
|
/* Event VQ not implemented */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_proc_req(VuDev *vu_dev, int idx)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
VuVirtq *vq;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
|
||||||
|
PERR("VQ Index out of range: %d", idx);
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vq = vu_get_queue(vu_dev, idx);
|
||||||
|
if (!vq) {
|
||||||
|
PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDBG("Got kicked on vq[%d]@%p", idx, vq);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
VuVirtqElement *elem;
|
||||||
|
VirtIOSCSICmdReq *req;
|
||||||
|
VirtIOSCSICmdResp *rsp;
|
||||||
|
|
||||||
|
elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
|
||||||
|
if (!elem) {
|
||||||
|
PDBG("No more elements pending on vq[%d]@%p", idx, vq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
PDBG("Popped elem@%p", elem);
|
||||||
|
|
||||||
|
assert(!((elem->out_num > 1) && (elem->in_num > 1)));
|
||||||
|
assert((elem->out_num > 0) && (elem->in_num > 0));
|
||||||
|
|
||||||
|
if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
|
||||||
|
PERR("Invalid virtio-scsi req header");
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
|
||||||
|
|
||||||
|
if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
|
||||||
|
PERR("Invalid virtio-scsi rsp header");
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
|
||||||
|
|
||||||
|
if (handle_cmd_sync(vdev_scsi->luns[0].iscsi_ctx,
|
||||||
|
req, &elem->out_sg[1], elem->out_num - 1,
|
||||||
|
rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vu_queue_push(vu_dev, vq, elem, 0);
|
||||||
|
vu_queue_notify(vu_dev, vq);
|
||||||
|
|
||||||
|
free(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
|
||||||
|
{
|
||||||
|
VuVirtq *vq;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
|
||||||
|
PERR("VQ Index out of range: %d", idx);
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vq = vu_get_queue(vu_dev, idx);
|
||||||
|
|
||||||
|
switch (idx) {
|
||||||
|
case 0:
|
||||||
|
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_ctl : NULL);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_evt : NULL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VuDevIface vus_iface = {
|
||||||
|
.queue_set_started = vus_queue_set_started,
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean vus_vhost_cb(gpointer data)
|
||||||
|
{
|
||||||
|
VuDev *vu_dev = (VuDev *)data;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
if (!vu_dispatch(vu_dev) != 0) {
|
||||||
|
PERR("Error processing vhost message");
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** misc helpers **/
|
||||||
|
|
||||||
|
static int unix_sock_new(char *unix_fn)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
struct sockaddr_un un;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
assert(unix_fn);
|
||||||
|
|
||||||
|
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sock <= 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
un.sun_family = AF_UNIX;
|
||||||
|
(void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
|
||||||
|
len = sizeof(un.sun_family) + strlen(un.sun_path);
|
||||||
|
|
||||||
|
(void)unlink(unix_fn);
|
||||||
|
if (bind(sock, (struct sockaddr *)&un, len) < 0) {
|
||||||
|
perror("bind");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(sock, 1) < 0) {
|
||||||
|
perror("listen");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
(void)close(sock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** vhost-user-scsi **/
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
for (i = 0; i < VUS_MAX_DEVS; i++) {
|
||||||
|
if (&vhost_scsi_devs[i]->vu_dev == vu_dev) {
|
||||||
|
return vhost_scsi_devs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PERR("Unknown VuDev %p", vu_dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vdev_scsi_deinit(vhost_scsi_dev_t *vdev_scsi)
|
||||||
|
{
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi->server_sock >= 0) {
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
socklen_t sslen = sizeof(ss);
|
||||||
|
|
||||||
|
if (getsockname(vdev_scsi->server_sock, (struct sockaddr *)&ss,
|
||||||
|
&sslen) == 0) {
|
||||||
|
struct sockaddr_un *su = (struct sockaddr_un *)&ss;
|
||||||
|
(void)unlink(su->sun_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)close(vdev_scsi->server_sock);
|
||||||
|
vdev_scsi->server_sock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi->loop) {
|
||||||
|
g_main_loop_unref(vdev_scsi->loop);
|
||||||
|
vdev_scsi->loop = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vdev_scsi_new(char *unix_fn)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi = NULL;
|
||||||
|
|
||||||
|
assert(unix_fn);
|
||||||
|
|
||||||
|
vdev_scsi = calloc(1, sizeof(vhost_scsi_dev_t));
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
PERR("calloc: %s", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi->server_sock = unix_sock_new(unix_fn);
|
||||||
|
if (vdev_scsi->server_sock < 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
if (!vdev_scsi->loop) {
|
||||||
|
PERR("Error creating glib event loop");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
|
||||||
|
if (!vdev_scsi->fdmap) {
|
||||||
|
PERR("Error creating glib tree for fdmap");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vdev_scsi;
|
||||||
|
|
||||||
|
err:
|
||||||
|
vdev_scsi_deinit(vdev_scsi);
|
||||||
|
free(vdev_scsi);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vdev_scsi_add_iscsi_lun(vhost_scsi_dev_t *vdev_scsi,
|
||||||
|
char *iscsi_uri, uint32_t lun) {
|
||||||
|
assert(vdev_scsi);
|
||||||
|
assert(iscsi_uri);
|
||||||
|
assert(lun < VUS_MAX_LUNS);
|
||||||
|
|
||||||
|
if (vdev_scsi->luns[lun].iscsi_ctx) {
|
||||||
|
PERR("Lun %d already configured", lun);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iscsi_add_lun(&vdev_scsi->luns[lun], iscsi_uri) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vdev_scsi_run(vhost_scsi_dev_t *vdev_scsi)
|
||||||
|
{
|
||||||
|
int cli_sock;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
assert(vdev_scsi);
|
||||||
|
assert(vdev_scsi->server_sock >= 0);
|
||||||
|
assert(vdev_scsi->loop);
|
||||||
|
|
||||||
|
cli_sock = accept(vdev_scsi->server_sock, (void *)0, (void *)0);
|
||||||
|
if (cli_sock < 0) {
|
||||||
|
perror("accept");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vu_init(&vdev_scsi->vu_dev,
|
||||||
|
cli_sock,
|
||||||
|
vus_panic_cb,
|
||||||
|
vus_add_watch_cb,
|
||||||
|
vus_del_watch_cb,
|
||||||
|
&vus_iface);
|
||||||
|
|
||||||
|
if (vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
|
||||||
|
&vdev_scsi->vu_dev)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_main_loop_run(vdev_scsi->loop);
|
||||||
|
|
||||||
|
out:
|
||||||
|
vu_deinit(&vdev_scsi->vu_dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi = NULL;
|
||||||
|
char *unix_fn = NULL;
|
||||||
|
char *iscsi_uri = NULL;
|
||||||
|
int opt, err = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "u:i:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
goto help;
|
||||||
|
case 'u':
|
||||||
|
unix_fn = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
iscsi_uri = strdup(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!unix_fn || !iscsi_uri) {
|
||||||
|
goto help;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_new(unix_fn);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
vhost_scsi_devs[0] = vdev_scsi;
|
||||||
|
|
||||||
|
if (vdev_scsi_add_iscsi_lun(vdev_scsi, iscsi_uri, 0) != 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi_run(vdev_scsi) != 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (vdev_scsi) {
|
||||||
|
vdev_scsi_deinit(vdev_scsi);
|
||||||
|
free(vdev_scsi);
|
||||||
|
}
|
||||||
|
if (unix_fn) {
|
||||||
|
free(unix_fn);
|
||||||
|
}
|
||||||
|
if (iscsi_uri) {
|
||||||
|
free(iscsi_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err:
|
||||||
|
err = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
help:
|
||||||
|
fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
|
||||||
|
argv[0]);
|
||||||
|
fprintf(stderr, " -u path to unix socket\n");
|
||||||
|
fprintf(stderr, " -i iscsi uri for lun 0\n");
|
||||||
|
fprintf(stderr, " -h print help and quit\n");
|
||||||
|
|
||||||
|
goto err;
|
||||||
|
}
|
|
@ -43,3 +43,4 @@ CONFIG_VGA=y
|
||||||
CONFIG_VGA_PCI=y
|
CONFIG_VGA_PCI=y
|
||||||
CONFIG_IVSHMEM=$(CONFIG_EVENTFD)
|
CONFIG_IVSHMEM=$(CONFIG_EVENTFD)
|
||||||
CONFIG_ROCKER=y
|
CONFIG_ROCKER=y
|
||||||
|
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
CONFIG_PCI=y
|
CONFIG_PCI=y
|
||||||
CONFIG_VIRTIO_PCI=y
|
CONFIG_VIRTIO_PCI=y
|
||||||
|
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
|
||||||
CONFIG_VIRTIO=y
|
CONFIG_VIRTIO=y
|
||||||
CONFIG_SCLPCONSOLE=y
|
CONFIG_SCLPCONSOLE=y
|
||||||
CONFIG_TERMINAL3270=y
|
CONFIG_TERMINAL3270=y
|
||||||
|
|
116
exec.c
116
exec.c
|
@ -1482,25 +1482,17 @@ static int64_t get_file_size(int fd)
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *file_ram_alloc(RAMBlock *block,
|
static int file_ram_open(const char *path,
|
||||||
ram_addr_t memory,
|
const char *region_name,
|
||||||
const char *path,
|
bool *created,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
bool unlink_on_error = false;
|
|
||||||
char *filename;
|
char *filename;
|
||||||
char *sanitized_name;
|
char *sanitized_name;
|
||||||
char *c;
|
char *c;
|
||||||
void *area = MAP_FAILED;
|
|
||||||
int fd = -1;
|
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 (;;) {
|
for (;;) {
|
||||||
fd = open(path, O_RDWR);
|
fd = open(path, O_RDWR);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
|
@ -1511,13 +1503,13 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||||
/* @path names a file that doesn't exist, create it */
|
/* @path names a file that doesn't exist, create it */
|
||||||
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
|
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
unlink_on_error = true;
|
*created = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (errno == EISDIR) {
|
} else if (errno == EISDIR) {
|
||||||
/* @path names a directory, create a file there */
|
/* @path names a directory, create a file there */
|
||||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
/* 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++) {
|
for (c = sanitized_name; *c != '\0'; c++) {
|
||||||
if (*c == '/') {
|
if (*c == '/') {
|
||||||
*c = '_';
|
*c = '_';
|
||||||
|
@ -1540,7 +1532,7 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||||
error_setg_errno(errp, errno,
|
error_setg_errno(errp, errno,
|
||||||
"can't open backing store %s for guest RAM",
|
"can't open backing store %s for guest RAM",
|
||||||
path);
|
path);
|
||||||
goto error;
|
return -1;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Try again on EINTR and EEXIST. The latter happens when
|
* 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->page_size = qemu_fd_getpagesize(fd);
|
||||||
block->mr->align = block->page_size;
|
block->mr->align = block->page_size;
|
||||||
#if defined(__s390x__)
|
#if defined(__s390x__)
|
||||||
|
@ -1556,20 +1559,11 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
file_size = get_file_size(fd);
|
|
||||||
|
|
||||||
if (memory < block->page_size) {
|
if (memory < block->page_size) {
|
||||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||||
"or larger than page size 0x%zx",
|
"or larger than page size 0x%zx",
|
||||||
memory, block->page_size);
|
memory, block->page_size);
|
||||||
goto error;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memory = ROUND_UP(memory, block->page_size);
|
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
|
* those labels. Therefore, extending the non-empty backend file
|
||||||
* is disabled as well.
|
* is disabled as well.
|
||||||
*/
|
*/
|
||||||
if (!file_size && ftruncate(fd, memory)) {
|
if (truncate && ftruncate(fd, memory)) {
|
||||||
perror("ftruncate");
|
perror("ftruncate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1597,30 +1591,19 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||||
if (area == MAP_FAILED) {
|
if (area == MAP_FAILED) {
|
||||||
error_setg_errno(errp, errno,
|
error_setg_errno(errp, errno,
|
||||||
"unable to map backing store for guest RAM");
|
"unable to map backing store for guest RAM");
|
||||||
goto error;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mem_prealloc) {
|
if (mem_prealloc) {
|
||||||
os_mem_prealloc(fd, area, memory, smp_cpus, errp);
|
os_mem_prealloc(fd, area, memory, smp_cpus, errp);
|
||||||
if (errp && *errp) {
|
if (errp && *errp) {
|
||||||
goto error;
|
qemu_ram_munmap(area, memory);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block->fd = fd;
|
block->fd = fd;
|
||||||
return area;
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -1931,18 +1914,25 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
|
||||||
bool share, const char *mem_path,
|
bool share, int fd,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
RAMBlock *new_block;
|
RAMBlock *new_block;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
int64_t file_size;
|
||||||
|
|
||||||
if (xen_enabled()) {
|
if (xen_enabled()) {
|
||||||
error_setg(errp, "-mem-path not supported with Xen");
|
error_setg(errp, "-mem-path not supported with Xen");
|
||||||
return NULL;
|
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) {
|
if (phys_mem_alloc != qemu_anon_ram_alloc) {
|
||||||
/*
|
/*
|
||||||
* file_ram_alloc() needs to allocate just like
|
* 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);
|
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 = g_malloc0(sizeof(*new_block));
|
||||||
new_block->mr = mr;
|
new_block->mr = mr;
|
||||||
new_block->used_length = size;
|
new_block->used_length = size;
|
||||||
new_block->max_length = size;
|
new_block->max_length = size;
|
||||||
new_block->flags = share ? RAM_SHARED : 0;
|
new_block->flags = share ? RAM_SHARED : 0;
|
||||||
new_block->host = file_ram_alloc(new_block, size,
|
new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp);
|
||||||
mem_path, errp);
|
|
||||||
if (!new_block->host) {
|
if (!new_block->host) {
|
||||||
g_free(new_block);
|
g_free(new_block);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1974,6 +1971,33 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return new_block;
|
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
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -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)
|
static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
size_t size;
|
size_t size;
|
||||||
void *ptr;
|
|
||||||
|
|
||||||
if (s->ivshmem_bar2) {
|
if (s->ivshmem_bar2) {
|
||||||
error_setg(errp, "server sent unexpected shared memory message");
|
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 */
|
/* mmap the region and map into the BAR2 */
|
||||||
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s),
|
||||||
if (ptr == MAP_FAILED) {
|
"ivshmem.bar2", size, true, fd, &local_err);
|
||||||
error_setg_errno(errp, errno, "Failed to mmap shared memory");
|
if (local_err) {
|
||||||
close(fd);
|
error_propagate(errp, local_err);
|
||||||
return;
|
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;
|
s->ivshmem_bar2 = &s->server_bar2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,5 @@ obj-$(CONFIG_PSERIES) += spapr_vscsi.o
|
||||||
ifeq ($(CONFIG_VIRTIO),y)
|
ifeq ($(CONFIG_VIRTIO),y)
|
||||||
obj-y += virtio-scsi.o virtio-scsi-dataplane.o
|
obj-y += virtio-scsi.o virtio-scsi-dataplane.o
|
||||||
obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.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
|
endif
|
||||||
|
|
|
@ -63,6 +63,7 @@ typedef struct MegasasCmd {
|
||||||
|
|
||||||
hwaddr pa;
|
hwaddr pa;
|
||||||
hwaddr pa_size;
|
hwaddr pa_size;
|
||||||
|
uint32_t dcmd_opcode;
|
||||||
union mfi_frame *frame;
|
union mfi_frame *frame;
|
||||||
SCSIRequest *req;
|
SCSIRequest *req;
|
||||||
QEMUSGList qsg;
|
QEMUSGList qsg;
|
||||||
|
@ -309,9 +310,11 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
|
||||||
PCIDevice *pcid = PCI_DEVICE(cmd->state);
|
PCIDevice *pcid = PCI_DEVICE(cmd->state);
|
||||||
uint32_t pa_hi = 0, pa_lo;
|
uint32_t pa_hi = 0, pa_lo;
|
||||||
hwaddr pa;
|
hwaddr pa;
|
||||||
|
int frame_sense_len;
|
||||||
|
|
||||||
if (sense_len > cmd->frame->header.sense_len) {
|
frame_sense_len = cmd->frame->header.sense_len;
|
||||||
sense_len = cmd->frame->header.sense_len;
|
if (sense_len > frame_sense_len) {
|
||||||
|
sense_len = frame_sense_len;
|
||||||
}
|
}
|
||||||
if (sense_len) {
|
if (sense_len) {
|
||||||
pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo);
|
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->context &= (uint64_t)0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
cmd->count = count;
|
cmd->count = count;
|
||||||
|
cmd->dcmd_opcode = -1;
|
||||||
s->busy++;
|
s->busy++;
|
||||||
|
|
||||||
if (s->consumer_pa) {
|
if (s->consumer_pa) {
|
||||||
|
@ -605,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s)
|
||||||
static void megasas_abort_command(MegasasCmd *cmd)
|
static void megasas_abort_command(MegasasCmd *cmd)
|
||||||
{
|
{
|
||||||
/* Never abort internal commands. */
|
/* Never abort internal commands. */
|
||||||
|
if (cmd->dcmd_opcode != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (cmd->req != NULL) {
|
if (cmd->req != NULL) {
|
||||||
scsi_req_cancel(cmd->req);
|
scsi_req_cancel(cmd->req);
|
||||||
}
|
}
|
||||||
|
@ -673,15 +680,16 @@ out:
|
||||||
static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
|
static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
|
||||||
{
|
{
|
||||||
dma_addr_t iov_pa, iov_size;
|
dma_addr_t iov_pa, iov_size;
|
||||||
|
int iov_count;
|
||||||
|
|
||||||
cmd->flags = le16_to_cpu(cmd->frame->header.flags);
|
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);
|
trace_megasas_dcmd_zero_sge(cmd->index);
|
||||||
cmd->iov_size = 0;
|
cmd->iov_size = 0;
|
||||||
return 0;
|
return 0;
|
||||||
} else if (cmd->frame->header.sge_count > 1) {
|
} else if (iov_count > 1) {
|
||||||
trace_megasas_dcmd_invalid_sge(cmd->index,
|
trace_megasas_dcmd_invalid_sge(cmd->index, iov_count);
|
||||||
cmd->frame->header.sge_count);
|
|
||||||
cmd->iov_size = 0;
|
cmd->iov_size = 0;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1012,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
|
||||||
uint64_t pd_size;
|
uint64_t pd_size;
|
||||||
uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
|
uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
|
||||||
uint8_t cmdbuf[6];
|
uint8_t cmdbuf[6];
|
||||||
SCSIRequest *req;
|
|
||||||
size_t len, resid;
|
size_t len, resid;
|
||||||
|
|
||||||
if (!cmd->iov_buf) {
|
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->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
|
||||||
info->vpd_page83[0] = 0x7f;
|
info->vpd_page83[0] = 0x7f;
|
||||||
megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
|
megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
|
||||||
req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
||||||
if (!req) {
|
if (!cmd->req) {
|
||||||
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
||||||
"PD get info std inquiry");
|
"PD get info std inquiry");
|
||||||
g_free(cmd->iov_buf);
|
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,
|
trace_megasas_dcmd_internal_submit(cmd->index,
|
||||||
"PD get info std inquiry", lun);
|
"PD get info std inquiry", lun);
|
||||||
len = scsi_req_enqueue(req);
|
len = scsi_req_enqueue(cmd->req);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
cmd->iov_size = len;
|
cmd->iov_size = len;
|
||||||
scsi_req_continue(req);
|
scsi_req_continue(cmd->req);
|
||||||
}
|
}
|
||||||
return MFI_STAT_INVALID_STATUS;
|
return MFI_STAT_INVALID_STATUS;
|
||||||
} else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
|
} else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
|
||||||
megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
|
megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
|
||||||
req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
||||||
if (!req) {
|
if (!cmd->req) {
|
||||||
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
||||||
"PD get info vpd inquiry");
|
"PD get info vpd inquiry");
|
||||||
return MFI_STAT_FLASH_ALLOC_FAIL;
|
return MFI_STAT_FLASH_ALLOC_FAIL;
|
||||||
}
|
}
|
||||||
trace_megasas_dcmd_internal_submit(cmd->index,
|
trace_megasas_dcmd_internal_submit(cmd->index,
|
||||||
"PD get info vpd inquiry", lun);
|
"PD get info vpd inquiry", lun);
|
||||||
len = scsi_req_enqueue(req);
|
len = scsi_req_enqueue(cmd->req);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
cmd->iov_size = len;
|
cmd->iov_size = len;
|
||||||
scsi_req_continue(req);
|
scsi_req_continue(cmd->req);
|
||||||
}
|
}
|
||||||
return MFI_STAT_INVALID_STATUS;
|
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;
|
struct mfi_ld_info *info = cmd->iov_buf;
|
||||||
size_t dcmd_size = sizeof(struct mfi_ld_info);
|
size_t dcmd_size = sizeof(struct mfi_ld_info);
|
||||||
uint8_t cdb[6];
|
uint8_t cdb[6];
|
||||||
SCSIRequest *req;
|
|
||||||
ssize_t len, resid;
|
ssize_t len, resid;
|
||||||
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
|
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
|
||||||
uint64_t ld_size;
|
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);
|
cmd->iov_buf = g_malloc0(dcmd_size);
|
||||||
info = cmd->iov_buf;
|
info = cmd->iov_buf;
|
||||||
megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
|
megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
|
||||||
req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
|
cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
|
||||||
if (!req) {
|
if (!cmd->req) {
|
||||||
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
||||||
"LD get info vpd inquiry");
|
"LD get info vpd inquiry");
|
||||||
g_free(cmd->iov_buf);
|
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,
|
trace_megasas_dcmd_internal_submit(cmd->index,
|
||||||
"LD get info vpd inquiry", lun);
|
"LD get info vpd inquiry", lun);
|
||||||
len = scsi_req_enqueue(req);
|
len = scsi_req_enqueue(cmd->req);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
cmd->iov_size = len;
|
cmd->iov_size = len;
|
||||||
scsi_req_continue(req);
|
scsi_req_continue(cmd->req);
|
||||||
}
|
}
|
||||||
return MFI_STAT_INVALID_STATUS;
|
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)
|
static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
|
||||||
{
|
{
|
||||||
int opcode;
|
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
size_t len;
|
size_t len;
|
||||||
const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
|
const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
|
||||||
|
|
||||||
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
cmd->dcmd_opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
||||||
trace_megasas_handle_dcmd(cmd->index, opcode);
|
trace_megasas_handle_dcmd(cmd->index, cmd->dcmd_opcode);
|
||||||
if (megasas_map_dcmd(s, cmd) < 0) {
|
if (megasas_map_dcmd(s, cmd) < 0) {
|
||||||
return MFI_STAT_MEMORY_NOT_AVAILABLE;
|
return MFI_STAT_MEMORY_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) {
|
while (cmdptr->opcode != -1 && cmdptr->opcode != cmd->dcmd_opcode) {
|
||||||
cmdptr++;
|
cmdptr++;
|
||||||
}
|
}
|
||||||
len = cmd->iov_size;
|
len = cmd->iov_size;
|
||||||
if (cmdptr->opcode == -1) {
|
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);
|
retval = megasas_dcmd_dummy(s, cmd);
|
||||||
} else {
|
} else {
|
||||||
trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len);
|
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,
|
static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
|
||||||
SCSIRequest *req)
|
SCSIRequest *req, size_t resid)
|
||||||
{
|
{
|
||||||
int opcode;
|
|
||||||
int retval = MFI_STAT_OK;
|
int retval = MFI_STAT_OK;
|
||||||
int lun = req->lun;
|
int lun = req->lun;
|
||||||
|
|
||||||
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun);
|
||||||
trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun);
|
cmd->iov_size -= resid;
|
||||||
switch (opcode) {
|
switch (cmd->dcmd_opcode) {
|
||||||
case MFI_DCMD_PD_GET_INFO:
|
case MFI_DCMD_PD_GET_INFO:
|
||||||
retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
|
retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
|
||||||
break;
|
break;
|
||||||
|
@ -1603,7 +1607,7 @@ static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
|
||||||
retval = megasas_ld_get_info_submit(req->dev, lun, cmd);
|
retval = megasas_ld_get_info_submit(req->dev, lun, cmd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
trace_megasas_dcmd_internal_invalid(cmd->index, opcode);
|
trace_megasas_dcmd_internal_invalid(cmd->index, cmd->dcmd_opcode);
|
||||||
retval = MFI_STAT_INVALID_DCMD;
|
retval = MFI_STAT_INVALID_DCMD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1647,43 +1651,42 @@ static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
||||||
bool is_logical)
|
int frame_cmd)
|
||||||
{
|
{
|
||||||
uint8_t *cdb;
|
uint8_t *cdb;
|
||||||
|
int target_id, lun_id, cdb_len;
|
||||||
bool is_write;
|
bool is_write;
|
||||||
struct SCSIDevice *sdev = NULL;
|
struct SCSIDevice *sdev = NULL;
|
||||||
|
bool is_logical = (frame_cmd == MFI_CMD_LD_SCSI_IO);
|
||||||
|
|
||||||
cdb = cmd->frame->pass.cdb;
|
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 (is_logical) {
|
||||||
if (cmd->frame->header.target_id >= MFI_MAX_LD ||
|
if (target_id >= MFI_MAX_LD || lun_id != 0) {
|
||||||
cmd->frame->header.lun_id != 0) {
|
|
||||||
trace_megasas_scsi_target_not_present(
|
trace_megasas_scsi_target_not_present(
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
|
mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id);
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
|
||||||
return MFI_STAT_DEVICE_NOT_FOUND;
|
return MFI_STAT_DEVICE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
|
sdev = scsi_device_find(&s->bus, 0, target_id, lun_id);
|
||||||
cmd->frame->header.lun_id);
|
|
||||||
|
|
||||||
cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
|
cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
|
||||||
trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd],
|
trace_megasas_handle_scsi(mfi_frame_desc[frame_cmd], is_logical,
|
||||||
is_logical, cmd->frame->header.target_id,
|
target_id, lun_id, sdev, cmd->iov_size);
|
||||||
cmd->frame->header.lun_id, sdev, cmd->iov_size);
|
|
||||||
|
|
||||||
if (!sdev || (megasas_is_jbod(s) && is_logical)) {
|
if (!sdev || (megasas_is_jbod(s) && is_logical)) {
|
||||||
trace_megasas_scsi_target_not_present(
|
trace_megasas_scsi_target_not_present(
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
|
mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id);
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
|
||||||
return MFI_STAT_DEVICE_NOT_FOUND;
|
return MFI_STAT_DEVICE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->frame->header.cdb_len > 16) {
|
if (cdb_len > 16) {
|
||||||
trace_megasas_scsi_invalid_cdb_len(
|
trace_megasas_scsi_invalid_cdb_len(
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
|
mfi_frame_desc[frame_cmd], is_logical,
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id,
|
target_id, lun_id, cdb_len);
|
||||||
cmd->frame->header.cdb_len);
|
|
||||||
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
|
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
|
||||||
cmd->frame->header.scsi_status = CHECK_CONDITION;
|
cmd->frame->header.scsi_status = CHECK_CONDITION;
|
||||||
s->event_count++;
|
s->event_count++;
|
||||||
|
@ -1697,12 +1700,10 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
||||||
return MFI_STAT_SCSI_DONE_WITH_ERROR;
|
return MFI_STAT_SCSI_DONE_WITH_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd->req = scsi_req_new(sdev, cmd->index,
|
cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cmd);
|
||||||
cmd->frame->header.lun_id, cdb, cmd);
|
|
||||||
if (!cmd->req) {
|
if (!cmd->req) {
|
||||||
trace_megasas_scsi_req_alloc_failed(
|
trace_megasas_scsi_req_alloc_failed(
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
mfi_frame_desc[frame_cmd], target_id, lun_id);
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
|
||||||
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
|
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
|
||||||
cmd->frame->header.scsi_status = BUSY;
|
cmd->frame->header.scsi_status = BUSY;
|
||||||
s->event_count++;
|
s->event_count++;
|
||||||
|
@ -1723,43 +1724,41 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
||||||
return MFI_STAT_INVALID_STATUS;
|
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;
|
uint32_t lba_count, lba_start_hi, lba_start_lo;
|
||||||
uint64_t lba_start;
|
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];
|
uint8_t cdb[16];
|
||||||
int len;
|
int len;
|
||||||
struct SCSIDevice *sdev = NULL;
|
struct SCSIDevice *sdev = NULL;
|
||||||
|
int target_id, lun_id, cdb_len;
|
||||||
|
|
||||||
lba_count = le32_to_cpu(cmd->frame->io.header.data_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_lo = le32_to_cpu(cmd->frame->io.lba_lo);
|
||||||
lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
|
lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
|
||||||
lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
|
lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
|
||||||
|
|
||||||
if (cmd->frame->header.target_id < MFI_MAX_LD &&
|
target_id = cmd->frame->header.target_id;
|
||||||
cmd->frame->header.lun_id == 0) {
|
lun_id = cmd->frame->header.lun_id;
|
||||||
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
|
cdb_len = cmd->frame->header.cdb_len;
|
||||||
cmd->frame->header.lun_id);
|
|
||||||
|
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,
|
trace_megasas_handle_io(cmd->index,
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
mfi_frame_desc[frame_cmd], target_id, lun_id,
|
||||||
cmd->frame->header.target_id,
|
|
||||||
cmd->frame->header.lun_id,
|
|
||||||
(unsigned long)lba_start, (unsigned long)lba_count);
|
(unsigned long)lba_start, (unsigned long)lba_count);
|
||||||
if (!sdev) {
|
if (!sdev) {
|
||||||
trace_megasas_io_target_not_present(cmd->index,
|
trace_megasas_io_target_not_present(cmd->index,
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
mfi_frame_desc[frame_cmd], target_id, lun_id);
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
|
||||||
return MFI_STAT_DEVICE_NOT_FOUND;
|
return MFI_STAT_DEVICE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->frame->header.cdb_len > 16) {
|
if (cdb_len > 16) {
|
||||||
trace_megasas_scsi_invalid_cdb_len(
|
trace_megasas_scsi_invalid_cdb_len(
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd], 1,
|
mfi_frame_desc[frame_cmd], 1, target_id, lun_id, cdb_len);
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id,
|
|
||||||
cmd->frame->header.cdb_len);
|
|
||||||
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
|
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
|
||||||
cmd->frame->header.scsi_status = CHECK_CONDITION;
|
cmd->frame->header.scsi_status = CHECK_CONDITION;
|
||||||
s->event_count++;
|
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);
|
megasas_encode_lba(cdb, lba_start, lba_count, is_write);
|
||||||
cmd->req = scsi_req_new(sdev, cmd->index,
|
cmd->req = scsi_req_new(sdev, cmd->index,
|
||||||
cmd->frame->header.lun_id, cdb, cmd);
|
lun_id, cdb, cmd);
|
||||||
if (!cmd->req) {
|
if (!cmd->req) {
|
||||||
trace_megasas_scsi_req_alloc_failed(
|
trace_megasas_scsi_req_alloc_failed(
|
||||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
mfi_frame_desc[frame_cmd], target_id, lun_id);
|
||||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
|
||||||
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
|
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
|
||||||
cmd->frame->header.scsi_status = BUSY;
|
cmd->frame->header.scsi_status = BUSY;
|
||||||
s->event_count++;
|
s->event_count++;
|
||||||
|
@ -1797,23 +1795,11 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
|
||||||
return MFI_STAT_INVALID_STATUS;
|
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)
|
static QEMUSGList *megasas_get_sg_list(SCSIRequest *req)
|
||||||
{
|
{
|
||||||
MegasasCmd *cmd = req->hba_private;
|
MegasasCmd *cmd = req->hba_private;
|
||||||
|
|
||||||
if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
|
if (cmd->dcmd_opcode != -1) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
return &cmd->qsg;
|
return &cmd->qsg;
|
||||||
|
@ -1824,18 +1810,16 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
|
||||||
{
|
{
|
||||||
MegasasCmd *cmd = req->hba_private;
|
MegasasCmd *cmd = req->hba_private;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
uint32_t opcode;
|
|
||||||
|
|
||||||
trace_megasas_io_complete(cmd->index, len);
|
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);
|
scsi_req_continue(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = scsi_req_get_buf(req);
|
buf = scsi_req_get_buf(req);
|
||||||
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
if (cmd->dcmd_opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
|
||||||
if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
|
|
||||||
struct mfi_pd_info *info = cmd->iov_buf;
|
struct mfi_pd_info *info = cmd->iov_buf;
|
||||||
|
|
||||||
if (info->inquiry_data[0] == 0x7f) {
|
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);
|
memcpy(info->vpd_page83, buf, len);
|
||||||
}
|
}
|
||||||
scsi_req_continue(req);
|
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;
|
struct mfi_ld_info *info = cmd->iov_buf;
|
||||||
|
|
||||||
if (cmd->iov_buf) {
|
if (cmd->iov_buf) {
|
||||||
|
@ -1868,11 +1852,11 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->req == NULL) {
|
if (cmd->dcmd_opcode != -1) {
|
||||||
/*
|
/*
|
||||||
* Internal command complete
|
* 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) {
|
if (cmd_status == MFI_STAT_INVALID_STATUS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1943,6 +1927,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
|
||||||
{
|
{
|
||||||
uint8_t frame_status = MFI_STAT_INVALID_CMD;
|
uint8_t frame_status = MFI_STAT_INVALID_CMD;
|
||||||
uint64_t frame_context;
|
uint64_t frame_context;
|
||||||
|
int frame_cmd;
|
||||||
MegasasCmd *cmd;
|
MegasasCmd *cmd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1961,7 +1946,8 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
|
||||||
s->event_count++;
|
s->event_count++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (cmd->frame->header.frame_cmd) {
|
frame_cmd = cmd->frame->header.frame_cmd;
|
||||||
|
switch (frame_cmd) {
|
||||||
case MFI_CMD_INIT:
|
case MFI_CMD_INIT:
|
||||||
frame_status = megasas_init_firmware(s, cmd);
|
frame_status = megasas_init_firmware(s, cmd);
|
||||||
break;
|
break;
|
||||||
|
@ -1972,18 +1958,15 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
|
||||||
frame_status = megasas_handle_abort(s, cmd);
|
frame_status = megasas_handle_abort(s, cmd);
|
||||||
break;
|
break;
|
||||||
case MFI_CMD_PD_SCSI_IO:
|
case MFI_CMD_PD_SCSI_IO:
|
||||||
frame_status = megasas_handle_scsi(s, cmd, 0);
|
|
||||||
break;
|
|
||||||
case MFI_CMD_LD_SCSI_IO:
|
case MFI_CMD_LD_SCSI_IO:
|
||||||
frame_status = megasas_handle_scsi(s, cmd, 1);
|
frame_status = megasas_handle_scsi(s, cmd, frame_cmd);
|
||||||
break;
|
break;
|
||||||
case MFI_CMD_LD_READ:
|
case MFI_CMD_LD_READ:
|
||||||
case MFI_CMD_LD_WRITE:
|
case MFI_CMD_LD_WRITE:
|
||||||
frame_status = megasas_handle_io(s, cmd);
|
frame_status = megasas_handle_io(s, cmd, frame_cmd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
trace_megasas_unhandled_frame_cmd(cmd->index,
|
trace_megasas_unhandled_frame_cmd(cmd->index, frame_cmd);
|
||||||
cmd->frame->header.frame_cmd);
|
|
||||||
s->event_count++;
|
s->event_count++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include <linux/vhost.h>
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* vhost-user-scsi host device
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Felipe Franciosi <felipe@nutanix.com>
|
||||||
|
*
|
||||||
|
* This work is largely based on the "vhost-scsi" implementation by:
|
||||||
|
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||||
|
* Nicholas Bellinger <nab@risingtidesystems.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||||
|
* See the COPYING.LIB file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/typedefs.h"
|
||||||
|
#include "qom/object.h"
|
||||||
|
#include "hw/fw-path-provider.h"
|
||||||
|
#include "hw/qdev-core.h"
|
||||||
|
#include "hw/virtio/vhost.h"
|
||||||
|
#include "hw/virtio/vhost-backend.h"
|
||||||
|
#include "hw/virtio/vhost-user-scsi.h"
|
||||||
|
#include "hw/virtio/virtio.h"
|
||||||
|
#include "hw/virtio/virtio-access.h"
|
||||||
|
#include "chardev/char-fe.h"
|
||||||
|
|
||||||
|
/* Features supported by the host application */
|
||||||
|
static const int user_feature_bits[] = {
|
||||||
|
VIRTIO_F_NOTIFY_ON_EMPTY,
|
||||||
|
VIRTIO_RING_F_INDIRECT_DESC,
|
||||||
|
VIRTIO_RING_F_EVENT_IDX,
|
||||||
|
VIRTIO_SCSI_F_HOTPLUG,
|
||||||
|
VHOST_INVALID_FEATURE_BIT
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status)
|
||||||
|
{
|
||||||
|
VHostUserSCSI *s = (VHostUserSCSI *)vdev;
|
||||||
|
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
|
||||||
|
bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running;
|
||||||
|
|
||||||
|
if (vsc->dev.started == start) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = vhost_scsi_common_start(vsc);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("unable to start vhost-user-scsi: %s", strerror(-ret));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vhost_scsi_common_stop(vsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
|
||||||
|
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
|
||||||
|
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
|
||||||
|
Error *err = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!vs->conf.chardev.chr) {
|
||||||
|
error_setg(errp, "vhost-user-scsi: missing chardev");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtio_scsi_common_realize(dev, vhost_dummy_handle_output,
|
||||||
|
vhost_dummy_handle_output,
|
||||||
|
vhost_dummy_handle_output, &err);
|
||||||
|
if (err != NULL) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vsc->dev.nvqs = 2 + vs->conf.num_queues;
|
||||||
|
vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs);
|
||||||
|
vsc->dev.vq_index = 0;
|
||||||
|
vsc->dev.backend_features = 0;
|
||||||
|
|
||||||
|
ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev,
|
||||||
|
VHOST_BACKEND_TYPE_USER, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Channel and lun both are 0 for bootable vhost-user-scsi disk */
|
||||||
|
vsc->channel = 0;
|
||||||
|
vsc->lun = 0;
|
||||||
|
vsc->target = vs->conf.boot_tpgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
|
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
|
||||||
|
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
|
||||||
|
|
||||||
|
/* This will stop the vhost backend. */
|
||||||
|
vhost_user_scsi_set_status(vdev, 0);
|
||||||
|
|
||||||
|
vhost_dev_cleanup(&vsc->dev);
|
||||||
|
g_free(vsc->dev.vqs);
|
||||||
|
|
||||||
|
virtio_scsi_common_unrealize(dev, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev,
|
||||||
|
uint64_t features, Error **errp)
|
||||||
|
{
|
||||||
|
VHostUserSCSI *s = VHOST_USER_SCSI(vdev);
|
||||||
|
|
||||||
|
/* Turn on predefined features supported by this device */
|
||||||
|
features |= s->host_features;
|
||||||
|
|
||||||
|
return vhost_scsi_common_get_features(vdev, features, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property vhost_user_scsi_properties[] = {
|
||||||
|
DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev),
|
||||||
|
DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
|
||||||
|
DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1),
|
||||||
|
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
|
||||||
|
0xFFFF),
|
||||||
|
DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
|
||||||
|
DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features,
|
||||||
|
VIRTIO_SCSI_F_HOTPLUG,
|
||||||
|
true),
|
||||||
|
DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features,
|
||||||
|
VIRTIO_SCSI_F_CHANGE,
|
||||||
|
true),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_vhost_scsi = {
|
||||||
|
.name = "virtio-scsi",
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_VIRTIO_DEVICE,
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vhost_user_scsi_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
|
||||||
|
|
||||||
|
dc->props = vhost_user_scsi_properties;
|
||||||
|
dc->vmsd = &vmstate_vhost_scsi;
|
||||||
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||||
|
vdc->realize = vhost_user_scsi_realize;
|
||||||
|
vdc->unrealize = vhost_user_scsi_unrealize;
|
||||||
|
vdc->get_features = vhost_user_scsi_get_features;
|
||||||
|
vdc->set_config = vhost_scsi_common_set_config;
|
||||||
|
vdc->set_status = vhost_user_scsi_set_status;
|
||||||
|
fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vhost_user_scsi_instance_init(Object *obj)
|
||||||
|
{
|
||||||
|
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
|
||||||
|
|
||||||
|
vsc->feature_bits = user_feature_bits;
|
||||||
|
|
||||||
|
/* Add the bootindex property for this object */
|
||||||
|
device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
|
||||||
|
DEVICE(vsc), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo vhost_user_scsi_info = {
|
||||||
|
.name = TYPE_VHOST_USER_SCSI,
|
||||||
|
.parent = TYPE_VHOST_SCSI_COMMON,
|
||||||
|
.instance_size = sizeof(VHostUserSCSI),
|
||||||
|
.class_init = vhost_user_scsi_class_init,
|
||||||
|
.instance_init = vhost_user_scsi_instance_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_FW_PATH_PROVIDER },
|
||||||
|
{ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void virtio_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&vhost_user_scsi_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(virtio_register_types)
|
|
@ -2135,6 +2135,61 @@ static const TypeInfo vhost_scsi_pci_info = {
|
||||||
};
|
};
|
||||||
#endif
|
#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 */
|
/* vhost-vsock-pci */
|
||||||
|
|
||||||
#ifdef CONFIG_VHOST_VSOCK
|
#ifdef CONFIG_VHOST_VSOCK
|
||||||
|
@ -2612,6 +2667,9 @@ static void virtio_pci_register_types(void)
|
||||||
#ifdef CONFIG_VHOST_SCSI
|
#ifdef CONFIG_VHOST_SCSI
|
||||||
type_register_static(&vhost_scsi_pci_info);
|
type_register_static(&vhost_scsi_pci_info);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_LINUX
|
||||||
|
type_register_static(&vhost_user_scsi_pci_info);
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_VHOST_VSOCK
|
#ifdef CONFIG_VHOST_VSOCK
|
||||||
type_register_static(&vhost_vsock_pci_info);
|
type_register_static(&vhost_vsock_pci_info);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "hw/virtio/virtio-input.h"
|
#include "hw/virtio/virtio-input.h"
|
||||||
#include "hw/virtio/virtio-gpu.h"
|
#include "hw/virtio/virtio-gpu.h"
|
||||||
#include "hw/virtio/virtio-crypto.h"
|
#include "hw/virtio/virtio-crypto.h"
|
||||||
|
#include "hw/virtio/vhost-user-scsi.h"
|
||||||
|
|
||||||
#ifdef CONFIG_VIRTFS
|
#ifdef CONFIG_VIRTFS
|
||||||
#include "hw/9pfs/virtio-9p.h"
|
#include "hw/9pfs/virtio-9p.h"
|
||||||
|
@ -44,6 +45,7 @@ typedef struct VirtIOBalloonPCI VirtIOBalloonPCI;
|
||||||
typedef struct VirtIOSerialPCI VirtIOSerialPCI;
|
typedef struct VirtIOSerialPCI VirtIOSerialPCI;
|
||||||
typedef struct VirtIONetPCI VirtIONetPCI;
|
typedef struct VirtIONetPCI VirtIONetPCI;
|
||||||
typedef struct VHostSCSIPCI VHostSCSIPCI;
|
typedef struct VHostSCSIPCI VHostSCSIPCI;
|
||||||
|
typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
|
||||||
typedef struct VirtIORngPCI VirtIORngPCI;
|
typedef struct VirtIORngPCI VirtIORngPCI;
|
||||||
typedef struct VirtIOInputPCI VirtIOInputPCI;
|
typedef struct VirtIOInputPCI VirtIOInputPCI;
|
||||||
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
|
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
|
||||||
|
@ -230,6 +232,15 @@ struct VHostSCSIPCI {
|
||||||
};
|
};
|
||||||
#endif
|
#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.
|
* virtio-blk-pci: This extends VirtioPCIProxy.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -123,12 +123,8 @@ enum {
|
||||||
* aren't overflowing some other buffer. */
|
* aren't overflowing some other buffer. */
|
||||||
#define NBD_MAX_NAME_SIZE 256
|
#define NBD_MAX_NAME_SIZE 256
|
||||||
|
|
||||||
ssize_t nbd_wr_syncv(QIOChannel *ioc,
|
ssize_t nbd_rwv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length,
|
||||||
struct iovec *iov,
|
bool do_read, Error **errp);
|
||||||
size_t niov,
|
|
||||||
size_t length,
|
|
||||||
bool do_read,
|
|
||||||
Error **errp);
|
|
||||||
int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
QCryptoTLSCreds *tlscreds, const char *hostname,
|
QCryptoTLSCreds *tlscreds, const char *hostname,
|
||||||
QIOChannel **outioc,
|
QIOChannel **outioc,
|
||||||
|
@ -162,7 +158,7 @@ void nbd_client_new(NBDExport *exp,
|
||||||
QIOChannelSocket *sioc,
|
QIOChannelSocket *sioc,
|
||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
const char *tlsaclname,
|
const char *tlsaclname,
|
||||||
void (*close)(NBDClient *));
|
void (*close_fn)(NBDClient *, bool));
|
||||||
void nbd_client_get(NBDClient *client);
|
void nbd_client_get(NBDClient *client);
|
||||||
void nbd_client_put(NBDClient *client);
|
void nbd_client_put(NBDClient *client);
|
||||||
|
|
||||||
|
|
|
@ -456,6 +456,26 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
|
||||||
bool share,
|
bool share,
|
||||||
const char *path,
|
const char *path,
|
||||||
Error **errp);
|
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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -804,17 +824,6 @@ static inline bool memory_region_is_rom(MemoryRegion *mr)
|
||||||
*/
|
*/
|
||||||
int memory_region_get_fd(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
|
* memory_region_from_host: Convert a pointer into a RAM memory region
|
||||||
* and an offset within it.
|
* and an offset within it.
|
||||||
|
|
|
@ -12,17 +12,28 @@
|
||||||
#pragma GCC poison TARGET_CRIS
|
#pragma GCC poison TARGET_CRIS
|
||||||
#pragma GCC poison TARGET_LM32
|
#pragma GCC poison TARGET_LM32
|
||||||
#pragma GCC poison TARGET_M68K
|
#pragma GCC poison TARGET_M68K
|
||||||
|
#pragma GCC poison TARGET_MICROBLAZE
|
||||||
#pragma GCC poison TARGET_MIPS
|
#pragma GCC poison TARGET_MIPS
|
||||||
|
#pragma GCC poison TARGET_ABI_MIPSO32
|
||||||
#pragma GCC poison TARGET_MIPS64
|
#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_OPENRISC
|
||||||
#pragma GCC poison TARGET_PPC
|
#pragma GCC poison TARGET_PPC
|
||||||
#pragma GCC poison TARGET_PPCEMB
|
#pragma GCC poison TARGET_PPCEMB
|
||||||
#pragma GCC poison TARGET_PPC64
|
#pragma GCC poison TARGET_PPC64
|
||||||
#pragma GCC poison TARGET_ABI32
|
#pragma GCC poison TARGET_ABI32
|
||||||
|
#pragma GCC poison TARGET_S390X
|
||||||
#pragma GCC poison TARGET_SH4
|
#pragma GCC poison TARGET_SH4
|
||||||
#pragma GCC poison TARGET_SPARC
|
#pragma GCC poison TARGET_SPARC
|
||||||
#pragma GCC poison TARGET_SPARC64
|
#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 TARGET_WORDS_BIGENDIAN
|
||||||
#pragma GCC poison BSWAP_NEEDED
|
#pragma GCC poison BSWAP_NEEDED
|
||||||
|
|
||||||
|
@ -50,5 +61,25 @@
|
||||||
#pragma GCC poison CPU_INTERRUPT_TGT_INT_1
|
#pragma GCC poison CPU_INTERRUPT_TGT_INT_1
|
||||||
#pragma GCC poison CPU_INTERRUPT_TGT_INT_2
|
#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
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -65,6 +65,9 @@ unsigned long last_ram_page(void);
|
||||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||||
bool share, const char *mem_path,
|
bool share, const char *mem_path,
|
||||||
Error **errp);
|
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,
|
RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
||||||
MemoryRegion *mr, Error **errp);
|
MemoryRegion *mr, Error **errp);
|
||||||
RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp);
|
RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp);
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* vhost-user-scsi host device
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Felipe Franciosi <felipe@nutanix.com>
|
||||||
|
*
|
||||||
|
* This file is largely based on "vhost-scsi.h" by:
|
||||||
|
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||||
|
* See the COPYING.LIB file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VHOST_USER_SCSI_H
|
||||||
|
#define VHOST_USER_SCSI_H
|
||||||
|
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "hw/qdev.h"
|
||||||
|
#include "hw/virtio/virtio-scsi.h"
|
||||||
|
#include "hw/virtio/vhost.h"
|
||||||
|
#include "hw/virtio/vhost-scsi-common.h"
|
||||||
|
|
||||||
|
#define TYPE_VHOST_USER_SCSI "vhost-user-scsi"
|
||||||
|
#define VHOST_USER_SCSI(obj) \
|
||||||
|
OBJECT_CHECK(VHostUserSCSI, (obj), TYPE_VHOST_USER_SCSI)
|
||||||
|
|
||||||
|
typedef struct VHostUserSCSI {
|
||||||
|
VHostSCSICommon parent_obj;
|
||||||
|
uint64_t host_features;
|
||||||
|
} VHostUserSCSI;
|
||||||
|
|
||||||
|
#endif /* VHOST_USER_SCSI_H */
|
|
@ -21,6 +21,7 @@
|
||||||
#include "hw/virtio/virtio.h"
|
#include "hw/virtio/virtio.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/scsi/scsi.h"
|
#include "hw/scsi/scsi.h"
|
||||||
|
#include "chardev/char-fe.h"
|
||||||
#include "sysemu/iothread.h"
|
#include "sysemu/iothread.h"
|
||||||
|
|
||||||
#define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common"
|
#define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common"
|
||||||
|
@ -53,6 +54,7 @@ struct VirtIOSCSIConf {
|
||||||
char *vhostfd;
|
char *vhostfd;
|
||||||
char *wwpn;
|
char *wwpn;
|
||||||
#endif
|
#endif
|
||||||
|
CharBackend chardev;
|
||||||
uint32_t boot_tpgt;
|
uint32_t boot_tpgt;
|
||||||
IOThread *iothread;
|
IOThread *iothread;
|
||||||
};
|
};
|
||||||
|
|
26
memory.c
26
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->ram_block = qemu_ram_alloc_from_file(size, mr, share, path, errp);
|
||||||
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
|
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
|
#endif
|
||||||
|
|
||||||
void memory_region_init_ram_ptr(MemoryRegion *mr,
|
void memory_region_init_ram_ptr(MemoryRegion *mr,
|
||||||
|
@ -1835,16 +1851,6 @@ int memory_region_get_fd(MemoryRegion *mr)
|
||||||
return fd;
|
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 *memory_region_get_ram_ptr(MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
64
nbd/client.c
64
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.
|
/* Send an option request.
|
||||||
*
|
*
|
||||||
* The request is for option @opt, with @data containing @len bytes of
|
* 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.option, opt);
|
||||||
stl_be_p(&req.length, len);
|
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");
|
error_prepend(errp, "Failed to send option request header");
|
||||||
return -1;
|
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");
|
error_prepend(errp, "Failed to send option request data");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -169,7 +143,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
|
||||||
nbd_opt_reply *reply, Error **errp)
|
nbd_opt_reply *reply, Error **errp)
|
||||||
{
|
{
|
||||||
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
|
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");
|
error_prepend(errp, "failed to read option reply");
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -218,7 +192,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
msg = g_malloc(reply->length + 1);
|
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");
|
error_prepend(errp, "failed to read option error message");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -320,7 +294,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
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");
|
error_prepend(errp, "failed to read option name length");
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -333,7 +307,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (namelen != strlen(want)) {
|
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");
|
error_prepend(errp, "failed to skip export name with wrong length");
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -342,14 +316,14 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(namelen < sizeof(name));
|
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");
|
error_prepend(errp, "failed to read export name");
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
name[namelen] = '\0';
|
name[namelen] = '\0';
|
||||||
len -= namelen;
|
len -= namelen;
|
||||||
if (drop_sync(ioc, len, errp) < 0) {
|
if (nbd_drop(ioc, len, errp) < 0) {
|
||||||
error_prepend(errp, "failed to read export description");
|
error_prepend(errp, "failed to read export description");
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -476,7 +450,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
goto fail;
|
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");
|
error_prepend(errp, "Failed to read data");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -502,7 +476,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
goto fail;
|
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");
|
error_prepend(errp, "Failed to read magic");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -514,7 +488,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
uint16_t globalflags;
|
uint16_t globalflags;
|
||||||
bool fixedNewStyle = false;
|
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");
|
error_prepend(errp, "Failed to read server flags");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -532,7 +506,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
}
|
}
|
||||||
/* client requested flags */
|
/* client requested flags */
|
||||||
clientflags = cpu_to_be32(clientflags);
|
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");
|
error_prepend(errp, "Failed to send clientflags field");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -570,13 +544,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read the response */
|
/* 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");
|
error_prepend(errp, "Failed to read export length");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
*size = be64_to_cpu(s);
|
*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");
|
error_prepend(errp, "Failed to read export flags");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -593,14 +567,14 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
||||||
goto fail;
|
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");
|
error_prepend(errp, "Failed to read export length");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
*size = be64_to_cpu(s);
|
*size = be64_to_cpu(s);
|
||||||
TRACE("Size is %" PRIu64, *size);
|
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");
|
error_prepend(errp, "Failed to read export flags");
|
||||||
goto fail;
|
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);
|
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");
|
error_prepend(errp, "Failed to read reserved block");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -759,7 +733,7 @@ ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request)
|
||||||
stq_be_p(buf + 16, request->from);
|
stq_be_p(buf + 16, request->from);
|
||||||
stl_be_p(buf + 24, request->len);
|
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)
|
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;
|
uint32_t magic;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = read_sync_eof(ioc, buf, sizeof(buf), errp);
|
ret = nbd_read_eof(ioc, buf, sizeof(buf), errp);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
34
nbd/common.c
34
nbd/common.c
|
@ -24,12 +24,8 @@
|
||||||
* The function may be called from coroutine or from non-coroutine context.
|
* The function may be called from coroutine or from non-coroutine context.
|
||||||
* When called from non-coroutine context @ioc must be in blocking mode.
|
* When called from non-coroutine context @ioc must be in blocking mode.
|
||||||
*/
|
*/
|
||||||
ssize_t nbd_wr_syncv(QIOChannel *ioc,
|
ssize_t nbd_rwv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length,
|
||||||
struct iovec *iov,
|
bool do_read, Error **errp)
|
||||||
size_t niov,
|
|
||||||
size_t length,
|
|
||||||
bool do_read,
|
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
ssize_t done = 0;
|
ssize_t done = 0;
|
||||||
struct iovec *local_iov = g_new(struct iovec, niov);
|
struct iovec *local_iov = g_new(struct iovec, niov);
|
||||||
|
@ -69,6 +65,32 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
|
||||||
return done;
|
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 nbd_tls_handshake(QIOTask *task,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
|
|
|
@ -94,14 +94,14 @@
|
||||||
#define NBD_ENOSPC 28
|
#define NBD_ENOSPC 28
|
||||||
#define NBD_ESHUTDOWN 108
|
#define NBD_ESHUTDOWN 108
|
||||||
|
|
||||||
/* read_sync_eof
|
/* nbd_read_eof
|
||||||
* Tries to read @size bytes from @ioc. Returns number of bytes actually read.
|
* 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
|
* 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.
|
* iteratively.
|
||||||
*/
|
*/
|
||||||
static inline ssize_t read_sync_eof(QIOChannel *ioc, void *buffer, size_t size,
|
static inline ssize_t nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct iovec iov = { .iov_base = buffer, .iov_len = size };
|
struct iovec iov = { .iov_base = buffer, .iov_len = size };
|
||||||
/* Sockets are kept in blocking mode in the negotiation phase. After
|
/* 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
|
* our request/reply. Synchronization is done with recv_coroutine, so
|
||||||
* that this is coroutine-safe.
|
* 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.
|
* Reads @size bytes from @ioc. Returns 0 on success.
|
||||||
*/
|
*/
|
||||||
static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size,
|
static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size,
|
||||||
Error **errp)
|
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) {
|
if (ret >= 0 && ret != size) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -128,15 +128,15 @@ static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size,
|
||||||
return ret < 0 ? ret : 0;
|
return ret < 0 ? ret : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write_sync
|
/* nbd_write
|
||||||
* Writes @size bytes to @ioc. Returns 0 on success.
|
* Writes @size bytes to @ioc. Returns 0 on success.
|
||||||
*/
|
*/
|
||||||
static inline int write_sync(QIOChannel *ioc, const void *buffer, size_t size,
|
static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct iovec iov = { .iov_base = (void *) buffer, .iov_len = size };
|
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);
|
assert(ret < 0 || ret == size);
|
||||||
|
|
||||||
|
@ -153,4 +153,6 @@ struct NBDTLSHandshakeData {
|
||||||
void nbd_tls_handshake(QIOTask *task,
|
void nbd_tls_handshake(QIOTask *task,
|
||||||
void *opaque);
|
void *opaque);
|
||||||
|
|
||||||
|
int nbd_drop(QIOChannel *ioc, size_t size, Error **errp);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
343
nbd/server.c
343
nbd/server.c
|
@ -81,7 +81,7 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
|
||||||
|
|
||||||
struct NBDClient {
|
struct NBDClient {
|
||||||
int refcount;
|
int refcount;
|
||||||
void (*close)(NBDClient *client);
|
void (*close_fn)(NBDClient *client, bool negotiated);
|
||||||
|
|
||||||
bool no_zeroes;
|
bool no_zeroes;
|
||||||
NBDExport *exp;
|
NBDExport *exp;
|
||||||
|
@ -104,69 +104,6 @@ struct NBDClient {
|
||||||
|
|
||||||
static void nbd_client_receive_next_request(NBDClient *client);
|
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
|
/* Basic flow for negotiation
|
||||||
|
|
||||||
Server Client
|
Server Client
|
||||||
|
@ -205,22 +142,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
|
||||||
type, opt, len);
|
type, opt, len);
|
||||||
|
|
||||||
magic = cpu_to_be64(NBD_REP_MAGIC);
|
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)");
|
LOG("write failed (rep magic)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
opt = cpu_to_be32(opt);
|
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)");
|
LOG("write failed (rep opt)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
type = cpu_to_be32(type);
|
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)");
|
LOG("write failed (rep type)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
len = cpu_to_be32(len);
|
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)");
|
LOG("write failed (rep data length)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -255,7 +192,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (nbd_negotiate_write(ioc, msg, len) < 0) {
|
if (nbd_write(ioc, msg, len, NULL) < 0) {
|
||||||
LOG("write failed (error message)");
|
LOG("write failed (error message)");
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,27 +211,27 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
const char *name = exp->name ? exp->name : "";
|
const char *name = exp->name ? exp->name : "";
|
||||||
const char *desc = exp->description ? exp->description : "";
|
const char *desc = exp->description ? exp->description : "";
|
||||||
int rc;
|
int ret;
|
||||||
|
|
||||||
TRACE("Advertising export name '%s' description '%s'", name, desc);
|
TRACE("Advertising export name '%s' description '%s'", name, desc);
|
||||||
name_len = strlen(name);
|
name_len = strlen(name);
|
||||||
desc_len = strlen(desc);
|
desc_len = strlen(desc);
|
||||||
len = name_len + desc_len + sizeof(len);
|
len = name_len + desc_len + sizeof(len);
|
||||||
rc = nbd_negotiate_send_rep_len(ioc, NBD_REP_SERVER, NBD_OPT_LIST, len);
|
ret = nbd_negotiate_send_rep_len(ioc, NBD_REP_SERVER, NBD_OPT_LIST, len);
|
||||||
if (rc < 0) {
|
if (ret < 0) {
|
||||||
return rc;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = cpu_to_be32(name_len);
|
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)");
|
LOG("write failed (name length)");
|
||||||
return -EINVAL;
|
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)");
|
LOG("write failed (name buffer)");
|
||||||
return -EINVAL;
|
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)");
|
LOG("write failed (description buffer)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -308,7 +245,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
|
||||||
NBDExport *exp;
|
NBDExport *exp;
|
||||||
|
|
||||||
if (length) {
|
if (length) {
|
||||||
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
|
if (nbd_drop(client->ioc, length, NULL) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
return nbd_negotiate_send_rep_err(client->ioc,
|
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)
|
static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
|
||||||
{
|
{
|
||||||
int rc = -EINVAL;
|
|
||||||
char name[NBD_MAX_NAME_SIZE + 1];
|
char name[NBD_MAX_NAME_SIZE + 1];
|
||||||
|
|
||||||
/* Client sends:
|
/* Client sends:
|
||||||
|
@ -337,11 +273,11 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
|
||||||
TRACE("Checking length");
|
TRACE("Checking length");
|
||||||
if (length >= sizeof(name)) {
|
if (length >= sizeof(name)) {
|
||||||
LOG("Bad length received");
|
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");
|
LOG("read failed");
|
||||||
goto fail;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
name[length] = '\0';
|
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);
|
client->exp = nbd_export_find(name);
|
||||||
if (!client->exp) {
|
if (!client->exp) {
|
||||||
LOG("export not found");
|
LOG("export not found");
|
||||||
goto fail;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
|
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
|
||||||
nbd_export_get(client->exp);
|
nbd_export_get(client->exp);
|
||||||
rc = 0;
|
|
||||||
fail:
|
return 0;
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
|
/* 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");
|
TRACE("Setting up TLS");
|
||||||
ioc = client->ioc;
|
ioc = client->ioc;
|
||||||
if (length) {
|
if (length) {
|
||||||
if (nbd_negotiate_drop_sync(ioc, length) < 0) {
|
if (nbd_drop(ioc, length, NULL) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS,
|
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
|
... 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");
|
LOG("read failed");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -462,7 +397,7 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||||
uint32_t clientflags, length;
|
uint32_t clientflags, length;
|
||||||
uint64_t magic;
|
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");
|
LOG("read failed");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -472,15 +407,15 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbd_negotiate_read(client->ioc, &clientflags,
|
if (nbd_read(client->ioc, &clientflags,
|
||||||
sizeof(clientflags)) < 0)
|
sizeof(clientflags), NULL) < 0)
|
||||||
{
|
{
|
||||||
LOG("read failed");
|
LOG("read failed");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
clientflags = be32_to_cpu(clientflags);
|
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");
|
LOG("read failed");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -510,7 +445,7 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
|
if (nbd_drop(client->ioc, length, NULL) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
ret = nbd_negotiate_send_rep_err(client->ioc,
|
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);
|
return nbd_negotiate_handle_export_name(client, length);
|
||||||
|
|
||||||
case NBD_OPT_STARTTLS:
|
case NBD_OPT_STARTTLS:
|
||||||
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
|
if (nbd_drop(client->ioc, length, NULL) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (client->tlscreds) {
|
if (client->tlscreds) {
|
||||||
|
@ -567,7 +502,7 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
|
if (nbd_drop(client->ioc, length, NULL) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
ret = nbd_negotiate_send_rep_err(client->ioc,
|
ret = nbd_negotiate_send_rep_err(client->ioc,
|
||||||
|
@ -598,16 +533,10 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
static coroutine_fn int nbd_negotiate(NBDClient *client)
|
||||||
NBDClient *client;
|
|
||||||
Coroutine *co;
|
|
||||||
} NBDClientNewData;
|
|
||||||
|
|
||||||
static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
|
|
||||||
{
|
{
|
||||||
NBDClient *client = data->client;
|
|
||||||
char buf[8 + 8 + 8 + 128];
|
char buf[8 + 8 + 8 + 128];
|
||||||
int rc;
|
int ret;
|
||||||
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
|
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
|
||||||
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
|
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
|
||||||
NBD_FLAG_SEND_WRITE_ZEROES);
|
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);
|
qio_channel_set_blocking(client->ioc, false, NULL);
|
||||||
rc = -EINVAL;
|
|
||||||
|
|
||||||
TRACE("Beginning negotiation.");
|
TRACE("Beginning negotiation.");
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
|
@ -654,21 +582,21 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
|
||||||
if (oldStyle) {
|
if (oldStyle) {
|
||||||
if (client->tlscreds) {
|
if (client->tlscreds) {
|
||||||
TRACE("TLS cannot be enabled with oldstyle protocol");
|
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");
|
LOG("write failed");
|
||||||
goto fail;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (nbd_negotiate_write(client->ioc, buf, 18) < 0) {
|
if (nbd_write(client->ioc, buf, 18, NULL) < 0) {
|
||||||
LOG("write failed");
|
LOG("write failed");
|
||||||
goto fail;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
rc = nbd_negotiate_options(client);
|
ret = nbd_negotiate_options(client);
|
||||||
if (rc != 0) {
|
if (ret != 0) {
|
||||||
LOG("option negotiation failed");
|
LOG("option negotiation failed");
|
||||||
goto fail;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("advertising size %" PRIu64 " and flags %x",
|
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);
|
stq_be_p(buf + 18, client->exp->size);
|
||||||
stw_be_p(buf + 26, client->exp->nbdflags | myflags);
|
stw_be_p(buf + 26, client->exp->nbdflags | myflags);
|
||||||
len = client->no_zeroes ? 10 : sizeof(buf) - 18;
|
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");
|
LOG("write failed");
|
||||||
goto fail;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Negotiation succeeded.");
|
TRACE("Negotiation succeeded.");
|
||||||
rc = 0;
|
|
||||||
fail:
|
return 0;
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
uint8_t buf[NBD_REQUEST_SIZE];
|
||||||
uint32_t magic;
|
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) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -726,7 +654,7 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
|
||||||
return 0;
|
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];
|
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);
|
stl_be_p(buf + 4, reply->error);
|
||||||
stq_be_p(buf + 8, reply->handle);
|
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
|
#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) {
|
if (client->closing) {
|
||||||
return;
|
return;
|
||||||
|
@ -793,8 +721,8 @@ static void client_close(NBDClient *client)
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
/* Also tell the client, so that they release their reference. */
|
/* Also tell the client, so that they release their reference. */
|
||||||
if (client->close) {
|
if (client->close_fn) {
|
||||||
client->close(client);
|
client->close_fn(client, negotiated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -975,7 +903,7 @@ void nbd_export_close(NBDExport *exp)
|
||||||
|
|
||||||
nbd_export_get(exp);
|
nbd_export_get(exp);
|
||||||
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
|
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
|
||||||
client_close(client);
|
client_close(client, true);
|
||||||
}
|
}
|
||||||
nbd_export_set_name(exp, NULL);
|
nbd_export_set_name(exp, NULL);
|
||||||
nbd_export_set_description(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,
|
static int nbd_co_send_reply(NBDRequestData *req, NBDReply *reply, int len)
|
||||||
int len)
|
|
||||||
{
|
{
|
||||||
NBDClient *client = req->client;
|
NBDClient *client = req->client;
|
||||||
ssize_t rc, ret;
|
int ret;
|
||||||
|
|
||||||
g_assert(qemu_in_coroutine());
|
g_assert(qemu_in_coroutine());
|
||||||
qemu_co_mutex_lock(&client->send_lock);
|
qemu_co_mutex_lock(&client->send_lock);
|
||||||
client->send_coroutine = qemu_coroutine_self();
|
client->send_coroutine = qemu_coroutine_self();
|
||||||
|
|
||||||
if (!len) {
|
if (!len) {
|
||||||
rc = nbd_send_reply(client->ioc, reply);
|
ret = nbd_send_reply(client->ioc, reply);
|
||||||
} else {
|
} else {
|
||||||
qio_channel_set_cork(client->ioc, true);
|
qio_channel_set_cork(client->ioc, true);
|
||||||
rc = nbd_send_reply(client->ioc, reply);
|
ret = nbd_send_reply(client->ioc, reply);
|
||||||
if (rc >= 0) {
|
if (ret == 0) {
|
||||||
ret = write_sync(client->ioc, req->data, len, NULL);
|
ret = nbd_write(client->ioc, req->data, len, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rc = -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qio_channel_set_cork(client->ioc, false);
|
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;
|
client->send_coroutine = NULL;
|
||||||
qemu_co_mutex_unlock(&client->send_lock);
|
qemu_co_mutex_unlock(&client->send_lock);
|
||||||
return rc;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Collect a client request. Return 0 if request looks valid, -EAGAIN
|
/* nbd_co_receive_request
|
||||||
* to keep trying the collection, -EIO to drop connection right away,
|
* Collect a client request. Return 0 if request looks valid, -EIO to drop
|
||||||
* and any other negative value to report an error to the client
|
* connection right away, and any other negative value to report an error to
|
||||||
* (although the caller may still need to disconnect after reporting
|
* the client (although the caller may still need to disconnect after reporting
|
||||||
* the error). */
|
* the error).
|
||||||
static ssize_t nbd_co_receive_request(NBDRequestData *req,
|
*/
|
||||||
NBDRequest *request)
|
static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request)
|
||||||
{
|
{
|
||||||
NBDClient *client = req->client;
|
NBDClient *client = req->client;
|
||||||
ssize_t rc;
|
|
||||||
|
|
||||||
g_assert(qemu_in_coroutine());
|
g_assert(qemu_in_coroutine());
|
||||||
assert(client->recv_coroutine == qemu_coroutine_self());
|
assert(client->recv_coroutine == qemu_coroutine_self());
|
||||||
rc = nbd_receive_request(client->ioc, request);
|
if (nbd_receive_request(client->ioc, request) < 0) {
|
||||||
if (rc < 0) {
|
return -EIO;
|
||||||
if (rc != -EAGAIN) {
|
|
||||||
rc = -EIO;
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Decoding type");
|
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,
|
/* Special case: we're going to disconnect without a reply,
|
||||||
* whether or not flags, from, or len are bogus */
|
* whether or not flags, from, or len are bogus */
|
||||||
TRACE("Request type is DISCONNECT");
|
TRACE("Request type is DISCONNECT");
|
||||||
rc = -EIO;
|
return -EIO;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for sanity in the parameters, part 1. Defer as many
|
/* 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. */
|
* payload, so we can try and keep the connection alive. */
|
||||||
if ((request->from + request->len) < request->from) {
|
if ((request->from + request->len) < request->from) {
|
||||||
LOG("integer overflow detected, you're probably being attacked");
|
LOG("integer overflow detected, you're probably being attacked");
|
||||||
rc = -EINVAL;
|
return -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) {
|
if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) {
|
||||||
if (request->len > NBD_MAX_BUFFER_SIZE) {
|
if (request->len > NBD_MAX_BUFFER_SIZE) {
|
||||||
LOG("len (%" PRIu32" ) is larger than max len (%u)",
|
LOG("len (%" PRIu32" ) is larger than max len (%u)",
|
||||||
request->len, NBD_MAX_BUFFER_SIZE);
|
request->len, NBD_MAX_BUFFER_SIZE);
|
||||||
rc = -EINVAL;
|
return -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req->data = blk_try_blockalign(client->exp->blk, request->len);
|
req->data = blk_try_blockalign(client->exp->blk, request->len);
|
||||||
if (req->data == NULL) {
|
if (req->data == NULL) {
|
||||||
rc = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (request->type == NBD_CMD_WRITE) {
|
if (request->type == NBD_CMD_WRITE) {
|
||||||
TRACE("Reading %" PRIu32 " byte(s)", request->len);
|
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");
|
LOG("reading from socket failed");
|
||||||
rc = -EIO;
|
return -EIO;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
req->complete = true;
|
req->complete = true;
|
||||||
}
|
}
|
||||||
|
@ -1136,28 +1053,19 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
|
||||||
LOG("operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
|
LOG("operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
|
||||||
", Size: %" PRIu64, request->from, request->len,
|
", Size: %" PRIu64, request->from, request->len,
|
||||||
(uint64_t)client->exp->size);
|
(uint64_t)client->exp->size);
|
||||||
rc = request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL;
|
return request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
if (request->flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) {
|
if (request->flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) {
|
||||||
LOG("unsupported flags (got 0x%x)", request->flags);
|
LOG("unsupported flags (got 0x%x)", request->flags);
|
||||||
rc = -EINVAL;
|
return -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
if (request->type != NBD_CMD_WRITE_ZEROES &&
|
if (request->type != NBD_CMD_WRITE_ZEROES &&
|
||||||
(request->flags & NBD_CMD_FLAG_NO_HOLE)) {
|
(request->flags & NBD_CMD_FLAG_NO_HOLE)) {
|
||||||
LOG("unexpected flags (got 0x%x)", request->flags);
|
LOG("unexpected flags (got 0x%x)", request->flags);
|
||||||
rc = -EINVAL;
|
return -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
|
||||||
client->recv_coroutine = NULL;
|
|
||||||
nbd_client_receive_next_request(client);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Owns a reference to the NBDClient passed as opaque. */
|
/* Owns a reference to the NBDClient passed as opaque. */
|
||||||
|
@ -1168,8 +1076,9 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
NBDRequestData *req;
|
NBDRequestData *req;
|
||||||
NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
|
NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
|
||||||
NBDReply reply;
|
NBDReply reply;
|
||||||
ssize_t ret;
|
int ret;
|
||||||
int flags;
|
int flags;
|
||||||
|
int reply_data_len = 0;
|
||||||
|
|
||||||
TRACE("Reading request.");
|
TRACE("Reading request.");
|
||||||
if (client->closing) {
|
if (client->closing) {
|
||||||
|
@ -1179,11 +1088,10 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
|
|
||||||
req = nbd_request_get(client);
|
req = nbd_request_get(client);
|
||||||
ret = nbd_co_receive_request(req, &request);
|
ret = nbd_co_receive_request(req, &request);
|
||||||
if (ret == -EAGAIN) {
|
client->recv_coroutine = NULL;
|
||||||
goto done;
|
nbd_client_receive_next_request(client);
|
||||||
}
|
|
||||||
if (ret == -EIO) {
|
if (ret == -EIO) {
|
||||||
goto out;
|
goto disconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply.handle = request.handle;
|
reply.handle = request.handle;
|
||||||
|
@ -1191,7 +1099,7 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
goto error_reply;
|
goto reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->closing) {
|
if (client->closing) {
|
||||||
|
@ -1212,7 +1120,7 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG("flush failed");
|
LOG("flush failed");
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
goto error_reply;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1221,12 +1129,12 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG("reading from file failed");
|
LOG("reading from file failed");
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
goto error_reply;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reply_data_len = request.len;
|
||||||
TRACE("Read %" PRIu32" byte(s)", request.len);
|
TRACE("Read %" PRIu32" byte(s)", request.len);
|
||||||
if (nbd_co_send_reply(req, &reply, request.len) < 0)
|
|
||||||
goto out;
|
|
||||||
break;
|
break;
|
||||||
case NBD_CMD_WRITE:
|
case NBD_CMD_WRITE:
|
||||||
TRACE("Request type is 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) {
|
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
|
||||||
TRACE("Server is read-only, return error");
|
TRACE("Server is read-only, return error");
|
||||||
reply.error = EROFS;
|
reply.error = EROFS;
|
||||||
goto error_reply;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Writing to device");
|
TRACE("Writing to device");
|
||||||
|
@ -1248,21 +1156,16 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG("writing to file failed");
|
LOG("writing to file failed");
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
goto error_reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbd_co_send_reply(req, &reply, 0) < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NBD_CMD_WRITE_ZEROES:
|
case NBD_CMD_WRITE_ZEROES:
|
||||||
TRACE("Request type is WRITE_ZEROES");
|
TRACE("Request type is WRITE_ZEROES");
|
||||||
|
|
||||||
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
|
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
|
||||||
TRACE("Server is read-only, return error");
|
TRACE("Server is read-only, return error");
|
||||||
reply.error = EROFS;
|
reply.error = EROFS;
|
||||||
goto error_reply;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Writing to device");
|
TRACE("Writing to device");
|
||||||
|
@ -1279,14 +1182,9 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG("writing to file failed");
|
LOG("writing to file failed");
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
goto error_reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbd_co_send_reply(req, &reply, 0) < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NBD_CMD_DISC:
|
case NBD_CMD_DISC:
|
||||||
/* unreachable, thanks to special case in nbd_co_receive_request() */
|
/* unreachable, thanks to special case in nbd_co_receive_request() */
|
||||||
abort();
|
abort();
|
||||||
|
@ -1299,9 +1197,7 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
LOG("flush failed");
|
LOG("flush failed");
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
}
|
}
|
||||||
if (nbd_co_send_reply(req, &reply, 0) < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case NBD_CMD_TRIM:
|
case NBD_CMD_TRIM:
|
||||||
TRACE("Request type is TRIM");
|
TRACE("Request type is TRIM");
|
||||||
|
@ -1311,21 +1207,19 @@ static coroutine_fn void nbd_trip(void *opaque)
|
||||||
LOG("discard failed");
|
LOG("discard failed");
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
}
|
}
|
||||||
if (nbd_co_send_reply(req, &reply, 0) < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG("invalid request type (%" PRIu32 ") received", request.type);
|
LOG("invalid request type (%" PRIu32 ") received", request.type);
|
||||||
reply.error = EINVAL;
|
reply.error = EINVAL;
|
||||||
error_reply:
|
}
|
||||||
/* We must disconnect after NBD_CMD_WRITE if we did not
|
|
||||||
* read the payload.
|
reply:
|
||||||
*/
|
/* We must disconnect after NBD_CMD_WRITE if we did not
|
||||||
if (nbd_co_send_reply(req, &reply, 0) < 0 || !req->complete) {
|
* read the payload.
|
||||||
goto out;
|
*/
|
||||||
}
|
if (nbd_co_send_reply(req, &reply, reply_data_len) < 0 || !req->complete) {
|
||||||
break;
|
goto disconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Request/Reply complete");
|
TRACE("Request/Reply complete");
|
||||||
|
@ -1335,9 +1229,9 @@ done:
|
||||||
nbd_client_put(client);
|
nbd_client_put(client);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
out:
|
disconnect:
|
||||||
nbd_request_put(req);
|
nbd_request_put(req);
|
||||||
client_close(client);
|
client_close(client, true);
|
||||||
nbd_client_put(client);
|
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)
|
static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||||
{
|
{
|
||||||
NBDClientNewData *data = opaque;
|
NBDClient *client = opaque;
|
||||||
NBDClient *client = data->client;
|
|
||||||
NBDExport *exp = client->exp;
|
NBDExport *exp = client->exp;
|
||||||
|
|
||||||
if (exp) {
|
if (exp) {
|
||||||
|
@ -1362,25 +1255,28 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||||
}
|
}
|
||||||
qemu_co_mutex_init(&client->send_lock);
|
qemu_co_mutex_init(&client->send_lock);
|
||||||
|
|
||||||
if (nbd_negotiate(data)) {
|
if (nbd_negotiate(client)) {
|
||||||
client_close(client);
|
client_close(client, false);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nbd_client_receive_next_request(client);
|
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,
|
void nbd_client_new(NBDExport *exp,
|
||||||
QIOChannelSocket *sioc,
|
QIOChannelSocket *sioc,
|
||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
const char *tlsaclname,
|
const char *tlsaclname,
|
||||||
void (*close_fn)(NBDClient *))
|
void (*close_fn)(NBDClient *, bool))
|
||||||
{
|
{
|
||||||
NBDClient *client;
|
NBDClient *client;
|
||||||
NBDClientNewData *data = g_new(NBDClientNewData, 1);
|
Coroutine *co;
|
||||||
|
|
||||||
client = g_malloc0(sizeof(NBDClient));
|
client = g_malloc0(sizeof(NBDClient));
|
||||||
client->refcount = 1;
|
client->refcount = 1;
|
||||||
|
@ -1394,9 +1290,8 @@ void nbd_client_new(NBDExport *exp,
|
||||||
object_ref(OBJECT(client->sioc));
|
object_ref(OBJECT(client->sioc));
|
||||||
client->ioc = QIO_CHANNEL(sioc);
|
client->ioc = QIO_CHANNEL(sioc);
|
||||||
object_ref(OBJECT(client->ioc));
|
object_ref(OBJECT(client->ioc));
|
||||||
client->close = close_fn;
|
client->close_fn = close_fn;
|
||||||
|
|
||||||
data->client = client;
|
co = qemu_coroutine_create(nbd_co_client_start, client);
|
||||||
data->co = qemu_coroutine_create(nbd_co_client_start, data);
|
qemu_coroutine_enter(co);
|
||||||
qemu_coroutine_enter(data->co);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
\input texinfo @c -*- texinfo -*-
|
\input texinfo @c -*- texinfo -*-
|
||||||
@c %**start of header
|
@c %**start of header
|
||||||
@setfilename qemu-doc.info
|
@setfilename qemu-doc.info
|
||||||
|
@include version.texi
|
||||||
|
|
||||||
@documentlanguage en
|
@documentlanguage en
|
||||||
@documentencoding UTF-8
|
@documentencoding UTF-8
|
||||||
|
|
||||||
@settitle QEMU Emulator User Documentation
|
@settitle QEMU version @value{VERSION} User Documentation
|
||||||
@exampleindent 0
|
@exampleindent 0
|
||||||
@paragraphindent 0
|
@paragraphindent 0
|
||||||
@c %**end of header
|
@c %**end of header
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
@iftex
|
@iftex
|
||||||
@titlepage
|
@titlepage
|
||||||
@sp 7
|
@sp 7
|
||||||
@center @titlefont{QEMU Emulator}
|
@center @titlefont{QEMU version @value{VERSION}}
|
||||||
@sp 1
|
@sp 1
|
||||||
@center @titlefont{User Documentation}
|
@center @titlefont{User Documentation}
|
||||||
@sp 3
|
@sp 3
|
||||||
|
|
|
@ -336,10 +336,10 @@ static void nbd_export_closed(NBDExport *exp)
|
||||||
|
|
||||||
static void nbd_update_server_watch(void);
|
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--;
|
nb_fds--;
|
||||||
if (nb_fds == 0 && !persistent && state == RUNNING) {
|
if (negotiated && nb_fds == 0 && !persistent && state == RUNNING) {
|
||||||
state = TERMINATE;
|
state = TERMINATE;
|
||||||
}
|
}
|
||||||
nbd_update_server_watch();
|
nbd_update_server_watch();
|
||||||
|
@ -581,6 +581,10 @@ int main(int argc, char **argv)
|
||||||
sa_sigterm.sa_handler = termsig_handler;
|
sa_sigterm.sa_handler = termsig_handler;
|
||||||
sigaction(SIGTERM, &sa_sigterm, NULL);
|
sigaction(SIGTERM, &sa_sigterm, NULL);
|
||||||
|
|
||||||
|
#ifdef CONFIG_POSIX
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
#endif
|
||||||
|
|
||||||
module_call_init(MODULE_INIT_TRACE);
|
module_call_init(MODULE_INIT_TRACE);
|
||||||
qcrypto_init(&error_fatal);
|
qcrypto_init(&error_fatal);
|
||||||
|
|
||||||
|
|
|
@ -377,7 +377,7 @@ define unnest-vars
|
||||||
endef
|
endef
|
||||||
|
|
||||||
TEXI2MAN = $(call quiet-command, \
|
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 > $@, \
|
$(POD2MAN) --section=$(subst .,,$(suffix $@)) --center=" " --release=" " $@.pod > $@, \
|
||||||
"GEN","$@")
|
"GEN","$@")
|
||||||
|
|
||||||
|
|
|
@ -514,9 +514,10 @@ static int hax_vcpu_hax_exec(CPUArchState *env)
|
||||||
hax_vcpu_interrupt(env);
|
hax_vcpu_interrupt(env);
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
cpu_exec_start(cpu);
|
||||||
hax_ret = hax_vcpu_run(vcpu);
|
hax_ret = hax_vcpu_run(vcpu);
|
||||||
|
cpu_exec_end(cpu);
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
current_cpu = cpu;
|
|
||||||
|
|
||||||
/* Simply continue the vcpu_run if system call interrupted */
|
/* Simply continue the vcpu_run if system call interrupted */
|
||||||
if (hax_ret == -EINTR || hax_ret == -EAGAIN) {
|
if (hax_ret == -EINTR || hax_ret == -EAGAIN) {
|
||||||
|
|
|
@ -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
|
gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c
|
||||||
check-qtest-pci-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
|
check-qtest-pci-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
|
||||||
gcov-files-pci-y += hw/misc/ivshmem.c
|
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/endianness-test$(EXESUF)
|
||||||
check-qtest-i386-y += tests/fdc-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-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/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/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/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-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
|
||||||
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
|
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
22
trace-events
22
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_blk_cb(void *dbs, int ret) "dbs=%p ret=%d"
|
||||||
dma_map_wait(void *dbs) "dbs=%p"
|
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.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_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"
|
memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr %#"PRIx64" value %#"PRIx64" size %u"
|
||||||
|
|
13
vl.c
13
vl.c
|
@ -3757,21 +3757,18 @@ int main(int argc, char **argv, char **envp)
|
||||||
qdev_prop_register_global(&kvm_pit_lost_tick_policy);
|
qdev_prop_register_global(&kvm_pit_lost_tick_policy);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QEMU_OPTION_accel: {
|
case QEMU_OPTION_accel:
|
||||||
QemuOpts *accel_opts;
|
|
||||||
|
|
||||||
accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"),
|
accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"),
|
||||||
optarg, true);
|
optarg, true);
|
||||||
optarg = qemu_opt_get(accel_opts, "accel");
|
optarg = qemu_opt_get(accel_opts, "accel");
|
||||||
if (!optarg || is_help_option(optarg)) {
|
if (!optarg || is_help_option(optarg)) {
|
||||||
error_printf("Possible accelerators: kvm, xen, hax, tcg\n");
|
error_printf("Possible accelerators: kvm, xen, hax, tcg\n");
|
||||||
exit(1);
|
exit(0);
|
||||||
}
|
}
|
||||||
accel_opts = qemu_opts_create(qemu_find_opts("machine"), NULL,
|
opts = qemu_opts_create(qemu_find_opts("machine"), NULL,
|
||||||
false, &error_abort);
|
false, &error_abort);
|
||||||
qemu_opt_set(accel_opts, "accel", optarg, &error_abort);
|
qemu_opt_set(opts, "accel", optarg, &error_abort);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case QEMU_OPTION_usb:
|
case QEMU_OPTION_usb:
|
||||||
olist = qemu_find_opts("machine");
|
olist = qemu_find_opts("machine");
|
||||||
qemu_opts_parse_noisily(olist, "usb=on", false);
|
qemu_opts_parse_noisily(olist, "usb=on", false);
|
||||||
|
|
Loading…
Reference in New Issue