Compare commits
188 Commits
pull-usb-2
...
queue/ui-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95e92000c8 | ||
|
|
3f8f1313e0 | ||
|
|
a4f113fd69 | ||
|
|
371c4ef637 | ||
|
|
d8dc67e119 | ||
|
|
6fafc26014 | ||
|
|
8dfaf23ae1 | ||
|
|
e85c0d1401 | ||
|
|
65a0e3e842 | ||
|
|
7e56accdaf | ||
|
|
5135a1056d | ||
|
|
5837aaac25 | ||
|
|
30ff7d1d0b | ||
|
|
cef8fd6836 | ||
|
|
a1fbe750fd | ||
|
|
7f3cf2d6e7 | ||
|
|
e7a3b91fdf | ||
|
|
b9313021f3 | ||
|
|
7feb51b709 | ||
|
|
e691ef6991 | ||
|
|
8bbf4aa96e | ||
|
|
2f295167e0 | ||
|
|
5b50bf77ce | ||
|
|
9caa6f3dbe | ||
|
|
39c1b4254e | ||
|
|
b64bd51efa | ||
|
|
c0bad49946 | ||
|
|
2119882c7e | ||
|
|
3783fa3dd3 | ||
|
|
47fec59941 | ||
|
|
f7946da274 | ||
|
|
ae2d489c34 | ||
|
|
93001e9d87 | ||
|
|
3b170dc867 | ||
|
|
7258ed930c | ||
|
|
850d54a2a9 | ||
|
|
e2a6ae7fe5 | ||
|
|
20fc71b25c | ||
|
|
d993b85804 | ||
|
|
414c2ec358 | ||
|
|
d3faa13e5f | ||
|
|
79f24568e5 | ||
|
|
80c58a5b1b | ||
|
|
73a27bbb69 | ||
|
|
f9f65a4af0 | ||
|
|
edf8bc9842 | ||
|
|
49cc0340f8 | ||
|
|
f12c1ebddf | ||
|
|
44cb280d33 | ||
|
|
d59157ea05 | ||
|
|
067b913619 | ||
|
|
e947738e38 | ||
|
|
8c372a02e0 | ||
|
|
2e5c9ad6f4 | ||
|
|
d9faeed854 | ||
|
|
7798d3aab9 | ||
|
|
c84087f2f5 | ||
|
|
ee898b870f | ||
|
|
2a6e128bfa | ||
|
|
572b97e722 | ||
|
|
a0dc63a6b7 | ||
|
|
2b0bbc4f88 | ||
|
|
44298024d3 | ||
|
|
d1fdf257d5 | ||
|
|
92229a57bb | ||
|
|
244f144134 | ||
|
|
d9bb58e510 | ||
|
|
a9ded6017e | ||
|
|
041e32b8d9 | ||
|
|
0c9390d978 | ||
|
|
457e03559d | ||
|
|
1d78a3c3ab | ||
|
|
a20fa79fa5 | ||
|
|
428952cfa9 | ||
|
|
87e459a810 | ||
|
|
b356807fcd | ||
|
|
36c327a69d | ||
|
|
5104fac853 | ||
|
|
24c0c77af5 | ||
|
|
134550bf81 | ||
|
|
660174fc1b | ||
|
|
6b9911d0b6 | ||
|
|
8381d89bec | ||
|
|
fea617c58b | ||
|
|
38b3362dd1 | ||
|
|
8d37b030fe | ||
|
|
e45e7ae281 | ||
|
|
73aa4692ec | ||
|
|
69e698220f | ||
|
|
c88f8107b1 | ||
|
|
e5b0cbe8e8 | ||
|
|
043b936ef6 | ||
|
|
5c3ad1a6a8 | ||
|
|
91589d9e5c | ||
|
|
3416ab5bb4 | ||
|
|
edc60127e4 | ||
|
|
1adc1ceef7 | ||
|
|
62a0265852 | ||
|
|
68a4a2fda1 | ||
|
|
fe5c44f9c9 | ||
|
|
8f4ea9cd0b | ||
|
|
5fe309ff0d | ||
|
|
bfefa6d7d6 | ||
|
|
af8862b2a2 | ||
|
|
076d4d39b6 | ||
|
|
becf8217de | ||
|
|
465aec4617 | ||
|
|
3f0602927b | ||
|
|
252a7a6a96 | ||
|
|
d5aa0c229a | ||
|
|
cddafd8f35 | ||
|
|
556969e938 | ||
|
|
1403f36447 | ||
|
|
a87e81b9b5 | ||
|
|
fe3874b6a1 | ||
|
|
d1bb099f63 | ||
|
|
a14f9b8292 | ||
|
|
5f7f22ffe1 | ||
|
|
9e883790dd | ||
|
|
310150c000 | ||
|
|
a2f2f6249b | ||
|
|
986924f875 | ||
|
|
54ab9927d1 | ||
|
|
92e5d7e222 | ||
|
|
ee78356eba | ||
|
|
6f153ceb9b | ||
|
|
b097efc002 | ||
|
|
79cad8b46b | ||
|
|
735286a4f8 | ||
|
|
e0b4891ae6 | ||
|
|
9746211baa | ||
|
|
8e3cf49c47 | ||
|
|
6666c96aac | ||
|
|
c4b63b7cc5 | ||
|
|
84a899de8c | ||
|
|
2ce3bf1aa9 | ||
|
|
da6f17903f | ||
|
|
c3d2e2e76c | ||
|
|
b7722747e4 | ||
|
|
f2a8f0a631 | ||
|
|
f8d806c992 | ||
|
|
543147116e | ||
|
|
c8f9f4f402 | ||
|
|
930ac04c22 | ||
|
|
250561e1ae | ||
|
|
0425dc9762 | ||
|
|
f4f3082b0c | ||
|
|
9bba618f18 | ||
|
|
5093f028ce | ||
|
|
2a8469aaab | ||
|
|
56faeb9bb6 | ||
|
|
593080936a | ||
|
|
b1fd36c363 | ||
|
|
9ed656631d | ||
|
|
100f738850 | ||
|
|
ad265631c0 | ||
|
|
3b95410507 | ||
|
|
bc277a52fb | ||
|
|
20fdef58a0 | ||
|
|
277238f9f4 | ||
|
|
27d4c3789d | ||
|
|
73119c2864 | ||
|
|
d203c64398 | ||
|
|
4871dd4c3f | ||
|
|
a4d4edce7a | ||
|
|
67b544d65f | ||
|
|
7980833619 | ||
|
|
6304fd27ef | ||
|
|
0be4e88621 | ||
|
|
cd74d27e42 | ||
|
|
7b7258f810 | ||
|
|
454b580ae9 | ||
|
|
f224d35be9 | ||
|
|
2c5534776b | ||
|
|
8a9e0e7b89 | ||
|
|
2d3e302ec2 | ||
|
|
ec69355bef | ||
|
|
11cde1c810 | ||
|
|
528f449f59 | ||
|
|
3a586d2f0b | ||
|
|
f652402487 | ||
|
|
462e5d5065 | ||
|
|
249e9f792c | ||
|
|
fbe8202ea8 | ||
|
|
64bc98f4b9 | ||
|
|
c68f4503e0 | ||
|
|
4e19b57b0e | ||
|
|
8ed179c937 |
8
.gdbinit
Normal file
8
.gdbinit
Normal file
@@ -0,0 +1,8 @@
|
||||
# GDB may have ./.gdbinit loading disabled by default. In that case you can
|
||||
# follow the instructions it prints. They boil down to adding the following to
|
||||
# your home directory's ~/.gdbinit file:
|
||||
#
|
||||
# add-auto-load-safe-path /path/to/qemu/.gdbinit
|
||||
|
||||
# Load QEMU-specific sub-commands and settings
|
||||
source scripts/qemu-gdb.py
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -50,6 +50,7 @@
|
||||
/qemu-version.h.tmp
|
||||
/module_block.h
|
||||
/vscclient
|
||||
/vhost-user-scsi
|
||||
/fsdev/virtfs-proxy-helper
|
||||
*.[1-9]
|
||||
*.a
|
||||
@@ -99,14 +100,14 @@
|
||||
/pc-bios/optionrom/kvmvapic.img
|
||||
/pc-bios/s390-ccw/s390-ccw.elf
|
||||
/pc-bios/s390-ccw/s390-ccw.img
|
||||
/docs/qemu-ga-qapi.texi
|
||||
/docs/qemu-ga-ref.html
|
||||
/docs/qemu-ga-ref.info*
|
||||
/docs/qemu-ga-ref.txt
|
||||
/docs/qemu-qmp-qapi.texi
|
||||
/docs/qemu-qmp-ref.html
|
||||
/docs/qemu-qmp-ref.info*
|
||||
/docs/qemu-qmp-ref.txt
|
||||
/docs/interop/qemu-ga-qapi.texi
|
||||
/docs/interop/qemu-ga-ref.html
|
||||
/docs/interop/qemu-ga-ref.info*
|
||||
/docs/interop/qemu-ga-ref.txt
|
||||
/docs/interop/qemu-qmp-qapi.texi
|
||||
/docs/interop/qemu-qmp-ref.html
|
||||
/docs/interop/qemu-qmp-ref.info*
|
||||
/docs/interop/qemu-qmp-ref.txt
|
||||
/docs/version.texi
|
||||
*.tps
|
||||
.stgit-*
|
||||
|
||||
77
Makefile
77
Makefile
@@ -207,8 +207,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
||||
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7
|
||||
DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.txt docs/qemu-ga-ref.7
|
||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||
ifdef CONFIG_VIRTFS
|
||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||
endif
|
||||
@@ -269,6 +269,7 @@ dummy := $(call unnest-vars,, \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
libvhost-user-obj-y \
|
||||
vhost-user-scsi-obj-y \
|
||||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
@@ -473,6 +474,8 @@ ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y)
|
||||
$(call LINK, $^)
|
||||
|
||||
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
||||
$(call quiet-command,$(PYTHON) $< $@ \
|
||||
@@ -519,11 +522,12 @@ distclean: clean
|
||||
rm -f qemu-doc.vr qemu-doc.txt
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
rm -f docs/qemu-ga-qapi.texi docs/qemu-qmp-qapi.texi docs/version.texi
|
||||
rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
|
||||
rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
||||
rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
||||
rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
||||
rm -f docs/version.texi
|
||||
rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi
|
||||
rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7
|
||||
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
@@ -562,13 +566,13 @@ install-doc: $(DOCS)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
ifdef CONFIG_POSIX
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
ifneq ($(TOOLS),)
|
||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
@@ -576,9 +580,9 @@ ifneq ($(TOOLS),)
|
||||
endif
|
||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_VIRTFS
|
||||
@@ -666,28 +670,27 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
|
||||
|
||||
# documentation
|
||||
MAKEINFO=makeinfo
|
||||
MAKEINFOFLAGS=--no-split --number-sections -I docs
|
||||
TEXIFLAG=$(if $(V),,--quiet)
|
||||
MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
|
||||
MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
|
||||
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)"
|
||||
TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
|
||||
|
||||
docs/version.texi: $(SRC_PATH)/VERSION
|
||||
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
|
||||
|
||||
%.html: %.texi
|
||||
%.html: %.texi docs/version.texi
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||
--html $< -o $@,"GEN","$@")
|
||||
|
||||
%.info: %.texi
|
||||
%.info: %.texi docs/version.texi
|
||||
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
|
||||
|
||||
%.txt: %.texi
|
||||
%.txt: %.texi docs/version.texi
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||
--plaintext $< -o $@,"GEN","$@")
|
||||
|
||||
%.pdf: %.texi
|
||||
$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I docs $< -o $@,"GEN","$@")
|
||||
|
||||
docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.txt docs/qemu-ga-ref.pdf docs/qemu-ga-ref.7.pod: docs/version.texi
|
||||
docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.pod: docs/version.texi
|
||||
%.pdf: %.texi docs/version.texi
|
||||
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
|
||||
|
||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
@@ -701,12 +704,12 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
|
||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
|
||||
docs/qemu-qmp-qapi.texi docs/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
|
||||
docs/interop/qemu-qmp-qapi.texi docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
|
||||
|
||||
docs/qemu-qmp-qapi.texi: $(qapi-modules)
|
||||
docs/interop/qemu-qmp-qapi.texi: $(qapi-modules)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
||||
|
||||
docs/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
|
||||
docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
||||
|
||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
@@ -716,21 +719,25 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||
qemu-ga.8: qemu-ga.texi
|
||||
|
||||
html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
||||
info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
|
||||
pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
||||
txt: qemu-doc.txt docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
|
||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi
|
||||
|
||||
docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
|
||||
docs/qemu-ga-ref.texi docs/qemu-ga-qapi.texi
|
||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||
docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7: \
|
||||
docs/interop/qemu-ga-ref.texi docs/interop/qemu-ga-qapi.texi
|
||||
|
||||
docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
|
||||
docs/qemu-qmp-ref.texi docs/qemu-qmp-qapi.texi
|
||||
docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
|
||||
docs/interop/qemu-qmp-ref.info docs/interop/qemu-qmp-ref.pdf \
|
||||
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
||||
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
||||
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
@@ -791,9 +798,11 @@ endif # CONFIG_WIN
|
||||
|
||||
# Add a dependency on the generated files, so that they are always
|
||||
# rebuilt before other object files
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||
Makefile: $(GENERATED_FILES)
|
||||
endif
|
||||
endif
|
||||
|
||||
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
|
||||
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
|
||||
|
||||
@@ -52,7 +52,6 @@ common-obj-y += migration/
|
||||
|
||||
common-obj-y += audio/
|
||||
common-obj-y += hw/
|
||||
common-obj-y += accel.o
|
||||
|
||||
common-obj-y += replay/
|
||||
|
||||
@@ -111,6 +110,10 @@ qga-vss-dll-obj-y = qga/
|
||||
ivshmem-client-obj-y = contrib/ivshmem-client/
|
||||
ivshmem-server-obj-y = contrib/ivshmem-server/
|
||||
libvhost-user-obj-y = contrib/libvhost-user/
|
||||
vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
|
||||
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
|
||||
vhost-user-scsi-obj-y += contrib/libvhost-user/libvhost-user.o
|
||||
|
||||
######################################################################
|
||||
trace-events-subdirs =
|
||||
@@ -163,6 +166,8 @@ trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += linux-user
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += accel/kvm
|
||||
|
||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||
|
||||
|
||||
@@ -88,20 +88,17 @@ all: $(PROGS) stap
|
||||
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
obj-y = exec.o translate-all.o cpu-exec.o
|
||||
obj-y += translate-common.o
|
||||
obj-y += cpu-exec-common.o
|
||||
obj-y += exec.o
|
||||
obj-y += accel/
|
||||
obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += tci.o
|
||||
obj-y += tcg/tcg-common.o
|
||||
obj-y += tcg/tcg-common.o tcg/tcg-runtime.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target/$(TARGET_BASE_ARCH)/
|
||||
obj-y += disas.o
|
||||
obj-y += tcg-runtime.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o
|
||||
@@ -142,8 +139,7 @@ ifdef CONFIG_SOFTMMU
|
||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||
obj-y += qtest.o bootdevice.o
|
||||
obj-y += hw/
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
obj-y += memory.o cputlb.o
|
||||
obj-y += memory.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += dump.o
|
||||
obj-y += migration/ram.o
|
||||
|
||||
4
accel/Makefile.objs
Normal file
4
accel/Makefile.objs
Normal file
@@ -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 "qom/object.h"
|
||||
|
||||
int tcg_tb_size;
|
||||
static bool tcg_allowed = true;
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const TypeInfo accel_type = {
|
||||
.name = TYPE_ACCEL,
|
||||
.parent = TYPE_OBJECT,
|
||||
@@ -129,27 +120,9 @@ void configure_accelerator(MachineState *ms)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->allowed = &tcg_allowed;
|
||||
}
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
|
||||
static const TypeInfo tcg_accel_type = {
|
||||
.name = TYPE_TCG_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = tcg_accel_class_init,
|
||||
};
|
||||
|
||||
static void register_accel_types(void)
|
||||
{
|
||||
type_register_static(&accel_type);
|
||||
type_register_static(&tcg_accel_type);
|
||||
}
|
||||
|
||||
type_init(register_accel_types);
|
||||
1
accel/kvm/Makefile.objs
Normal file
1
accel/kvm/Makefile.objs
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/msix.h"
|
||||
@@ -35,7 +36,7 @@
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
@@ -1976,6 +1977,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu_exec_start(cpu);
|
||||
|
||||
do {
|
||||
MemTxAttrs attrs;
|
||||
@@ -2105,6 +2107,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||
}
|
||||
} while (ret == 0);
|
||||
|
||||
cpu_exec_end(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -2216,8 +2219,8 @@ int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr)
|
||||
return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1;
|
||||
}
|
||||
|
||||
void kvm_device_access(int fd, int group, uint64_t attr,
|
||||
void *val, bool write)
|
||||
int kvm_device_access(int fd, int group, uint64_t attr,
|
||||
void *val, bool write, Error **errp)
|
||||
{
|
||||
struct kvm_device_attr kvmattr;
|
||||
int err;
|
||||
@@ -2231,11 +2234,12 @@ void kvm_device_access(int fd, int group, uint64_t attr,
|
||||
write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR,
|
||||
&kvmattr);
|
||||
if (err < 0) {
|
||||
error_report("KVM_%s_DEVICE_ATTR failed: %s",
|
||||
write ? "SET" : "GET", strerror(-err));
|
||||
error_printf("Group %d attr 0x%016" PRIx64 "\n", group, attr);
|
||||
abort();
|
||||
error_setg_errno(errp, -err,
|
||||
"KVM_%s_DEVICE_ATTR failed: Group %d "
|
||||
"attr 0x%016" PRIx64,
|
||||
write ? "SET" : "GET", group, attr);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Return 1 on success, 0 on failure */
|
||||
15
accel/kvm/trace-events
Normal file
15
accel/kvm/trace-events
Normal file
@@ -0,0 +1,15 @@
|
||||
# Trace events for debugging and performance instrumentation
|
||||
|
||||
# kvm-all.c
|
||||
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||
kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
|
||||
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
||||
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
|
||||
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
|
||||
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
|
||||
kvm_irqchip_commit_routes(void) ""
|
||||
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
||||
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
||||
kvm_irqchip_release_virq(int virq) "virq %d"
|
||||
|
||||
1
accel/stubs/Makefile.objs
Normal file
1
accel/stubs/Makefile.objs
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
3
accel/tcg/Makefile.objs
Normal file
3
accel/tcg/Makefile.objs
Normal file
@@ -0,0 +1,3 @@
|
||||
obj-$(CONFIG_SOFTMMU) += tcg-all.o
|
||||
obj-$(CONFIG_SOFTMMU) += cputlb.o
|
||||
obj-y += cpu-exec.o cpu-exec-common.o translate-all.o translate-common.o
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg.h"
|
||||
61
accel/tcg/tcg-all.c
Normal file
61
accel/tcg/tcg-all.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* QEMU System Emulator, accelerator interfaces
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
* Copyright (c) 2014 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
int tcg_tb_size;
|
||||
static bool tcg_allowed = true;
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->allowed = &tcg_allowed;
|
||||
}
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
|
||||
static const TypeInfo tcg_accel_type = {
|
||||
.name = TYPE_TCG_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = tcg_accel_class_init,
|
||||
};
|
||||
|
||||
static void register_accel_types(void)
|
||||
{
|
||||
type_register_static(&tcg_accel_type);
|
||||
}
|
||||
|
||||
type_init(register_accel_types);
|
||||
10
accel/tcg/trace-events
Normal file
10
accel/tcg/trace-events
Normal file
@@ -0,0 +1,10 @@
|
||||
# Trace events for debugging and performance instrumentation
|
||||
|
||||
# TCG related tracing (mostly disabled by default)
|
||||
# cpu-exec.c
|
||||
disable exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
disable exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
disable exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=%x"
|
||||
|
||||
# translate-all.c
|
||||
translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "qemu-common.h"
|
||||
#define NO_CPU_IO_DEFS
|
||||
#include "cpu.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg.h"
|
||||
10
block.c
10
block.c
@@ -320,6 +320,8 @@ BlockDriverState *bdrv_new(void)
|
||||
QLIST_INIT(&bs->op_blockers[i]);
|
||||
}
|
||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||
qemu_co_mutex_init(&bs->reqs_lock);
|
||||
qemu_mutex_init(&bs->dirty_bitmap_mutex);
|
||||
bs->refcnt = 1;
|
||||
bs->aio_context = qemu_get_aio_context();
|
||||
|
||||
@@ -1300,7 +1302,9 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
||||
/* bdrv_new() and bdrv_close() make it so */
|
||||
assert(atomic_read(&bs->copy_on_read) == 0);
|
||||
|
||||
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
|
||||
if (!bs->read_only) {
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
@@ -3063,7 +3067,7 @@ static void bdrv_close(BlockDriverState *bs)
|
||||
|
||||
g_free(bs->opaque);
|
||||
bs->opaque = NULL;
|
||||
bs->copy_on_read = 0;
|
||||
atomic_set(&bs->copy_on_read, 0);
|
||||
bs->backing_file[0] = '\0';
|
||||
bs->backing_format[0] = '\0';
|
||||
bs->total_sectors = 0;
|
||||
@@ -3422,7 +3426,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
|
||||
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
||||
bdrv_dirty_bitmap_truncate(bs);
|
||||
bdrv_parent_cb_resize(bs);
|
||||
++bs->write_gen;
|
||||
atomic_inc(&bs->write_gen);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -32,23 +32,28 @@
|
||||
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
||||
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
||||
|
||||
void block_acct_init(BlockAcctStats *stats, bool account_invalid,
|
||||
bool account_failed)
|
||||
void block_acct_init(BlockAcctStats *stats)
|
||||
{
|
||||
stats->account_invalid = account_invalid;
|
||||
stats->account_failed = account_failed;
|
||||
|
||||
qemu_mutex_init(&stats->lock);
|
||||
if (qtest_enabled()) {
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_setup(BlockAcctStats *stats, bool account_invalid,
|
||||
bool account_failed)
|
||||
{
|
||||
stats->account_invalid = account_invalid;
|
||||
stats->account_failed = account_failed;
|
||||
}
|
||||
|
||||
void block_acct_cleanup(BlockAcctStats *stats)
|
||||
{
|
||||
BlockAcctTimedStats *s, *next;
|
||||
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
|
||||
g_free(s);
|
||||
}
|
||||
qemu_mutex_destroy(&stats->lock);
|
||||
}
|
||||
|
||||
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
||||
@@ -58,12 +63,15 @@ void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
||||
|
||||
s = g_new0(BlockAcctTimedStats, 1);
|
||||
s->interval_length = interval_length;
|
||||
s->stats = stats;
|
||||
qemu_mutex_lock(&stats->lock);
|
||||
QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
timed_average_init(&s->latency[i], clock_type,
|
||||
(uint64_t) interval_length * NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
qemu_mutex_unlock(&stats->lock);
|
||||
}
|
||||
|
||||
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
||||
@@ -86,7 +94,8 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
bool failed)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
@@ -98,31 +107,16 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
qemu_mutex_lock(&stats->lock);
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
if (failed) {
|
||||
stats->failed_ops[cookie->type]++;
|
||||
} else {
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
stats->failed_ops[cookie->type]++;
|
||||
|
||||
if (stats->account_failed) {
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
latency_ns = qtest_latency_ns;
|
||||
}
|
||||
|
||||
if (!failed || stats->account_failed) {
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
@@ -130,29 +124,45 @@ void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&stats->lock);
|
||||
}
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
block_account_one_io(stats, cookie, false);
|
||||
}
|
||||
|
||||
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
block_account_one_io(stats, cookie, true);
|
||||
}
|
||||
|
||||
void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
|
||||
{
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
/* block_acct_done() and block_acct_failed() update
|
||||
* total_time_ns[], but this one does not. The reason is that
|
||||
* invalid requests are accounted during their submission,
|
||||
* therefore there's no actual I/O involved. */
|
||||
|
||||
/* block_account_one_io() updates total_time_ns[], but this one does
|
||||
* not. The reason is that invalid requests are accounted during their
|
||||
* submission, therefore there's no actual I/O involved.
|
||||
*/
|
||||
qemu_mutex_lock(&stats->lock);
|
||||
stats->invalid_ops[type]++;
|
||||
|
||||
if (stats->account_invalid) {
|
||||
stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
|
||||
}
|
||||
qemu_mutex_unlock(&stats->lock);
|
||||
}
|
||||
|
||||
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
int num_requests)
|
||||
{
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
qemu_mutex_lock(&stats->lock);
|
||||
stats->merged[type] += num_requests;
|
||||
qemu_mutex_unlock(&stats->lock);
|
||||
}
|
||||
|
||||
int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
|
||||
@@ -167,7 +177,9 @@ double block_acct_queue_depth(BlockAcctTimedStats *stats,
|
||||
|
||||
assert(type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
qemu_mutex_lock(&stats->stats->lock);
|
||||
sum = timed_average_sum(&stats->latency[type], &elapsed);
|
||||
qemu_mutex_unlock(&stats->stats->lock);
|
||||
|
||||
return (double) sum / elapsed;
|
||||
}
|
||||
|
||||
@@ -216,8 +216,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
blk->shared_perm = shared_perm;
|
||||
blk_set_enable_write_cache(blk, true);
|
||||
|
||||
qemu_co_mutex_init(&blk->public.throttled_reqs_lock);
|
||||
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
|
||||
qemu_co_queue_init(&blk->public.throttled_reqs[1]);
|
||||
block_acct_init(&blk->stats);
|
||||
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
notifier_list_init(&blk->insert_bs_notifiers);
|
||||
@@ -1953,7 +1955,7 @@ static void blk_root_drained_begin(BdrvChild *child)
|
||||
/* Note that blk->root may not be accessible here yet if we are just
|
||||
* attaching to a BlockDriverState that is drained. Use child instead. */
|
||||
|
||||
if (blk->public.io_limits_disabled++ == 0) {
|
||||
if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) {
|
||||
throttle_group_restart_blk(blk);
|
||||
}
|
||||
}
|
||||
@@ -1964,7 +1966,7 @@ static void blk_root_drained_end(BdrvChild *child)
|
||||
assert(blk->quiesce_counter);
|
||||
|
||||
assert(blk->public.io_limits_disabled);
|
||||
--blk->public.io_limits_disabled;
|
||||
atomic_dec(&blk->public.io_limits_disabled);
|
||||
|
||||
if (--blk->quiesce_counter == 0) {
|
||||
if (blk->dev_ops && blk->dev_ops->drained_end) {
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
||||
*/
|
||||
struct BdrvDirtyBitmap {
|
||||
QemuMutex *mutex;
|
||||
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
||||
HBitmap *meta; /* Meta dirty bitmap */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||
@@ -52,6 +53,27 @@ struct BdrvDirtyBitmapIter {
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
};
|
||||
|
||||
static inline void bdrv_dirty_bitmaps_lock(BlockDriverState *bs)
|
||||
{
|
||||
qemu_mutex_lock(&bs->dirty_bitmap_mutex);
|
||||
}
|
||||
|
||||
static inline void bdrv_dirty_bitmaps_unlock(BlockDriverState *bs)
|
||||
{
|
||||
qemu_mutex_unlock(&bs->dirty_bitmap_mutex);
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called with BQL or dirty_bitmap lock taken. */
|
||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
@@ -65,6 +87,7 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
@@ -72,6 +95,7 @@ void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||
bitmap->name = NULL;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
const char *name,
|
||||
@@ -96,11 +120,14 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
return NULL;
|
||||
}
|
||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||
bitmap->mutex = &bs->dirty_bitmap_mutex;
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
||||
bitmap->size = bitmap_size;
|
||||
bitmap->name = g_strdup(name);
|
||||
bitmap->disabled = false;
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@@ -119,20 +146,24 @@ void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int chunk_size)
|
||||
{
|
||||
assert(!bitmap->meta);
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bitmap->meta = hbitmap_create_meta(bitmap->bitmap,
|
||||
chunk_size * BITS_PER_BYTE);
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(bitmap->meta);
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
hbitmap_free_meta(bitmap->bitmap);
|
||||
bitmap->meta = NULL;
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||
int nb_sectors)
|
||||
int bdrv_dirty_bitmap_get_meta_locked(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||
int nb_sectors)
|
||||
{
|
||||
uint64_t i;
|
||||
int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta);
|
||||
@@ -147,11 +178,26 @@ int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
|
||||
return false;
|
||||
}
|
||||
|
||||
int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||
int nb_sectors)
|
||||
{
|
||||
bool dirty;
|
||||
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
dirty = bdrv_dirty_bitmap_get_meta_locked(bs, bitmap, sector, nb_sectors);
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||
int nb_sectors)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
hbitmap_reset(bitmap->meta, sector, nb_sectors);
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap)
|
||||
@@ -164,16 +210,19 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap)
|
||||
return bitmap->name;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !(bitmap->disabled || bitmap->successor);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
@@ -188,6 +237,7 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
/**
|
||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||
* Requires that the bitmap is not frozen and has no successor.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
||||
@@ -220,6 +270,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
/**
|
||||
* For a bitmap with a successor, yield our name to the successor,
|
||||
* delete the old bitmap, and return a handle to the new bitmap.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
@@ -247,6 +298,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
* In cases of failure where we can no longer safely delete the parent,
|
||||
* we may wish to re-join the parent and child/successor.
|
||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
@@ -271,25 +323,30 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
|
||||
/**
|
||||
* Truncates _all_ bitmaps attached to a BDS.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t size = bdrv_nb_sectors(bs);
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
assert(!bitmap->active_iterators);
|
||||
hbitmap_truncate(bitmap->bitmap, size);
|
||||
bitmap->size = size;
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
bool only_named)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
||||
assert(!bm->active_iterators);
|
||||
@@ -301,15 +358,19 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bitmap) {
|
||||
abort();
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
||||
@@ -318,18 +379,21 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
/**
|
||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = true;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
@@ -342,6 +406,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
BlockDirtyInfoList *list = NULL;
|
||||
BlockDirtyInfoList **plist = &list;
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
||||
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
||||
@@ -354,12 +419,14 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
*plist = entry;
|
||||
plist = &entry->next;
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
int64_t sector)
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
int bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
int64_t sector)
|
||||
{
|
||||
if (bitmap) {
|
||||
return hbitmap_get(bitmap->bitmap, sector);
|
||||
@@ -432,23 +499,42 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
||||
return hbitmap_iter_next(&iter->hbi);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int64_t nr_sectors)
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int64_t nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int64_t nr_sectors)
|
||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int64_t nr_sectors)
|
||||
{
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
bdrv_set_dirty_bitmap_locked(bitmap, cur_sector, nr_sectors);
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int64_t nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int64_t nr_sectors)
|
||||
{
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
bdrv_reset_dirty_bitmap_locked(bitmap, cur_sector, nr_sectors);
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
if (!out) {
|
||||
hbitmap_reset_all(bitmap->bitmap);
|
||||
} else {
|
||||
@@ -457,6 +543,7 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
hbitmap_granularity(backup));
|
||||
*out = backup;
|
||||
}
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||
@@ -508,12 +595,19 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int64_t nr_sectors)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
|
||||
if (QLIST_EMPTY(&bs->dirty_bitmaps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -493,8 +493,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
||||
Error *local_err = NULL;
|
||||
char *str = NULL;
|
||||
const char *ptr;
|
||||
size_t num_servers;
|
||||
int i, type;
|
||||
int i, type, num_servers;
|
||||
|
||||
/* create opts info from runtime_json_opts list */
|
||||
opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
|
||||
|
||||
51
block/io.c
51
block/io.c
@@ -130,13 +130,13 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
*/
|
||||
void bdrv_enable_copy_on_read(BlockDriverState *bs)
|
||||
{
|
||||
bs->copy_on_read++;
|
||||
atomic_inc(&bs->copy_on_read);
|
||||
}
|
||||
|
||||
void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
{
|
||||
assert(bs->copy_on_read > 0);
|
||||
bs->copy_on_read--;
|
||||
int old = atomic_fetch_dec(&bs->copy_on_read);
|
||||
assert(old >= 1);
|
||||
}
|
||||
|
||||
/* Check if any requests are in-flight (including throttled requests) */
|
||||
@@ -241,7 +241,7 @@ void bdrv_drained_begin(BlockDriverState *bs)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bs->quiesce_counter++) {
|
||||
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
||||
aio_disable_external(bdrv_get_aio_context(bs));
|
||||
bdrv_parent_drained_begin(bs);
|
||||
}
|
||||
@@ -252,7 +252,7 @@ void bdrv_drained_begin(BlockDriverState *bs)
|
||||
void bdrv_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
assert(bs->quiesce_counter > 0);
|
||||
if (--bs->quiesce_counter > 0) {
|
||||
if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -375,11 +375,13 @@ void bdrv_drain_all(void)
|
||||
static void tracked_request_end(BdrvTrackedRequest *req)
|
||||
{
|
||||
if (req->serialising) {
|
||||
req->bs->serialising_in_flight--;
|
||||
atomic_dec(&req->bs->serialising_in_flight);
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&req->bs->reqs_lock);
|
||||
QLIST_REMOVE(req, list);
|
||||
qemu_co_queue_restart_all(&req->wait_queue);
|
||||
qemu_co_mutex_unlock(&req->bs->reqs_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,7 +406,9 @@ static void tracked_request_begin(BdrvTrackedRequest *req,
|
||||
|
||||
qemu_co_queue_init(&req->wait_queue);
|
||||
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
}
|
||||
|
||||
static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
||||
@@ -414,7 +418,7 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
||||
- overlap_offset;
|
||||
|
||||
if (!req->serialising) {
|
||||
req->bs->serialising_in_flight++;
|
||||
atomic_inc(&req->bs->serialising_in_flight);
|
||||
req->serialising = true;
|
||||
}
|
||||
|
||||
@@ -501,7 +505,8 @@ static void dummy_bh_cb(void *opaque)
|
||||
|
||||
void bdrv_wakeup(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->wakeup) {
|
||||
/* The barrier (or an atomic op) is in the caller. */
|
||||
if (atomic_read(&bs->wakeup)) {
|
||||
aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
|
||||
}
|
||||
}
|
||||
@@ -519,12 +524,13 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
||||
bool retry;
|
||||
bool waited = false;
|
||||
|
||||
if (!bs->serialising_in_flight) {
|
||||
if (!atomic_read(&bs->serialising_in_flight)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
retry = false;
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
QLIST_FOREACH(req, &bs->tracked_requests, list) {
|
||||
if (req == self || (!req->serialising && !self->serialising)) {
|
||||
continue;
|
||||
@@ -543,7 +549,7 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
||||
* (instead of producing a deadlock in the former case). */
|
||||
if (!req->waiting_for) {
|
||||
self->waiting_for = req;
|
||||
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||
qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
|
||||
self->waiting_for = NULL;
|
||||
retry = true;
|
||||
waited = true;
|
||||
@@ -551,6 +557,7 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
} while (retry);
|
||||
|
||||
return waited;
|
||||
@@ -1144,7 +1151,7 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
|
||||
bdrv_inc_in_flight(bs);
|
||||
|
||||
/* Don't do copy-on-read if we read data before write operation */
|
||||
if (bs->copy_on_read && !(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
if (atomic_read(&bs->copy_on_read) && !(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
flags |= BDRV_REQ_COPY_ON_READ;
|
||||
}
|
||||
|
||||
@@ -1401,12 +1408,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
|
||||
}
|
||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
|
||||
|
||||
++bs->write_gen;
|
||||
atomic_inc(&bs->write_gen);
|
||||
bdrv_set_dirty(bs, start_sector, end_sector - start_sector);
|
||||
|
||||
if (bs->wr_highest_offset < offset + bytes) {
|
||||
bs->wr_highest_offset = offset + bytes;
|
||||
}
|
||||
stat64_max(&bs->wr_highest_offset, offset + bytes);
|
||||
|
||||
if (ret >= 0) {
|
||||
bs->total_sectors = MAX(bs->total_sectors, end_sector);
|
||||
@@ -2292,14 +2297,17 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
goto early_exit;
|
||||
}
|
||||
|
||||
current_gen = bs->write_gen;
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
current_gen = atomic_read(&bs->write_gen);
|
||||
|
||||
/* Wait until any previous flushes are completed */
|
||||
while (bs->active_flush_req) {
|
||||
qemu_co_queue_wait(&bs->flush_queue, NULL);
|
||||
qemu_co_queue_wait(&bs->flush_queue, &bs->reqs_lock);
|
||||
}
|
||||
|
||||
/* Flushes reach this point in nondecreasing current_gen order. */
|
||||
bs->active_flush_req = true;
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
|
||||
/* Write back all layers by calling one driver function */
|
||||
if (bs->drv->bdrv_co_flush) {
|
||||
@@ -2371,9 +2379,12 @@ out:
|
||||
if (ret == 0) {
|
||||
bs->flushed_gen = current_gen;
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
bs->active_flush_req = false;
|
||||
/* Return value is ignored - it's ok if wait queue is empty */
|
||||
qemu_co_queue_next(&bs->flush_queue);
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
|
||||
early_exit:
|
||||
bdrv_dec_in_flight(bs);
|
||||
@@ -2517,7 +2528,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
++bs->write_gen;
|
||||
atomic_inc(&bs->write_gen);
|
||||
bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS,
|
||||
req.bytes >> BDRV_SECTOR_BITS);
|
||||
tracked_request_end(&req);
|
||||
@@ -2644,7 +2655,7 @@ void bdrv_io_plug(BlockDriverState *bs)
|
||||
bdrv_io_plug(child->bs);
|
||||
}
|
||||
|
||||
if (bs->io_plugged++ == 0) {
|
||||
if (atomic_fetch_inc(&bs->io_plugged) == 0) {
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_io_plug) {
|
||||
drv->bdrv_io_plug(bs);
|
||||
@@ -2657,7 +2668,7 @@ void bdrv_io_unplug(BlockDriverState *bs)
|
||||
BdrvChild *child;
|
||||
|
||||
assert(bs->io_plugged);
|
||||
if (--bs->io_plugged == 0) {
|
||||
if (atomic_fetch_dec(&bs->io_plugged) == 1) {
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_io_unplug) {
|
||||
drv->bdrv_io_unplug(bs);
|
||||
|
||||
@@ -1732,6 +1732,10 @@ static QemuOptsList runtime_opts = {
|
||||
.name = "timeout",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -1747,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
char *initiator_name = NULL;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *transport_name, *portal, *target;
|
||||
const char *transport_name, *portal, *target, *filename;
|
||||
#if LIBISCSI_API_VERSION >= (20160603)
|
||||
enum iscsi_transport_type transport;
|
||||
#endif
|
||||
int i, ret = 0, timeout = 0, lun;
|
||||
|
||||
/* If we are given a filename, parse the filename, with precedence given to
|
||||
* filename encoded options */
|
||||
filename = qdict_get_try_str(options, "filename");
|
||||
if (filename) {
|
||||
error_report("Warning: 'filename' option specified. "
|
||||
"This is an unsupported option, and may be deprecated "
|
||||
"in the future");
|
||||
iscsi_parse_filename(filename, options, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
@@ -1967,6 +1986,7 @@ out:
|
||||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -342,6 +342,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT,
|
||||
MAX_IO_SECTORS);
|
||||
|
||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||
sector_num = bdrv_dirty_iter_next(s->dbi);
|
||||
if (sector_num < 0) {
|
||||
bdrv_set_dirty_iter(s->dbi, 0);
|
||||
@@ -349,6 +350,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||
assert(sector_num >= 0);
|
||||
}
|
||||
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||
|
||||
first_chunk = sector_num / sectors_per_chunk;
|
||||
while (test_bit(first_chunk, s->in_flight_bitmap)) {
|
||||
@@ -360,12 +362,13 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
|
||||
/* Find the number of consective dirty chunks following the first dirty
|
||||
* one, and wait for in flight requests in them. */
|
||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||
int64_t next_dirty;
|
||||
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
||||
int64_t next_chunk = next_sector / sectors_per_chunk;
|
||||
if (next_sector >= end ||
|
||||
!bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
||||
!bdrv_get_dirty_locked(source, s->dirty_bitmap, next_sector)) {
|
||||
break;
|
||||
}
|
||||
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
@@ -386,8 +389,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
* calling bdrv_get_block_status_above could yield - if some blocks are
|
||||
* marked dirty in this window, we need to know.
|
||||
*/
|
||||
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num,
|
||||
nb_chunks * sectors_per_chunk);
|
||||
bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, sector_num,
|
||||
nb_chunks * sectors_per_chunk);
|
||||
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||
|
||||
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
||||
while (nb_chunks > 0 && sector_num < end) {
|
||||
int64_t ret;
|
||||
@@ -506,6 +511,8 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
|
||||
Error *local_err = NULL;
|
||||
|
||||
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
||||
|
||||
/* Make sure that the source BDS doesn't go away before we called
|
||||
* block_job_completed(). */
|
||||
bdrv_ref(src);
|
||||
@@ -904,7 +911,6 @@ immediate_exit:
|
||||
g_free(s->cow_bitmap);
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_dirty_iter_free(s->dbi);
|
||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
|
||||
@@ -144,8 +144,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
qio_channel_set_cork(s->ioc, true);
|
||||
rc = nbd_send_request(s->ioc, request);
|
||||
if (rc >= 0) {
|
||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
|
||||
false, NULL);
|
||||
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, false,
|
||||
NULL);
|
||||
if (ret != request->len) {
|
||||
rc = -EIO;
|
||||
}
|
||||
@@ -173,8 +173,8 @@ static void nbd_co_receive_reply(NBDClientSession *s,
|
||||
reply->error = EIO;
|
||||
} else {
|
||||
if (qiov && reply->error == 0) {
|
||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
|
||||
true, NULL);
|
||||
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, true,
|
||||
NULL);
|
||||
if (ret != request->len) {
|
||||
reply->error = EIO;
|
||||
}
|
||||
|
||||
@@ -730,7 +730,9 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
|
||||
if (task->ret < 0) {
|
||||
error_report("NFS Error: %s", nfs_get_error(nfs));
|
||||
}
|
||||
task->complete = 1;
|
||||
|
||||
/* Set task->complete before reading bs->wakeup. */
|
||||
atomic_mb_set(&task->complete, 1);
|
||||
bdrv_wakeup(task->bs);
|
||||
}
|
||||
|
||||
|
||||
@@ -441,7 +441,7 @@ static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
|
||||
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
||||
}
|
||||
|
||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||
s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
|
||||
22
block/rbd.c
22
block/rbd.c
@@ -340,6 +340,10 @@ static QemuOptsList runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Legacy rados key/value option parameters",
|
||||
},
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -541,12 +545,27 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
const char *pool, *snap, *conf, *user, *image_name, *keypairs;
|
||||
const char *secretid;
|
||||
const char *secretid, *filename;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
char *mon_host = NULL;
|
||||
int r;
|
||||
|
||||
/* If we are given a filename, parse the filename, with precedence given to
|
||||
* filename encoded options */
|
||||
filename = qdict_get_try_str(options, "filename");
|
||||
if (filename) {
|
||||
error_report("Warning: 'filename' option specified. "
|
||||
"This is an unsupported option, and may be deprecated "
|
||||
"in the future");
|
||||
qemu_rbd_parse_filename(filename, options, &local_err);
|
||||
if (local_err) {
|
||||
r = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
@@ -665,6 +684,7 @@ failed_shutdown:
|
||||
failed_opts:
|
||||
qemu_opts_del(opts);
|
||||
g_free(mon_host);
|
||||
exit:
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -698,7 +698,8 @@ out:
|
||||
|
||||
srco->co = NULL;
|
||||
srco->ret = ret;
|
||||
srco->finished = true;
|
||||
/* Set srco->finished before reading bs->wakeup. */
|
||||
atomic_mb_set(&srco->finished, true);
|
||||
if (srco->bs) {
|
||||
bdrv_wakeup(srco->bs);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
bool must_wait;
|
||||
|
||||
if (blkp->io_limits_disabled) {
|
||||
if (atomic_read(&blkp->io_limits_disabled)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -260,6 +260,25 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
|
||||
return must_wait;
|
||||
}
|
||||
|
||||
/* Start the next pending I/O request for a BlockBackend. Return whether
|
||||
* any request was actually pending.
|
||||
*
|
||||
* @blk: the current BlockBackend
|
||||
* @is_write: the type of operation (read/write)
|
||||
*/
|
||||
static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
|
||||
bool is_write)
|
||||
{
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
bool ret;
|
||||
|
||||
qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
|
||||
ret = qemu_co_queue_next(&blkp->throttled_reqs[is_write]);
|
||||
qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Look for the next pending I/O request and schedule it.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
@@ -287,12 +306,12 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
|
||||
if (!must_wait) {
|
||||
/* Give preference to requests from the current blk */
|
||||
if (qemu_in_coroutine() &&
|
||||
qemu_co_queue_next(&blkp->throttled_reqs[is_write])) {
|
||||
throttle_group_co_restart_queue(blk, is_write)) {
|
||||
token = blk;
|
||||
} else {
|
||||
ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
|
||||
int64_t now = qemu_clock_get_ns(tt->clock_type);
|
||||
timer_mod(tt->timers[is_write], now + 1);
|
||||
timer_mod(tt->timers[is_write], now);
|
||||
tg->any_timer_armed[is_write] = true;
|
||||
}
|
||||
tg->tokens[is_write] = token;
|
||||
@@ -326,7 +345,10 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
||||
if (must_wait || blkp->pending_reqs[is_write]) {
|
||||
blkp->pending_reqs[is_write]++;
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
qemu_co_queue_wait(&blkp->throttled_reqs[is_write], NULL);
|
||||
qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
|
||||
qemu_co_queue_wait(&blkp->throttled_reqs[is_write],
|
||||
&blkp->throttled_reqs_lock);
|
||||
qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
blkp->pending_reqs[is_write]--;
|
||||
}
|
||||
@@ -340,15 +362,50 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BlockBackend *blk;
|
||||
bool is_write;
|
||||
} RestartData;
|
||||
|
||||
static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
|
||||
{
|
||||
RestartData *data = opaque;
|
||||
BlockBackend *blk = data->blk;
|
||||
bool is_write = data->is_write;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||
bool empty_queue;
|
||||
|
||||
empty_queue = !throttle_group_co_restart_queue(blk, is_write);
|
||||
|
||||
/* If the request queue was empty then we have to take care of
|
||||
* scheduling the next one */
|
||||
if (empty_queue) {
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
schedule_next_request(blk, is_write);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
|
||||
{
|
||||
Coroutine *co;
|
||||
RestartData rd = {
|
||||
.blk = blk,
|
||||
.is_write = is_write
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd);
|
||||
aio_co_enter(blk_get_aio_context(blk), co);
|
||||
}
|
||||
|
||||
void throttle_group_restart_blk(BlockBackend *blk)
|
||||
{
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
while (qemu_co_enter_next(&blkp->throttled_reqs[i])) {
|
||||
;
|
||||
}
|
||||
if (blkp->throttle_state) {
|
||||
throttle_group_restart_queue(blk, 0);
|
||||
throttle_group_restart_queue(blk, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,8 +433,7 @@ void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
|
||||
throttle_config(ts, tt, cfg);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
qemu_co_enter_next(&blkp->throttled_reqs[0]);
|
||||
qemu_co_enter_next(&blkp->throttled_reqs[1]);
|
||||
throttle_group_restart_blk(blk);
|
||||
}
|
||||
|
||||
/* Get the throttle configuration from a particular group. Similar to
|
||||
@@ -408,7 +464,6 @@ static void timer_cb(BlockBackend *blk, bool is_write)
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleState *ts = blkp->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
bool empty_queue;
|
||||
|
||||
/* The timer has just been fired, so we can update the flag */
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
@@ -416,17 +471,7 @@ static void timer_cb(BlockBackend *blk, bool is_write)
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
/* Run the request that was waiting for this timer */
|
||||
aio_context_acquire(blk_get_aio_context(blk));
|
||||
empty_queue = !qemu_co_enter_next(&blkp->throttled_reqs[is_write]);
|
||||
aio_context_release(blk_get_aio_context(blk));
|
||||
|
||||
/* If the request queue was empty then we have to take care of
|
||||
* scheduling the next one */
|
||||
if (empty_queue) {
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
schedule_next_request(blk, is_write);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
throttle_group_restart_queue(blk, is_write);
|
||||
}
|
||||
|
||||
static void read_timer_cb(void *opaque)
|
||||
|
||||
@@ -27,6 +27,10 @@ typedef struct NBDServerData {
|
||||
|
||||
static NBDServerData *nbd_server;
|
||||
|
||||
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
{
|
||||
nbd_client_put(client);
|
||||
}
|
||||
|
||||
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||
gpointer opaque)
|
||||
@@ -46,7 +50,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
nbd_client_new(NULL, cioc,
|
||||
nbd_server->tlscreds, NULL,
|
||||
nbd_client_put);
|
||||
nbd_blockdev_client_closed);
|
||||
object_unref(OBJECT(cioc));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
48
blockdev.c
48
blockdev.c
@@ -595,7 +595,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
autostart = 0;
|
||||
}
|
||||
|
||||
block_acct_init(blk_get_stats(blk), account_invalid, account_failed);
|
||||
block_acct_setup(blk_get_stats(blk), account_invalid, account_failed);
|
||||
|
||||
if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) {
|
||||
blk_unref(blk);
|
||||
@@ -1362,12 +1362,10 @@ out_aio_context:
|
||||
static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
|
||||
const char *name,
|
||||
BlockDriverState **pbs,
|
||||
AioContext **paio,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
AioContext *aio_context;
|
||||
|
||||
if (!node) {
|
||||
error_setg(errp, "Node cannot be NULL");
|
||||
@@ -1383,29 +1381,17 @@ static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bitmap = bdrv_find_dirty_bitmap(bs, name);
|
||||
if (!bitmap) {
|
||||
error_setg(errp, "Dirty bitmap '%s' not found", name);
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pbs) {
|
||||
*pbs = bs;
|
||||
}
|
||||
if (paio) {
|
||||
*paio = aio_context;
|
||||
} else {
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
|
||||
fail:
|
||||
aio_context_release(aio_context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* New and old BlockDriverState structs for atomic group operations */
|
||||
@@ -1791,7 +1777,7 @@ static void external_snapshot_commit(BlkActionState *common)
|
||||
/* We don't need (or want) to use the transactional
|
||||
* bdrv_reopen_multiple() across all the entries at once, because we
|
||||
* don't want to abort all of them if one of them fails the reopen */
|
||||
if (!state->old_bs->copy_on_read) {
|
||||
if (!atomic_read(&state->old_bs->copy_on_read)) {
|
||||
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
|
||||
NULL);
|
||||
}
|
||||
@@ -2025,7 +2011,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
||||
action->name,
|
||||
&state->bs,
|
||||
&state->aio_context,
|
||||
errp);
|
||||
if (!state->bitmap) {
|
||||
return;
|
||||
@@ -2733,7 +2718,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BlockDriverState *bs;
|
||||
|
||||
if (!name || name[0] == '\0') {
|
||||
@@ -2746,14 +2730,11 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (has_granularity) {
|
||||
if (granularity < 512 || !is_power_of_2(granularity)) {
|
||||
error_setg(errp, "Granularity must be power of 2 "
|
||||
"and at least 512");
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Default to cluster size, if available: */
|
||||
@@ -2761,19 +2742,15 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
}
|
||||
|
||||
bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BlockDriverState *bs;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
|
||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
|
||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
||||
if (!bitmap || !bs) {
|
||||
return;
|
||||
}
|
||||
@@ -2782,13 +2759,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently frozen and cannot be removed",
|
||||
name);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
bdrv_dirty_bitmap_make_anon(bitmap);
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2798,11 +2772,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
BlockDriverState *bs;
|
||||
|
||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
|
||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
||||
if (!bitmap || !bs) {
|
||||
return;
|
||||
}
|
||||
@@ -2811,18 +2784,15 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently frozen and cannot be modified",
|
||||
name);
|
||||
goto out;
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently disabled and cannot be cleared",
|
||||
name);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_clear_dirty_bitmap(bitmap, NULL);
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
||||
|
||||
@@ -450,12 +450,12 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
||||
}
|
||||
if (strstart(filename, "/dev/parport", NULL) ||
|
||||
strstart(filename, "/dev/ppi", NULL)) {
|
||||
qemu_opt_set(opts, "backend", "parport", &error_abort);
|
||||
qemu_opt_set(opts, "backend", "parallel", &error_abort);
|
||||
qemu_opt_set(opts, "path", filename, &error_abort);
|
||||
return opts;
|
||||
}
|
||||
if (strstart(filename, "/dev/", NULL)) {
|
||||
qemu_opt_set(opts, "backend", "tty", &error_abort);
|
||||
qemu_opt_set(opts, "backend", "serial", &error_abort);
|
||||
qemu_opt_set(opts, "path", filename, &error_abort);
|
||||
return opts;
|
||||
}
|
||||
|
||||
103
configure
vendored
103
configure
vendored
@@ -91,7 +91,8 @@ update_cxxflags() {
|
||||
# Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those
|
||||
# options which some versions of GCC's C++ compiler complain about
|
||||
# because they only make sense for C programs.
|
||||
QEMU_CXXFLAGS=
|
||||
QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS"
|
||||
|
||||
for arg in $QEMU_CFLAGS; do
|
||||
case $arg in
|
||||
-Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\
|
||||
@@ -345,6 +346,9 @@ for opt do
|
||||
--extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
|
||||
EXTRA_CFLAGS="$optarg"
|
||||
;;
|
||||
--extra-cxxflags=*) QEMU_CXXFLAGS="$QEMU_CXXFLAGS $optarg"
|
||||
EXTRA_CXXFLAGS="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="$LDFLAGS $optarg"
|
||||
EXTRA_LDFLAGS="$optarg"
|
||||
;;
|
||||
@@ -403,7 +407,7 @@ QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
|
||||
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/include"
|
||||
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/accel/tcg -I\$(SRC_PATH)/include"
|
||||
if test "$debug_info" = "yes"; then
|
||||
CFLAGS="-g $CFLAGS"
|
||||
LDFLAGS="-g $LDFLAGS"
|
||||
@@ -788,6 +792,8 @@ for opt do
|
||||
;;
|
||||
--extra-cflags=*)
|
||||
;;
|
||||
--extra-cxxflags=*)
|
||||
;;
|
||||
--extra-ldflags=*)
|
||||
;;
|
||||
--enable-debug-info)
|
||||
@@ -1305,6 +1311,7 @@ Advanced options (experts only):
|
||||
--cxx=CXX use C++ compiler CXX [$cxx]
|
||||
--objcc=OBJCC use Objective-C compiler OBJCC [$objcc]
|
||||
--extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS
|
||||
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS
|
||||
--extra-ldflags=LDFLAGS append extra linker flags LDFLAGS
|
||||
--make=MAKE use specified make [$make]
|
||||
--install=INSTALL use specified install [$install]
|
||||
@@ -1490,37 +1497,6 @@ if test "$bogus_os" = "yes"; then
|
||||
error_exit "Unrecognized host OS $targetos"
|
||||
fi
|
||||
|
||||
# Check that the C++ compiler exists and works with the C compiler
|
||||
if has $cxx; then
|
||||
cat > $TMPC <<EOF
|
||||
int c_function(void);
|
||||
int main(void) { return c_function(); }
|
||||
EOF
|
||||
|
||||
compile_object
|
||||
|
||||
cat > $TMPCXX <<EOF
|
||||
extern "C" {
|
||||
int c_function(void);
|
||||
}
|
||||
int c_function(void) { return 42; }
|
||||
EOF
|
||||
|
||||
update_cxxflags
|
||||
|
||||
if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then
|
||||
# C++ compiler $cxx works ok with C compiler $cc
|
||||
:
|
||||
else
|
||||
echo "C++ compiler $cxx does not work with C compiler $cc"
|
||||
echo "Disabling C++ specific optional code"
|
||||
cxx=
|
||||
fi
|
||||
else
|
||||
echo "No C++ compiler available; disabling C++ specific optional code"
|
||||
cxx=
|
||||
fi
|
||||
|
||||
gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
|
||||
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
|
||||
gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
||||
@@ -2325,14 +2301,14 @@ fi
|
||||
# GTK probe
|
||||
|
||||
if test "$gtkabi" = ""; then
|
||||
# The GTK ABI was not specified explicitly, so try whether 2.0 is available.
|
||||
# Use 3.0 as a fallback if that is available.
|
||||
if $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
|
||||
gtkabi=2.0
|
||||
elif $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
|
||||
# The GTK ABI was not specified explicitly, so try whether 3.0 is available.
|
||||
# Use 2.0 as a fallback if that is available.
|
||||
if $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
|
||||
gtkabi=3.0
|
||||
else
|
||||
elif $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
|
||||
gtkabi=2.0
|
||||
else
|
||||
gtkabi=3.0
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -2355,7 +2331,7 @@ if test "$gtk" != "no"; then
|
||||
libs_softmmu="$gtk_libs $libs_softmmu"
|
||||
gtk="yes"
|
||||
elif test "$gtk" = "yes"; then
|
||||
feature_not_found "gtk" "Install gtk2 or gtk3 devel"
|
||||
feature_not_found "gtk" "Install gtk3-devel"
|
||||
else
|
||||
gtk="no"
|
||||
fi
|
||||
@@ -2622,12 +2598,12 @@ fi
|
||||
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
|
||||
|
||||
if test "$sdlabi" = ""; then
|
||||
if $pkg_config --exists "sdl"; then
|
||||
sdlabi=1.2
|
||||
elif $pkg_config --exists "sdl2"; then
|
||||
if $pkg_config --exists "sdl2"; then
|
||||
sdlabi=2.0
|
||||
else
|
||||
elif $pkg_config --exists "sdl"; then
|
||||
sdlabi=1.2
|
||||
else
|
||||
sdlabi=2.0
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -2654,7 +2630,7 @@ elif has ${sdl_config}; then
|
||||
sdlversion=$($sdlconfig --version)
|
||||
else
|
||||
if test "$sdl" = "yes" ; then
|
||||
feature_not_found "sdl" "Install SDL devel"
|
||||
feature_not_found "sdl" "Install SDL2-devel"
|
||||
fi
|
||||
sdl=no
|
||||
fi
|
||||
@@ -5064,6 +5040,38 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check that the C++ compiler exists and works with the C compiler.
|
||||
# All the QEMU_CXXFLAGS are based on QEMU_CFLAGS. Keep this at the end to don't miss any other that could be added.
|
||||
if has $cxx; then
|
||||
cat > $TMPC <<EOF
|
||||
int c_function(void);
|
||||
int main(void) { return c_function(); }
|
||||
EOF
|
||||
|
||||
compile_object
|
||||
|
||||
cat > $TMPCXX <<EOF
|
||||
extern "C" {
|
||||
int c_function(void);
|
||||
}
|
||||
int c_function(void) { return 42; }
|
||||
EOF
|
||||
|
||||
update_cxxflags
|
||||
|
||||
if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then
|
||||
# C++ compiler $cxx works ok with C compiler $cc
|
||||
:
|
||||
else
|
||||
echo "C++ compiler $cxx does not work with C compiler $cc"
|
||||
echo "Disabling C++ specific optional code"
|
||||
cxx=
|
||||
fi
|
||||
else
|
||||
echo "No C++ compiler available; disabling C++ specific optional code"
|
||||
cxx=
|
||||
fi
|
||||
|
||||
echo_version() {
|
||||
if test "$1" = "yes" ; then
|
||||
echo "($2)"
|
||||
@@ -5269,6 +5277,7 @@ if test "$mingw32" = "no" ; then
|
||||
fi
|
||||
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
|
||||
echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
|
||||
echo "extra_cxxflags=$EXTRA_CXXFLAGS" >> $config_host_mak
|
||||
echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
|
||||
echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
|
||||
echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
|
||||
@@ -5911,6 +5920,7 @@ echo "WINDRES=$windres" >> $config_host_mak
|
||||
echo "CFLAGS=$CFLAGS" >> $config_host_mak
|
||||
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
|
||||
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
||||
echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak
|
||||
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
|
||||
if test "$sparse" = "yes" ; then
|
||||
echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
|
||||
@@ -6364,7 +6374,7 @@ fi
|
||||
|
||||
# build tree in object directory in case the source is not in the current directory
|
||||
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
|
||||
DIRS="$DIRS docs fsdev"
|
||||
DIRS="$DIRS docs docs/interop fsdev"
|
||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
DIRS="$DIRS qapi-generated"
|
||||
@@ -6376,6 +6386,7 @@ FILES="$FILES pc-bios/spapr-rtas/Makefile"
|
||||
FILES="$FILES pc-bios/s390-ccw/Makefile"
|
||||
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
||||
FILES="$FILES pc-bios/qemu-icon.bmp"
|
||||
FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit
|
||||
for bios_file in \
|
||||
$source_path/pc-bios/*.bin \
|
||||
$source_path/pc-bios/*.lid \
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/vhost.h>
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
|
||||
@@ -192,11 +193,11 @@ typedef struct VuVirtq {
|
||||
} VuVirtq;
|
||||
|
||||
enum VuWatchCondtion {
|
||||
VU_WATCH_IN = 1 << 0,
|
||||
VU_WATCH_OUT = 1 << 1,
|
||||
VU_WATCH_PRI = 1 << 2,
|
||||
VU_WATCH_ERR = 1 << 3,
|
||||
VU_WATCH_HUP = 1 << 4,
|
||||
VU_WATCH_IN = POLLIN,
|
||||
VU_WATCH_OUT = POLLOUT,
|
||||
VU_WATCH_PRI = POLLPRI,
|
||||
VU_WATCH_ERR = POLLERR,
|
||||
VU_WATCH_HUP = POLLHUP,
|
||||
};
|
||||
|
||||
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);
|
||||
|
||||
1
contrib/vhost-user-scsi/Makefile.objs
Normal file
1
contrib/vhost-user-scsi/Makefile.objs
Normal file
@@ -0,0 +1 @@
|
||||
vhost-user-scsi-obj-y = vhost-user-scsi.o
|
||||
886
contrib/vhost-user-scsi/vhost-user-scsi.c
Normal file
886
contrib/vhost-user-scsi/vhost-user-scsi.c
Normal file
@@ -0,0 +1,886 @@
|
||||
/*
|
||||
* vhost-user-scsi sample application
|
||||
*
|
||||
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Felipe Franciosi <felipe@nutanix.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 only.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "contrib/libvhost-user/libvhost-user.h"
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
#include "iscsi/iscsi.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/* Small compat shim from glib 2.32 */
|
||||
#ifndef G_SOURCE_CONTINUE
|
||||
#define G_SOURCE_CONTINUE TRUE
|
||||
#endif
|
||||
#ifndef G_SOURCE_REMOVE
|
||||
#define G_SOURCE_REMOVE FALSE
|
||||
#endif
|
||||
|
||||
/* #define VUS_DEBUG 1 */
|
||||
|
||||
/** Log helpers **/
|
||||
|
||||
#define PPRE \
|
||||
struct timespec ts; \
|
||||
char timebuf[64]; \
|
||||
struct tm tm; \
|
||||
(void)clock_gettime(CLOCK_REALTIME, &ts); \
|
||||
(void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
|
||||
|
||||
#define PEXT(lvl, msg, ...) do { \
|
||||
PPRE; \
|
||||
fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n", \
|
||||
timebuf, ts.tv_nsec / 1000, \
|
||||
__FILE__, __func__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define PNOR(lvl, msg, ...) do { \
|
||||
PPRE; \
|
||||
fprintf(stderr, "%s.%06ld " lvl ": " msg "\n", \
|
||||
timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#ifdef VUS_DEBUG
|
||||
#define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
|
||||
#define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
|
||||
#define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
|
||||
#else
|
||||
#define PDBG(msg, ...) { }
|
||||
#define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
|
||||
#define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/** vhost-user-scsi specific definitions **/
|
||||
|
||||
/* Only 1 LUN and device supported today */
|
||||
#define VUS_MAX_LUNS 1
|
||||
#define VUS_MAX_DEVS 1
|
||||
|
||||
#define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
|
||||
|
||||
typedef struct iscsi_lun {
|
||||
struct iscsi_context *iscsi_ctx;
|
||||
int iscsi_lun;
|
||||
} iscsi_lun_t;
|
||||
|
||||
typedef struct vhost_scsi_dev {
|
||||
VuDev vu_dev;
|
||||
int server_sock;
|
||||
GMainLoop *loop;
|
||||
GTree *fdmap; /* fd -> gsource context id */
|
||||
iscsi_lun_t luns[VUS_MAX_LUNS];
|
||||
} vhost_scsi_dev_t;
|
||||
|
||||
static vhost_scsi_dev_t *vhost_scsi_devs[VUS_MAX_DEVS];
|
||||
|
||||
/** glib event loop integration for libvhost-user and misc callbacks **/
|
||||
|
||||
QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
|
||||
QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
|
||||
QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
|
||||
QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
|
||||
QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
|
||||
|
||||
typedef struct vus_gsrc {
|
||||
GSource parent;
|
||||
vhost_scsi_dev_t *vdev_scsi;
|
||||
GPollFD gfd;
|
||||
vu_watch_cb vu_cb;
|
||||
} vus_gsrc_t;
|
||||
|
||||
static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
return (b > a) - (b < a);
|
||||
}
|
||||
|
||||
static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
|
||||
{
|
||||
assert(timeout);
|
||||
|
||||
*timeout = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean vus_gsrc_check(GSource *src)
|
||||
{
|
||||
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
|
||||
|
||||
assert(vus_src);
|
||||
|
||||
return vus_src->gfd.revents & vus_src->gfd.events;
|
||||
}
|
||||
|
||||
static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
|
||||
{
|
||||
vhost_scsi_dev_t *vdev_scsi;
|
||||
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
|
||||
|
||||
assert(vus_src);
|
||||
assert(!(vus_src->vu_cb && cb));
|
||||
|
||||
vdev_scsi = vus_src->vdev_scsi;
|
||||
|
||||
assert(vdev_scsi);
|
||||
|
||||
if (cb) {
|
||||
return cb(data);
|
||||
}
|
||||
if (vus_src->vu_cb) {
|
||||
vus_src->vu_cb(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
|
||||
}
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static GSourceFuncs vus_gsrc_funcs = {
|
||||
vus_gsrc_prepare,
|
||||
vus_gsrc_check,
|
||||
vus_gsrc_dispatch,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int vus_gsrc_new(vhost_scsi_dev_t *vdev_scsi, int fd, GIOCondition cond,
|
||||
vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
|
||||
{
|
||||
GSource *vus_gsrc;
|
||||
vus_gsrc_t *vus_src;
|
||||
guint id;
|
||||
|
||||
assert(vdev_scsi);
|
||||
assert(fd >= 0);
|
||||
assert(vu_cb || gsrc_cb);
|
||||
assert(!(vu_cb && gsrc_cb));
|
||||
|
||||
vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
|
||||
if (!vus_gsrc) {
|
||||
PERR("Error creating GSource for new watch");
|
||||
return -1;
|
||||
}
|
||||
vus_src = (vus_gsrc_t *)vus_gsrc;
|
||||
|
||||
vus_src->vdev_scsi = vdev_scsi;
|
||||
vus_src->gfd.fd = fd;
|
||||
vus_src->gfd.events = cond;
|
||||
vus_src->vu_cb = vu_cb;
|
||||
|
||||
g_source_add_poll(vus_gsrc, &vus_src->gfd);
|
||||
g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
|
||||
id = g_source_attach(vus_gsrc, NULL);
|
||||
assert(id);
|
||||
g_source_unref(vus_gsrc);
|
||||
|
||||
g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
|
||||
(gpointer)(uintptr_t)id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* from libiscsi's scsi-lowlevel.h **
|
||||
*
|
||||
* nb. We can't directly include scsi-lowlevel.h due to a namespace conflict:
|
||||
* QEMU's scsi.h also defines "SCSI_XFER_NONE".
|
||||
*/
|
||||
|
||||
#define SCSI_CDB_MAX_SIZE 16
|
||||
|
||||
struct scsi_iovector {
|
||||
struct scsi_iovec *iov;
|
||||
int niov;
|
||||
int nalloc;
|
||||
size_t offset;
|
||||
int consumed;
|
||||
};
|
||||
|
||||
struct scsi_allocated_memory {
|
||||
struct scsi_allocated_memory *next;
|
||||
char buf[0];
|
||||
};
|
||||
|
||||
struct scsi_data {
|
||||
int size;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
enum scsi_sense_key {
|
||||
SCSI_SENSE_NO_SENSE = 0x00,
|
||||
SCSI_SENSE_RECOVERED_ERROR = 0x01,
|
||||
SCSI_SENSE_NOT_READY = 0x02,
|
||||
SCSI_SENSE_MEDIUM_ERROR = 0x03,
|
||||
SCSI_SENSE_HARDWARE_ERROR = 0x04,
|
||||
SCSI_SENSE_ILLEGAL_REQUEST = 0x05,
|
||||
SCSI_SENSE_UNIT_ATTENTION = 0x06,
|
||||
SCSI_SENSE_DATA_PROTECTION = 0x07,
|
||||
SCSI_SENSE_BLANK_CHECK = 0x08,
|
||||
SCSI_SENSE_VENDOR_SPECIFIC = 0x09,
|
||||
SCSI_SENSE_COPY_ABORTED = 0x0a,
|
||||
SCSI_SENSE_COMMAND_ABORTED = 0x0b,
|
||||
SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
|
||||
SCSI_SENSE_OVERFLOW_COMMAND = 0x0d,
|
||||
SCSI_SENSE_MISCOMPARE = 0x0e
|
||||
};
|
||||
|
||||
struct scsi_sense {
|
||||
unsigned char error_type;
|
||||
enum scsi_sense_key key;
|
||||
int ascq;
|
||||
unsigned sense_specific:1;
|
||||
unsigned ill_param_in_cdb:1;
|
||||
unsigned bit_pointer_valid:1;
|
||||
unsigned char bit_pointer;
|
||||
uint16_t field_pointer;
|
||||
};
|
||||
|
||||
enum scsi_residual {
|
||||
SCSI_RESIDUAL_NO_RESIDUAL = 0,
|
||||
SCSI_RESIDUAL_UNDERFLOW,
|
||||
SCSI_RESIDUAL_OVERFLOW
|
||||
};
|
||||
|
||||
struct scsi_task {
|
||||
int status;
|
||||
int cdb_size;
|
||||
int xfer_dir;
|
||||
int expxferlen;
|
||||
unsigned char cdb[SCSI_CDB_MAX_SIZE];
|
||||
enum scsi_residual residual_status;
|
||||
size_t residual;
|
||||
struct scsi_sense sense;
|
||||
struct scsi_data datain;
|
||||
struct scsi_allocated_memory *mem;
|
||||
void *ptr;
|
||||
|
||||
uint32_t itt;
|
||||
uint32_t cmdsn;
|
||||
uint32_t lun;
|
||||
|
||||
struct scsi_iovector iovector_in;
|
||||
struct scsi_iovector iovector_out;
|
||||
};
|
||||
|
||||
/** libiscsi integration **/
|
||||
|
||||
static int iscsi_add_lun(iscsi_lun_t *lun, char *iscsi_uri)
|
||||
{
|
||||
struct iscsi_url *iscsi_url;
|
||||
struct iscsi_context *iscsi_ctx;
|
||||
int ret = 0;
|
||||
|
||||
assert(lun);
|
||||
assert(iscsi_uri);
|
||||
|
||||
iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
|
||||
if (!iscsi_ctx) {
|
||||
PERR("Unable to create iSCSI context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
|
||||
if (!iscsi_url) {
|
||||
PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
|
||||
iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
|
||||
PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lun->iscsi_ctx = iscsi_ctx;
|
||||
lun->iscsi_lun = iscsi_url->lun;
|
||||
|
||||
PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
|
||||
|
||||
out:
|
||||
if (iscsi_url) {
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
}
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
(void)iscsi_destroy_context(iscsi_ctx);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
|
||||
int xfer_len) {
|
||||
struct scsi_task *task;
|
||||
|
||||
assert(cdb_len > 0);
|
||||
assert(cdb);
|
||||
|
||||
task = calloc(1, sizeof(struct scsi_task));
|
||||
if (!task) {
|
||||
PERR("Error allocating task: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(task->cdb, cdb, cdb_len);
|
||||
task->cdb_size = cdb_len;
|
||||
task->xfer_dir = dir;
|
||||
task->expxferlen = xfer_len;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
static int get_cdb_len(uint8_t *cdb)
|
||||
{
|
||||
assert(cdb);
|
||||
|
||||
switch (cdb[0] >> 5) {
|
||||
case 0: return 6;
|
||||
case 1: /* fall through */
|
||||
case 2: return 10;
|
||||
case 4: return 16;
|
||||
case 5: return 12;
|
||||
}
|
||||
PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int handle_cmd_sync(struct iscsi_context *ctx,
|
||||
VirtIOSCSICmdReq *req,
|
||||
struct iovec *out, unsigned int out_len,
|
||||
VirtIOSCSICmdResp *rsp,
|
||||
struct iovec *in, unsigned int in_len) {
|
||||
struct scsi_task *task;
|
||||
uint32_t dir;
|
||||
uint32_t len;
|
||||
int cdb_len;
|
||||
int i;
|
||||
|
||||
assert(ctx);
|
||||
assert(req);
|
||||
assert(rsp);
|
||||
|
||||
if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
|
||||
/* Ignore anything different than target=0, lun=0 */
|
||||
PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
|
||||
req->lun[1], req->lun[3]);
|
||||
rsp->status = SCSI_STATUS_CHECK_CONDITION;
|
||||
memset(rsp->sense, 0, sizeof(rsp->sense));
|
||||
rsp->sense_len = 18;
|
||||
rsp->sense[0] = 0x70;
|
||||
rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
|
||||
rsp->sense[7] = 10;
|
||||
rsp->sense[12] = 0x24;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cdb_len = get_cdb_len(req->cdb);
|
||||
if (cdb_len == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
if (!out_len && !in_len) {
|
||||
dir = SCSI_XFER_NONE;
|
||||
} else if (out_len) {
|
||||
dir = SCSI_XFER_TO_DEV;
|
||||
for (i = 0; i < out_len; i++) {
|
||||
len += out[i].iov_len;
|
||||
}
|
||||
} else {
|
||||
dir = SCSI_XFER_FROM_DEV;
|
||||
for (i = 0; i < in_len; i++) {
|
||||
len += in[i].iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
task = scsi_task_new(cdb_len, req->cdb, dir, len);
|
||||
if (!task) {
|
||||
PERR("Unable to create iscsi task");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dir == SCSI_XFER_TO_DEV) {
|
||||
task->iovector_out.iov = (struct scsi_iovec *)out;
|
||||
task->iovector_out.niov = out_len;
|
||||
} else if (dir == SCSI_XFER_FROM_DEV) {
|
||||
task->iovector_in.iov = (struct scsi_iovec *)in;
|
||||
task->iovector_in.niov = in_len;
|
||||
}
|
||||
|
||||
PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
|
||||
cdb_len, dir, task);
|
||||
if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
|
||||
PERR("Error serving SCSI command");
|
||||
free(task);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(rsp, 0, sizeof(*rsp));
|
||||
|
||||
rsp->status = task->status;
|
||||
rsp->resid = task->residual;
|
||||
|
||||
if (task->status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
rsp->response = VIRTIO_SCSI_S_FAILURE;
|
||||
rsp->sense_len = task->datain.size - 2;
|
||||
memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
|
||||
}
|
||||
|
||||
free(task);
|
||||
|
||||
PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
|
||||
rsp->status, rsp->resid, rsp->response, rsp->sense_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** libvhost-user callbacks **/
|
||||
|
||||
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev);
|
||||
|
||||
static void vus_panic_cb(VuDev *vu_dev, const char *buf)
|
||||
{
|
||||
vhost_scsi_dev_t *vdev_scsi;
|
||||
|
||||
assert(vu_dev);
|
||||
|
||||
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||
|
||||
if (buf) {
|
||||
PERR("vu_panic: %s", buf);
|
||||
}
|
||||
|
||||
if (vdev_scsi) {
|
||||
assert(vdev_scsi->loop);
|
||||
g_main_loop_quit(vdev_scsi->loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
|
||||
void *pvt) {
|
||||
vhost_scsi_dev_t *vdev_scsi;
|
||||
guint id;
|
||||
|
||||
assert(vu_dev);
|
||||
assert(fd >= 0);
|
||||
assert(cb);
|
||||
|
||||
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||
if (!vdev_scsi) {
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
|
||||
(gpointer)(uintptr_t)fd);
|
||||
if (id) {
|
||||
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
|
||||
assert(vus_src);
|
||||
g_source_destroy(vus_src);
|
||||
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
|
||||
}
|
||||
|
||||
if (vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt)) {
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void vus_del_watch_cb(VuDev *vu_dev, int fd)
|
||||
{
|
||||
vhost_scsi_dev_t *vdev_scsi;
|
||||
guint id;
|
||||
|
||||
assert(vu_dev);
|
||||
assert(fd >= 0);
|
||||
|
||||
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||
if (!vdev_scsi) {
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
|
||||
(gpointer)(uintptr_t)fd);
|
||||
if (id) {
|
||||
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
|
||||
assert(vus_src);
|
||||
g_source_destroy(vus_src);
|
||||
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void vus_proc_ctl(VuDev *vu_dev, int idx)
|
||||
{
|
||||
/* Control VQ not implemented */
|
||||
}
|
||||
|
||||
static void vus_proc_evt(VuDev *vu_dev, int idx)
|
||||
{
|
||||
/* Event VQ not implemented */
|
||||
}
|
||||
|
||||
static void vus_proc_req(VuDev *vu_dev, int idx)
|
||||
{
|
||||
vhost_scsi_dev_t *vdev_scsi;
|
||||
VuVirtq *vq;
|
||||
|
||||
assert(vu_dev);
|
||||
|
||||
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||
if (!vdev_scsi) {
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
|
||||
PERR("VQ Index out of range: %d", idx);
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
vq = vu_get_queue(vu_dev, idx);
|
||||
if (!vq) {
|
||||
PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
PDBG("Got kicked on vq[%d]@%p", idx, vq);
|
||||
|
||||
while (1) {
|
||||
VuVirtqElement *elem;
|
||||
VirtIOSCSICmdReq *req;
|
||||
VirtIOSCSICmdResp *rsp;
|
||||
|
||||
elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
|
||||
if (!elem) {
|
||||
PDBG("No more elements pending on vq[%d]@%p", idx, vq);
|
||||
break;
|
||||
}
|
||||
PDBG("Popped elem@%p", elem);
|
||||
|
||||
assert(!((elem->out_num > 1) && (elem->in_num > 1)));
|
||||
assert((elem->out_num > 0) && (elem->in_num > 0));
|
||||
|
||||
if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
|
||||
PERR("Invalid virtio-scsi req header");
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
break;
|
||||
}
|
||||
req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
|
||||
|
||||
if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
|
||||
PERR("Invalid virtio-scsi rsp header");
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
break;
|
||||
}
|
||||
rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
|
||||
|
||||
if (handle_cmd_sync(vdev_scsi->luns[0].iscsi_ctx,
|
||||
req, &elem->out_sg[1], elem->out_num - 1,
|
||||
rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
vu_queue_push(vu_dev, vq, elem, 0);
|
||||
vu_queue_notify(vu_dev, vq);
|
||||
|
||||
free(elem);
|
||||
}
|
||||
}
|
||||
|
||||
static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
|
||||
{
|
||||
VuVirtq *vq;
|
||||
|
||||
assert(vu_dev);
|
||||
|
||||
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
|
||||
PERR("VQ Index out of range: %d", idx);
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
vq = vu_get_queue(vu_dev, idx);
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_ctl : NULL);
|
||||
break;
|
||||
case 1:
|
||||
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_evt : NULL);
|
||||
break;
|
||||
default:
|
||||
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static const VuDevIface vus_iface = {
|
||||
.queue_set_started = vus_queue_set_started,
|
||||
};
|
||||
|
||||
static gboolean vus_vhost_cb(gpointer data)
|
||||
{
|
||||
VuDev *vu_dev = (VuDev *)data;
|
||||
|
||||
assert(vu_dev);
|
||||
|
||||
if (!vu_dispatch(vu_dev) != 0) {
|
||||
PERR("Error processing vhost message");
|
||||
vus_panic_cb(vu_dev, NULL);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
/** misc helpers **/
|
||||
|
||||
static int unix_sock_new(char *unix_fn)
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_un un;
|
||||
size_t len;
|
||||
|
||||
assert(unix_fn);
|
||||
|
||||
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock <= 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
un.sun_family = AF_UNIX;
|
||||
(void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
|
||||
len = sizeof(un.sun_family) + strlen(un.sun_path);
|
||||
|
||||
(void)unlink(unix_fn);
|
||||
if (bind(sock, (struct sockaddr *)&un, len) < 0) {
|
||||
perror("bind");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (listen(sock, 1) < 0) {
|
||||
perror("listen");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return sock;
|
||||
|
||||
fail:
|
||||
(void)close(sock);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** vhost-user-scsi **/
|
||||
|
||||
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(vu_dev);
|
||||
|
||||
for (i = 0; i < VUS_MAX_DEVS; i++) {
|
||||
if (&vhost_scsi_devs[i]->vu_dev == vu_dev) {
|
||||
return vhost_scsi_devs[i];
|
||||
}
|
||||
}
|
||||
|
||||
PERR("Unknown VuDev %p", vu_dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vdev_scsi_deinit(vhost_scsi_dev_t *vdev_scsi)
|
||||
{
|
||||
if (!vdev_scsi) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vdev_scsi->server_sock >= 0) {
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t sslen = sizeof(ss);
|
||||
|
||||
if (getsockname(vdev_scsi->server_sock, (struct sockaddr *)&ss,
|
||||
&sslen) == 0) {
|
||||
struct sockaddr_un *su = (struct sockaddr_un *)&ss;
|
||||
(void)unlink(su->sun_path);
|
||||
}
|
||||
|
||||
(void)close(vdev_scsi->server_sock);
|
||||
vdev_scsi->server_sock = -1;
|
||||
}
|
||||
|
||||
if (vdev_scsi->loop) {
|
||||
g_main_loop_unref(vdev_scsi->loop);
|
||||
vdev_scsi->loop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static vhost_scsi_dev_t *vdev_scsi_new(char *unix_fn)
|
||||
{
|
||||
vhost_scsi_dev_t *vdev_scsi = NULL;
|
||||
|
||||
assert(unix_fn);
|
||||
|
||||
vdev_scsi = calloc(1, sizeof(vhost_scsi_dev_t));
|
||||
if (!vdev_scsi) {
|
||||
PERR("calloc: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vdev_scsi->server_sock = unix_sock_new(unix_fn);
|
||||
if (vdev_scsi->server_sock < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
|
||||
if (!vdev_scsi->loop) {
|
||||
PERR("Error creating glib event loop");
|
||||
goto err;
|
||||
}
|
||||
|
||||
vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
|
||||
if (!vdev_scsi->fdmap) {
|
||||
PERR("Error creating glib tree for fdmap");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return vdev_scsi;
|
||||
|
||||
err:
|
||||
vdev_scsi_deinit(vdev_scsi);
|
||||
free(vdev_scsi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vdev_scsi_add_iscsi_lun(vhost_scsi_dev_t *vdev_scsi,
|
||||
char *iscsi_uri, uint32_t lun) {
|
||||
assert(vdev_scsi);
|
||||
assert(iscsi_uri);
|
||||
assert(lun < VUS_MAX_LUNS);
|
||||
|
||||
if (vdev_scsi->luns[lun].iscsi_ctx) {
|
||||
PERR("Lun %d already configured", lun);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi_add_lun(&vdev_scsi->luns[lun], iscsi_uri) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdev_scsi_run(vhost_scsi_dev_t *vdev_scsi)
|
||||
{
|
||||
int cli_sock;
|
||||
int ret = 0;
|
||||
|
||||
assert(vdev_scsi);
|
||||
assert(vdev_scsi->server_sock >= 0);
|
||||
assert(vdev_scsi->loop);
|
||||
|
||||
cli_sock = accept(vdev_scsi->server_sock, (void *)0, (void *)0);
|
||||
if (cli_sock < 0) {
|
||||
perror("accept");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vu_init(&vdev_scsi->vu_dev,
|
||||
cli_sock,
|
||||
vus_panic_cb,
|
||||
vus_add_watch_cb,
|
||||
vus_del_watch_cb,
|
||||
&vus_iface);
|
||||
|
||||
if (vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
|
||||
&vdev_scsi->vu_dev)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_main_loop_run(vdev_scsi->loop);
|
||||
|
||||
out:
|
||||
vu_deinit(&vdev_scsi->vu_dev);
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
vhost_scsi_dev_t *vdev_scsi = NULL;
|
||||
char *unix_fn = NULL;
|
||||
char *iscsi_uri = NULL;
|
||||
int opt, err = EXIT_SUCCESS;
|
||||
|
||||
while ((opt = getopt(argc, argv, "u:i:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
goto help;
|
||||
case 'u':
|
||||
unix_fn = strdup(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
iscsi_uri = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
goto help;
|
||||
}
|
||||
}
|
||||
if (!unix_fn || !iscsi_uri) {
|
||||
goto help;
|
||||
}
|
||||
|
||||
vdev_scsi = vdev_scsi_new(unix_fn);
|
||||
if (!vdev_scsi) {
|
||||
goto err;
|
||||
}
|
||||
vhost_scsi_devs[0] = vdev_scsi;
|
||||
|
||||
if (vdev_scsi_add_iscsi_lun(vdev_scsi, iscsi_uri, 0) != 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (vdev_scsi_run(vdev_scsi) != 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
out:
|
||||
if (vdev_scsi) {
|
||||
vdev_scsi_deinit(vdev_scsi);
|
||||
free(vdev_scsi);
|
||||
}
|
||||
if (unix_fn) {
|
||||
free(unix_fn);
|
||||
}
|
||||
if (iscsi_uri) {
|
||||
free(iscsi_uri);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
err:
|
||||
err = EXIT_FAILURE;
|
||||
goto out;
|
||||
|
||||
help:
|
||||
fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
|
||||
argv[0]);
|
||||
fprintf(stderr, " -u path to unix socket\n");
|
||||
fprintf(stderr, " -i iscsi uri for lun 0\n");
|
||||
fprintf(stderr, " -h print help and quit\n");
|
||||
|
||||
goto err;
|
||||
}
|
||||
@@ -15,6 +15,7 @@ CONFIG_TWL92230=y
|
||||
CONFIG_TSC2005=y
|
||||
CONFIG_LM832X=y
|
||||
CONFIG_TMP105=y
|
||||
CONFIG_TMP421=y
|
||||
CONFIG_STELLARIS=y
|
||||
CONFIG_STELLARIS_INPUT=y
|
||||
CONFIG_STELLARIS_ENET=y
|
||||
|
||||
@@ -43,3 +43,4 @@ CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_IVSHMEM=$(CONFIG_EVENTFD)
|
||||
CONFIG_ROCKER=y
|
||||
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
CONFIG_PCI=y
|
||||
CONFIG_VIRTIO_PCI=y
|
||||
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
|
||||
CONFIG_VIRTIO=y
|
||||
CONFIG_SCLPCONSOLE=y
|
||||
CONFIG_TERMINAL3270=y
|
||||
|
||||
@@ -6,9 +6,9 @@ libvixl_OBJS = vixl/utils.o \
|
||||
|
||||
# The -Wno-sign-compare is needed only for gcc 4.6, which complains about
|
||||
# some signed-unsigned equality comparisons which later gcc versions do not.
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) -Wno-sign-compare
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CXXFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CXXFLAGS) -Wno-sign-compare
|
||||
# Ensure that C99 macros are defined regardless of the inclusion order of
|
||||
# headers in vixl. This is required at least on NetBSD.
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CXXFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
|
||||
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS)
|
||||
|
||||
116
exec.c
116
exec.c
@@ -1482,25 +1482,17 @@ static int64_t get_file_size(int fd)
|
||||
return size;
|
||||
}
|
||||
|
||||
static void *file_ram_alloc(RAMBlock *block,
|
||||
ram_addr_t memory,
|
||||
const char *path,
|
||||
Error **errp)
|
||||
static int file_ram_open(const char *path,
|
||||
const char *region_name,
|
||||
bool *created,
|
||||
Error **errp)
|
||||
{
|
||||
bool unlink_on_error = false;
|
||||
char *filename;
|
||||
char *sanitized_name;
|
||||
char *c;
|
||||
void *area = MAP_FAILED;
|
||||
int fd = -1;
|
||||
int64_t file_size;
|
||||
|
||||
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
||||
error_setg(errp,
|
||||
"host lacks kvm mmu notifiers, -mem-path unsupported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*created = false;
|
||||
for (;;) {
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
@@ -1511,13 +1503,13 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
/* @path names a file that doesn't exist, create it */
|
||||
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
|
||||
if (fd >= 0) {
|
||||
unlink_on_error = true;
|
||||
*created = true;
|
||||
break;
|
||||
}
|
||||
} else if (errno == EISDIR) {
|
||||
/* @path names a directory, create a file there */
|
||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||
sanitized_name = g_strdup(memory_region_name(block->mr));
|
||||
sanitized_name = g_strdup(region_name);
|
||||
for (c = sanitized_name; *c != '\0'; c++) {
|
||||
if (*c == '/') {
|
||||
*c = '_';
|
||||
@@ -1540,7 +1532,7 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
error_setg_errno(errp, errno,
|
||||
"can't open backing store %s for guest RAM",
|
||||
path);
|
||||
goto error;
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Try again on EINTR and EEXIST. The latter happens when
|
||||
@@ -1548,6 +1540,17 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
*/
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void *file_ram_alloc(RAMBlock *block,
|
||||
ram_addr_t memory,
|
||||
int fd,
|
||||
bool truncate,
|
||||
Error **errp)
|
||||
{
|
||||
void *area;
|
||||
|
||||
block->page_size = qemu_fd_getpagesize(fd);
|
||||
block->mr->align = block->page_size;
|
||||
#if defined(__s390x__)
|
||||
@@ -1556,20 +1559,11 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
}
|
||||
#endif
|
||||
|
||||
file_size = get_file_size(fd);
|
||||
|
||||
if (memory < block->page_size) {
|
||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||
"or larger than page size 0x%zx",
|
||||
memory, block->page_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (file_size > 0 && file_size < memory) {
|
||||
error_setg(errp, "backing store %s size 0x%" PRIx64
|
||||
" does not match 'size' option 0x" RAM_ADDR_FMT,
|
||||
path, file_size, memory);
|
||||
goto error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memory = ROUND_UP(memory, block->page_size);
|
||||
@@ -1588,7 +1582,7 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
* those labels. Therefore, extending the non-empty backend file
|
||||
* is disabled as well.
|
||||
*/
|
||||
if (!file_size && ftruncate(fd, memory)) {
|
||||
if (truncate && ftruncate(fd, memory)) {
|
||||
perror("ftruncate");
|
||||
}
|
||||
|
||||
@@ -1597,30 +1591,19 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
if (area == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno,
|
||||
"unable to map backing store for guest RAM");
|
||||
goto error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mem_prealloc) {
|
||||
os_mem_prealloc(fd, area, memory, smp_cpus, errp);
|
||||
if (errp && *errp) {
|
||||
goto error;
|
||||
qemu_ram_munmap(area, memory);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
block->fd = fd;
|
||||
return area;
|
||||
|
||||
error:
|
||||
if (area != MAP_FAILED) {
|
||||
qemu_ram_munmap(area, memory);
|
||||
}
|
||||
if (unlink_on_error) {
|
||||
unlink(path);
|
||||
}
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1931,18 +1914,25 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
bool share, const char *mem_path,
|
||||
Error **errp)
|
||||
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
|
||||
bool share, int fd,
|
||||
Error **errp)
|
||||
{
|
||||
RAMBlock *new_block;
|
||||
Error *local_err = NULL;
|
||||
int64_t file_size;
|
||||
|
||||
if (xen_enabled()) {
|
||||
error_setg(errp, "-mem-path not supported with Xen");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
||||
error_setg(errp,
|
||||
"host lacks kvm mmu notifiers, -mem-path unsupported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (phys_mem_alloc != qemu_anon_ram_alloc) {
|
||||
/*
|
||||
* file_ram_alloc() needs to allocate just like
|
||||
@@ -1955,13 +1945,20 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
}
|
||||
|
||||
size = HOST_PAGE_ALIGN(size);
|
||||
file_size = get_file_size(fd);
|
||||
if (file_size > 0 && file_size < size) {
|
||||
error_setg(errp, "backing store %s size 0x%" PRIx64
|
||||
" does not match 'size' option 0x" RAM_ADDR_FMT,
|
||||
mem_path, file_size, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_block = g_malloc0(sizeof(*new_block));
|
||||
new_block->mr = mr;
|
||||
new_block->used_length = size;
|
||||
new_block->max_length = size;
|
||||
new_block->flags = share ? RAM_SHARED : 0;
|
||||
new_block->host = file_ram_alloc(new_block, size,
|
||||
mem_path, errp);
|
||||
new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp);
|
||||
if (!new_block->host) {
|
||||
g_free(new_block);
|
||||
return NULL;
|
||||
@@ -1974,6 +1971,33 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
return NULL;
|
||||
}
|
||||
return new_block;
|
||||
|
||||
}
|
||||
|
||||
|
||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
bool share, const char *mem_path,
|
||||
Error **errp)
|
||||
{
|
||||
int fd;
|
||||
bool created;
|
||||
RAMBlock *block;
|
||||
|
||||
fd = file_ram_open(mem_path, memory_region_name(mr), &created, errp);
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp);
|
||||
if (!block) {
|
||||
if (created) {
|
||||
unlink(mem_path);
|
||||
}
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ float16 float16_default_nan(float_status *status)
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_default_nan(float_status *status)
|
||||
{
|
||||
#if defined(TARGET_SPARC)
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_M68K)
|
||||
return const_float32(0x7FFFFFFF);
|
||||
#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
|
||||
defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE)
|
||||
@@ -136,7 +136,7 @@ float32 float32_default_nan(float_status *status)
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_default_nan(float_status *status)
|
||||
{
|
||||
#if defined(TARGET_SPARC)
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_M68K)
|
||||
return const_float64(LIT64(0x7FFFFFFFFFFFFFFF));
|
||||
#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
|
||||
defined(TARGET_S390X)
|
||||
@@ -162,7 +162,10 @@ float64 float64_default_nan(float_status *status)
|
||||
floatx80 floatx80_default_nan(float_status *status)
|
||||
{
|
||||
floatx80 r;
|
||||
|
||||
#if defined(TARGET_M68K)
|
||||
r.low = LIT64(0xFFFFFFFFFFFFFFFF);
|
||||
r.high = 0x7FFF;
|
||||
#else
|
||||
if (status->snan_bit_is_one) {
|
||||
r.low = LIT64(0xBFFFFFFFFFFFFFFF);
|
||||
r.high = 0x7FFF;
|
||||
@@ -170,6 +173,7 @@ floatx80 floatx80_default_nan(float_status *status)
|
||||
r.low = LIT64(0xC000000000000000);
|
||||
r.high = 0xFFFF;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -502,6 +506,30 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#elif defined(TARGET_M68K)
|
||||
static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
|
||||
flag aIsLargerSignificand)
|
||||
{
|
||||
/* M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL
|
||||
* 3.4 FLOATING-POINT INSTRUCTION DETAILS
|
||||
* If either operand, but not both operands, of an operation is a
|
||||
* nonsignaling NaN, then that NaN is returned as the result. If both
|
||||
* operands are nonsignaling NaNs, then the destination operand
|
||||
* nonsignaling NaN is returned as the result.
|
||||
* If either operand to an operation is a signaling NaN (SNaN), then the
|
||||
* SNaN bit is set in the FPSR EXC byte. If the SNaN exception enable bit
|
||||
* is set in the FPCR ENABLE byte, then the exception is taken and the
|
||||
* destination is not modified. If the SNaN exception enable bit is not
|
||||
* set, setting the SNaN bit in the operand to a one converts the SNaN to
|
||||
* a nonsignaling NaN. The operation then continues as described in the
|
||||
* preceding paragraph for nonsignaling NaNs.
|
||||
*/
|
||||
if (aIsQNaN || aIsSNaN) { /* a is the destination operand */
|
||||
return 0; /* return the destination operand */
|
||||
} else {
|
||||
return 1; /* return b */
|
||||
}
|
||||
}
|
||||
#else
|
||||
static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
|
||||
flag aIsLargerSignificand)
|
||||
|
||||
@@ -338,9 +338,10 @@ static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev)
|
||||
nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported
|
||||
in ACPI 6.0 is 1. */);
|
||||
nfit_dcr->serial_number = cpu_to_le32(sn);
|
||||
nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter
|
||||
2: NVDIMM Device Specific Method
|
||||
(DSM) in DSM Spec Rev1.*/);
|
||||
nfit_dcr->fic = cpu_to_le16(0x301 /* Format Interface Code:
|
||||
Byte addressable, no energy backed.
|
||||
See ACPI 6.2, sect 5.2.25.6 and
|
||||
JEDEC Annex L Release 3. */);
|
||||
}
|
||||
|
||||
static GArray *nvdimm_build_device_structure(void)
|
||||
|
||||
@@ -239,10 +239,19 @@ static void aspeed_board_init(MachineState *machine,
|
||||
static void palmetto_bmc_i2c_init(AspeedBoardState *bmc)
|
||||
{
|
||||
AspeedSoCState *soc = &bmc->soc;
|
||||
DeviceState *dev;
|
||||
|
||||
/* The palmetto platform expects a ds3231 RTC but a ds1338 is
|
||||
* enough to provide basic RTC features. Alarms will be missing */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68);
|
||||
|
||||
/* add a TMP423 temperature sensor */
|
||||
dev = i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 2),
|
||||
"tmp423", 0x4c);
|
||||
object_property_set_int(OBJECT(dev), 31000, "temperature0", &error_abort);
|
||||
object_property_set_int(OBJECT(dev), 28000, "temperature1", &error_abort);
|
||||
object_property_set_int(OBJECT(dev), 20000, "temperature2", &error_abort);
|
||||
object_property_set_int(OBJECT(dev), 110000, "temperature3", &error_abort);
|
||||
}
|
||||
|
||||
static void palmetto_bmc_init(MachineState *machine)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/log.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/cpu/a9mpcore.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
@@ -160,16 +161,14 @@ static uint64_t exynos4210_calc_affinity(int cpu)
|
||||
return mp_affinity;
|
||||
}
|
||||
|
||||
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
||||
unsigned long ram_size)
|
||||
Exynos4210State *exynos4210_init(MemoryRegion *system_mem)
|
||||
{
|
||||
int i, n;
|
||||
Exynos4210State *s = g_new(Exynos4210State, 1);
|
||||
qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS];
|
||||
unsigned long mem_size;
|
||||
DeviceState *dev;
|
||||
SysBusDevice *busdev;
|
||||
ObjectClass *cpu_oc;
|
||||
DeviceState *dev;
|
||||
int i, n;
|
||||
|
||||
cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, "cortex-a9");
|
||||
assert(cpu_oc);
|
||||
@@ -213,7 +212,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
||||
}
|
||||
|
||||
/* Private memory region and Internal GIC */
|
||||
dev = qdev_create(NULL, "a9mpcore_priv");
|
||||
dev = qdev_create(NULL, TYPE_A9MPCORE_PRIV);
|
||||
qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
|
||||
qdev_init_nofail(dev);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
@@ -299,22 +298,6 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
||||
memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
|
||||
&s->iram_mem);
|
||||
|
||||
/* DRAM */
|
||||
mem_size = ram_size;
|
||||
if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
|
||||
memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1",
|
||||
mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_fatal);
|
||||
vmstate_register_ram_global(&s->dram1_mem);
|
||||
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
|
||||
&s->dram1_mem);
|
||||
mem_size = EXYNOS4210_DRAM_MAX_SIZE;
|
||||
}
|
||||
memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size,
|
||||
&error_fatal);
|
||||
vmstate_register_ram_global(&s->dram0_mem);
|
||||
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
|
||||
&s->dram0_mem);
|
||||
|
||||
/* PMU.
|
||||
* The only reason of existence at the moment is that secondary CPU boot
|
||||
* loader uses PMU INFORM5 register as a holding pen.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
@@ -56,6 +57,12 @@ typedef enum Exynos4BoardType {
|
||||
EXYNOS4_NUM_OF_BOARDS
|
||||
} Exynos4BoardType;
|
||||
|
||||
typedef struct Exynos4BoardState {
|
||||
Exynos4210State *soc;
|
||||
MemoryRegion dram0_mem;
|
||||
MemoryRegion dram1_mem;
|
||||
} Exynos4BoardState;
|
||||
|
||||
static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = {
|
||||
[EXYNOS4_BOARD_NURI] = 0xD33,
|
||||
[EXYNOS4_BOARD_SMDKC210] = 0xB16,
|
||||
@@ -96,9 +103,34 @@ static void lan9215_init(uint32_t base, qemu_irq irq)
|
||||
}
|
||||
}
|
||||
|
||||
static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
|
||||
Exynos4BoardType board_type)
|
||||
static void exynos4_boards_init_ram(Exynos4BoardState *s,
|
||||
MemoryRegion *system_mem,
|
||||
unsigned long ram_size)
|
||||
{
|
||||
unsigned long mem_size = ram_size;
|
||||
|
||||
if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
|
||||
memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1",
|
||||
mem_size - EXYNOS4210_DRAM_MAX_SIZE,
|
||||
&error_fatal);
|
||||
vmstate_register_ram_global(&s->dram1_mem);
|
||||
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
|
||||
&s->dram1_mem);
|
||||
mem_size = EXYNOS4210_DRAM_MAX_SIZE;
|
||||
}
|
||||
|
||||
memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size,
|
||||
&error_fatal);
|
||||
vmstate_register_ram_global(&s->dram0_mem);
|
||||
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
|
||||
&s->dram0_mem);
|
||||
}
|
||||
|
||||
static Exynos4BoardState *
|
||||
exynos4_boards_init_common(MachineState *machine,
|
||||
Exynos4BoardType board_type)
|
||||
{
|
||||
Exynos4BoardState *s = g_new(Exynos4BoardState, 1);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
|
||||
if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) {
|
||||
@@ -127,8 +159,12 @@ static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
|
||||
machine->kernel_cmdline,
|
||||
machine->initrd_filename);
|
||||
|
||||
return exynos4210_init(get_system_memory(),
|
||||
exynos4_board_ram_size[board_type]);
|
||||
exynos4_boards_init_ram(s, get_system_memory(),
|
||||
exynos4_board_ram_size[board_type]);
|
||||
|
||||
s->soc = exynos4210_init(get_system_memory());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void nuri_init(MachineState *machine)
|
||||
@@ -140,11 +176,11 @@ static void nuri_init(MachineState *machine)
|
||||
|
||||
static void smdkc210_init(MachineState *machine)
|
||||
{
|
||||
Exynos4210State *s = exynos4_boards_init_common(machine,
|
||||
EXYNOS4_BOARD_SMDKC210);
|
||||
Exynos4BoardState *s = exynos4_boards_init_common(machine,
|
||||
EXYNOS4_BOARD_SMDKC210);
|
||||
|
||||
lan9215_init(SMDK_LAN9118_BASE_ADDR,
|
||||
qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)]));
|
||||
qemu_irq_invert(s->soc->irq_table[exynos4210_get_irq(37, 1)]));
|
||||
arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o
|
||||
obj-$(CONFIG_REALVIEW) += realview_mpcore.o
|
||||
obj-$(CONFIG_A9MPCORE) += a9mpcore.o
|
||||
obj-$(CONFIG_A15MPCORE) += a15mpcore.o
|
||||
obj-y += core.o
|
||||
|
||||
common-obj-y += core.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -384,6 +384,7 @@ typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo;
|
||||
/* Pagesize of VTD paging structures, including root and context tables */
|
||||
#define VTD_PAGE_SHIFT 12
|
||||
#define VTD_PAGE_SIZE (1ULL << VTD_PAGE_SHIFT)
|
||||
#define VTD_PAGE_MASK (VTD_PAGE_SIZE - 1)
|
||||
|
||||
#define VTD_PAGE_SHIFT_4K 12
|
||||
#define VTD_PAGE_MASK_4K (~((1ULL << VTD_PAGE_SHIFT_4K) - 1))
|
||||
|
||||
14
hw/i386/pc.c
14
hw/i386/pc.c
@@ -1692,6 +1692,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm);
|
||||
uint64_t align = TARGET_PAGE_SIZE;
|
||||
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
|
||||
|
||||
if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
|
||||
align = memory_region_get_alignment(mr);
|
||||
@@ -1703,17 +1704,18 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) {
|
||||
error_setg(&local_err,
|
||||
"nvdimm is not enabled: missing 'nvdimm' in '-M'");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
|
||||
if (!pcms->acpi_nvdimm_state.is_enabled) {
|
||||
error_setg(&local_err,
|
||||
"nvdimm is not enabled: missing 'nvdimm' in '-M'");
|
||||
goto out;
|
||||
}
|
||||
if (is_nvdimm) {
|
||||
nvdimm_plug(&pcms->acpi_nvdimm_state);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
#include <xen/hvm/hvm_info_table.h>
|
||||
#include "hw/xen/xen_pt.h"
|
||||
#endif
|
||||
#include "migration/migration.h"
|
||||
#include "migration/global_state.h"
|
||||
#include "migration/misc.h"
|
||||
#include "kvm_i386.h"
|
||||
#include "sysemu/numa.h"
|
||||
|
||||
|
||||
@@ -19,6 +19,13 @@ vtd_inv_desc_wait_sw(uint64_t addr, uint32_t data) "wait invalidate status write
|
||||
vtd_inv_desc_wait_irq(const char *msg) "%s"
|
||||
vtd_inv_desc_wait_invalid(uint64_t hi, uint64_t lo) "invalid wait desc hi 0x%"PRIx64" lo 0x%"PRIx64
|
||||
vtd_inv_desc_wait_write_fail(uint64_t hi, uint64_t lo) "write fail for wait desc hi 0x%"PRIx64" lo 0x%"PRIx64
|
||||
vtd_inv_desc_iec(uint32_t granularity, uint32_t index, uint32_t mask) "granularity 0x%"PRIx32" index 0x%"PRIx32" mask 0x%"PRIx32
|
||||
vtd_inv_qi_enable(bool enable) "enabled %d"
|
||||
vtd_inv_qi_setup(uint64_t addr, int size) "addr 0x%"PRIx64" size %d"
|
||||
vtd_inv_qi_head(uint16_t head) "read head %d"
|
||||
vtd_inv_qi_tail(uint16_t head) "write tail %d"
|
||||
vtd_inv_qi_fetch(void) ""
|
||||
vtd_context_cache_reset(void) ""
|
||||
vtd_re_not_present(uint8_t bus) "Root entry bus %"PRIu8" not present"
|
||||
vtd_re_invalid(uint64_t hi, uint64_t lo) "invalid root entry hi 0x%"PRIx64" lo 0x%"PRIx64
|
||||
vtd_ce_not_present(uint8_t bus, uint8_t devfn) "Context entry bus %"PRIu8" devfn %"PRIu8" not present"
|
||||
@@ -40,6 +47,43 @@ vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device
|
||||
vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64
|
||||
vtd_translate_pt(uint16_t sid, uint64_t addr) "source id 0x%"PRIu16", iova 0x%"PRIx64
|
||||
vtd_pt_enable_fast_path(uint16_t sid, bool success) "sid 0x%"PRIu16" %d"
|
||||
vtd_irq_generate(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PRIx64
|
||||
vtd_reg_read(uint64_t addr, uint64_t size) "addr 0x%"PRIx64" size 0x%"PRIx64
|
||||
vtd_reg_write(uint64_t addr, uint64_t size, uint64_t val) "addr 0x%"PRIx64" size 0x%"PRIx64" value 0x%"PRIx64
|
||||
vtd_reg_dmar_root(uint64_t addr, bool extended) "addr 0x%"PRIx64" extended %d"
|
||||
vtd_reg_ir_root(uint64_t addr, uint32_t size) "addr 0x%"PRIx64" size 0x%"PRIx32
|
||||
vtd_reg_write_gcmd(uint32_t status, uint32_t val) "status 0x%"PRIx32" value 0x%"PRIx32
|
||||
vtd_reg_write_fectl(uint32_t value) "value 0x%"PRIx32
|
||||
vtd_reg_write_iectl(uint32_t value) "value 0x%"PRIx32
|
||||
vtd_reg_ics_clear_ip(void) ""
|
||||
vtd_dmar_translate(uint8_t bus, uint8_t slot, uint8_t func, uint64_t iova, uint64_t gpa, uint64_t mask) "dev %02x:%02x.%02x iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64
|
||||
vtd_dmar_enable(bool en) "enable %d"
|
||||
vtd_dmar_fault(uint16_t sid, int fault, uint64_t addr, bool is_write) "sid 0x%"PRIx16" fault %d addr 0x%"PRIx64" write %d"
|
||||
vtd_ir_enable(bool en) "enable %d"
|
||||
vtd_ir_irte_get(int index, uint64_t lo, uint64_t hi) "index %d low 0x%"PRIx64" high 0x%"PRIx64
|
||||
vtd_ir_remap(int index, int tri, int vec, int deliver, uint32_t dest, int dest_mode) "index %d trigger %d vector %d deliver %d dest 0x%"PRIx32" mode %d"
|
||||
vtd_ir_remap_type(const char *type) "%s"
|
||||
vtd_ir_remap_msi(uint64_t addr, uint64_t data, uint64_t addr2, uint64_t data2) "(addr 0x%"PRIx64", data 0x%"PRIx64") -> (addr 0x%"PRIx64", data 0x%"PRIx64")"
|
||||
vtd_ir_remap_msi_req(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PRIx64
|
||||
vtd_fsts_ppf(bool set) "FSTS PPF bit set to %d"
|
||||
vtd_fsts_clear_ip(void) ""
|
||||
vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low 0x%"PRIx64
|
||||
vtd_err(const char *str) "%s"
|
||||
vtd_err_dmar_iova_overflow(uint64_t iova) "iova 0x%"PRIx64
|
||||
vtd_err_dmar_slpte_read_error(uint64_t iova, int level) "iova 0x%"PRIx64" level %d"
|
||||
vtd_err_dmar_slpte_perm_error(uint64_t iova, int level, uint64_t slpte, bool is_write) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64" write %d"
|
||||
vtd_err_dmar_slpte_resv_error(uint64_t iova, int level, uint64_t slpte) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64
|
||||
vtd_err_dmar_translate(uint8_t bus, uint8_t slot, uint8_t func, uint64_t iova) "dev %02x:%02x.%02x iova 0x%"PRIx64
|
||||
vtd_err_qi_enable(uint16_t tail) "tail 0x%"PRIx16
|
||||
vtd_err_qi_disable(uint16_t head, uint16_t tail, int type) "head 0x%"PRIx16" tail 0x%"PRIx16" last_desc_type %d"
|
||||
vtd_err_qi_tail(uint16_t tail, uint16_t size) "tail 0x%"PRIx16" size 0x%"PRIx16
|
||||
vtd_err_irte(int index, uint64_t lo, uint64_t hi) "index %d low 0x%"PRIx64" high 0x%"PRIx64
|
||||
vtd_err_irte_sid(int index, uint16_t req, uint16_t target) "index %d SVT_ALL sid 0x%"PRIx16" (should be: 0x%"PRIx16")"
|
||||
vtd_err_irte_sid_bus(int index, uint8_t bus, uint8_t min, uint8_t max) "index %d SVT_BUS bus 0x%"PRIx8" (should be: 0x%"PRIx8"-0x%"PRIx8")"
|
||||
vtd_err_irte_svt(int index, int type) "index %d SVT type %d"
|
||||
vtd_err_ir_msi_invalid(uint16_t sid, uint64_t addr, uint64_t data) "sid 0x%"PRIx16" addr 0x%"PRIx64" data 0x%"PRIx64
|
||||
vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)"
|
||||
vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)"
|
||||
|
||||
# hw/i386/amd_iommu.c
|
||||
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
|
||||
|
||||
@@ -100,14 +100,14 @@ static void kvm_gicd_access(GICState *s, int offset, int cpu,
|
||||
uint32_t *val, bool write)
|
||||
{
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
KVM_VGIC_ATTR(offset, cpu), val, write);
|
||||
KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort);
|
||||
}
|
||||
|
||||
static void kvm_gicc_access(GICState *s, int offset, int cpu,
|
||||
uint32_t *val, bool write)
|
||||
{
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
|
||||
KVM_VGIC_ATTR(offset, cpu), val, write);
|
||||
KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort);
|
||||
}
|
||||
|
||||
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
|
||||
@@ -538,13 +538,14 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
|
||||
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
|
||||
uint32_t numirqs = s->num_irq;
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
|
||||
&numirqs, true);
|
||||
&numirqs, true, &error_abort);
|
||||
}
|
||||
/* Tell the kernel to complete VGIC initialization now */
|
||||
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT)) {
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true,
|
||||
&error_abort);
|
||||
}
|
||||
} else if (ret != -ENODEV && ret != -ENOTSUP) {
|
||||
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
|
||||
|
||||
@@ -145,6 +145,7 @@ static const VMStateDescription vmstate_gicv3 = {
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = gicv3_pre_save,
|
||||
.post_load = gicv3_post_load,
|
||||
.priority = MIG_PRI_GICV3,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(gicd_ctlr, GICv3State),
|
||||
VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2),
|
||||
|
||||
@@ -48,7 +48,16 @@ static const VMStateDescription vmstate_its = {
|
||||
.name = "arm_gicv3_its",
|
||||
.pre_save = gicv3_its_pre_save,
|
||||
.post_load = gicv3_its_post_load,
|
||||
.unmigratable = true,
|
||||
.priority = MIG_PRI_GICV3_ITS,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(ctlr, GICv3ITSState),
|
||||
VMSTATE_UINT32(iidr, GICv3ITSState),
|
||||
VMSTATE_UINT64(cbaser, GICv3ITSState),
|
||||
VMSTATE_UINT64(cwriter, GICv3ITSState),
|
||||
VMSTATE_UINT64(creadr, GICv3ITSState),
|
||||
VMSTATE_UINT64_ARRAY(baser, GICv3ITSState, 8),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static MemTxResult gicv3_its_trans_read(void *opaque, hwaddr offset,
|
||||
@@ -118,6 +127,7 @@ static void gicv3_its_common_reset(DeviceState *dev)
|
||||
s->cbaser = 0;
|
||||
s->cwriter = 0;
|
||||
s->creadr = 0;
|
||||
s->iidr = 0;
|
||||
memset(&s->baser, 0, sizeof(s->baser));
|
||||
|
||||
gicv3_its_post_load(s, 0);
|
||||
|
||||
@@ -53,23 +53,38 @@ static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid)
|
||||
return kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi);
|
||||
}
|
||||
|
||||
/**
|
||||
* vm_change_state_handler - VM change state callback aiming at flushing
|
||||
* ITS tables into guest RAM
|
||||
*
|
||||
* The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
*/
|
||||
static void vm_change_state_handler(void *opaque, int running,
|
||||
RunState state)
|
||||
{
|
||||
GICv3ITSState *s = (GICv3ITSState *)opaque;
|
||||
Error *err = NULL;
|
||||
int ret;
|
||||
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_ITS_SAVE_TABLES, NULL, true, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
if (ret < 0 && ret != -EFAULT) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
/*
|
||||
* Block migration of a KVM GICv3 ITS device: the API for saving and
|
||||
* restoring the state in the kernel is not yet available
|
||||
*/
|
||||
error_setg(&s->migration_blocker, "vITS migration is not implemented");
|
||||
migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
return;
|
||||
}
|
||||
|
||||
s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_ITS, false);
|
||||
if (s->dev_fd < 0) {
|
||||
error_setg_errno(errp, -s->dev_fd, "error creating in-kernel ITS");
|
||||
@@ -78,7 +93,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
/* explicit init of the ITS */
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort);
|
||||
|
||||
/* register the base address */
|
||||
kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
@@ -86,9 +101,23 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
gicv3_its_init_mmio(s, NULL);
|
||||
|
||||
if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR)) {
|
||||
error_setg(&s->migration_blocker, "This operating system kernel "
|
||||
"does not support vITS migration");
|
||||
migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
kvm_msi_use_devid = true;
|
||||
kvm_gsi_direct_mapping = false;
|
||||
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
|
||||
|
||||
qemu_add_vm_change_state_handler(vm_change_state_handler, s);
|
||||
}
|
||||
|
||||
static void kvm_arm_its_init(Object *obj)
|
||||
@@ -102,6 +131,80 @@ static void kvm_arm_its_init(Object *obj)
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arm_its_pre_save - handles the saving of ITS registers.
|
||||
* ITS tables are flushed into guest RAM separately and earlier,
|
||||
* through the VM change state handler, since at the moment pre_save()
|
||||
* is called, the guest RAM has already been saved.
|
||||
*/
|
||||
static void kvm_arm_its_pre_save(GICv3ITSState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8, &s->baser[i], false,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR, &s->ctlr, false, &error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER, &s->cbaser, false, &error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR, &s->creadr, false, &error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER, &s->cwriter, false, &error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR, &s->iidr, false, &error_abort);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arm_its_post_load - Restore both the ITS registers and tables
|
||||
*/
|
||||
static void kvm_arm_its_post_load(GICv3ITSState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!s->iidr) {
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR, &s->iidr, true, &error_abort);
|
||||
|
||||
/*
|
||||
* must be written before GITS_CREADR since GITS_CBASER write
|
||||
* access resets GITS_CREADR.
|
||||
*/
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER, &s->cbaser, true, &error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR, &s->creadr, true, &error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER, &s->cwriter, true, &error_abort);
|
||||
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8, &s->baser[i], true,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_ITS_RESTORE_TABLES, NULL, true,
|
||||
&error_abort);
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR, &s->ctlr, true, &error_abort);
|
||||
}
|
||||
|
||||
static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@@ -109,6 +212,8 @@ static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
dc->realize = kvm_arm_its_realize;
|
||||
icc->send_msi = kvm_its_send_msi;
|
||||
icc->pre_save = kvm_arm_its_pre_save;
|
||||
icc->post_load = kvm_arm_its_post_load;
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_arm_its_info = {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "kvm_arm.h"
|
||||
#include "gicv3_internal.h"
|
||||
#include "vgic_common.h"
|
||||
@@ -93,7 +94,7 @@ static inline void kvm_gicd_access(GICv3State *s, int offset,
|
||||
{
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
KVM_VGIC_ATTR(offset, 0),
|
||||
val, write);
|
||||
val, write, &error_abort);
|
||||
}
|
||||
|
||||
static inline void kvm_gicr_access(GICv3State *s, int offset, int cpu,
|
||||
@@ -101,7 +102,7 @@ static inline void kvm_gicr_access(GICv3State *s, int offset, int cpu,
|
||||
{
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
||||
KVM_VGIC_ATTR(offset, s->cpu[cpu].gicr_typer),
|
||||
val, write);
|
||||
val, write, &error_abort);
|
||||
}
|
||||
|
||||
static inline void kvm_gicc_access(GICv3State *s, uint64_t reg, int cpu,
|
||||
@@ -109,7 +110,7 @@ static inline void kvm_gicc_access(GICv3State *s, uint64_t reg, int cpu,
|
||||
{
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
KVM_VGIC_ATTR(reg, s->cpu[cpu].gicr_typer),
|
||||
val, write);
|
||||
val, write, &error_abort);
|
||||
}
|
||||
|
||||
static inline void kvm_gic_line_level_access(GICv3State *s, int irq, int cpu,
|
||||
@@ -119,7 +120,7 @@ static inline void kvm_gic_line_level_access(GICv3State *s, int irq, int cpu,
|
||||
KVM_VGIC_ATTR(irq, s->cpu[cpu].gicr_typer) |
|
||||
(VGIC_LEVEL_INFO_LINE_LEVEL <<
|
||||
KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT),
|
||||
val, write);
|
||||
val, write, &error_abort);
|
||||
}
|
||||
|
||||
/* Loop through each distributor IRQ related register; since bits
|
||||
@@ -630,7 +631,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
/* Initialize to actual HW supported configuration */
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity),
|
||||
&c->icc_ctlr_el1[GICV3_NS], false);
|
||||
&c->icc_ctlr_el1[GICV3_NS], false, &error_abort);
|
||||
|
||||
c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS];
|
||||
}
|
||||
@@ -680,6 +681,35 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = {
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
/**
|
||||
* vm_change_state_handler - VM change state callback aiming at flushing
|
||||
* RDIST pending tables into guest RAM
|
||||
*
|
||||
* The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
*/
|
||||
static void vm_change_state_handler(void *opaque, int running,
|
||||
RunState state)
|
||||
{
|
||||
GICv3State *s = (GICv3State *)opaque;
|
||||
Error *err = NULL;
|
||||
int ret;
|
||||
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES,
|
||||
NULL, true, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
if (ret < 0 && ret != -EFAULT) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
GICv3State *s = KVM_ARM_GICV3(dev);
|
||||
@@ -717,11 +747,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
0, &s->num_irq, true);
|
||||
0, &s->num_irq, true, &error_abort);
|
||||
|
||||
/* Tell the kernel to complete VGIC initialization now */
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort);
|
||||
|
||||
kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
|
||||
@@ -751,6 +781,10 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) {
|
||||
qemu_add_vm_change_state_handler(vm_change_state_handler, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
@@ -116,7 +116,7 @@ enum ExtInt {
|
||||
* which is INTG16 in Internal Interrupt Combiner.
|
||||
*/
|
||||
|
||||
static uint32_t
|
||||
static const uint32_t
|
||||
combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
|
||||
/* int combiner groups 16-19 */
|
||||
{ }, { }, { }, { },
|
||||
@@ -286,21 +286,21 @@ static void exynos4210_gic_init(Object *obj)
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
Exynos4210GicState *s = EXYNOS4210_GIC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
uint32_t i;
|
||||
const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
|
||||
const char dist_prefix[] = "exynos4210-gic-alias_dist";
|
||||
char cpu_alias_name[sizeof(cpu_prefix) + 3];
|
||||
char dist_alias_name[sizeof(cpu_prefix) + 3];
|
||||
SysBusDevice *busdev;
|
||||
SysBusDevice *gicbusdev;
|
||||
uint32_t i;
|
||||
|
||||
s->gic = qdev_create(NULL, "arm_gic");
|
||||
qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
|
||||
qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ);
|
||||
qdev_init_nofail(s->gic);
|
||||
busdev = SYS_BUS_DEVICE(s->gic);
|
||||
gicbusdev = SYS_BUS_DEVICE(s->gic);
|
||||
|
||||
/* Pass through outbound IRQ lines from the GIC */
|
||||
sysbus_pass_irq(sbd, busdev);
|
||||
sysbus_pass_irq(sbd, gicbusdev);
|
||||
|
||||
/* Pass through inbound GPIO lines to the GIC */
|
||||
qdev_init_gpio_in(dev, exynos4210_gic_set_irq,
|
||||
@@ -316,7 +316,7 @@ static void exynos4210_gic_init(Object *obj)
|
||||
sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
|
||||
memory_region_init_alias(&s->cpu_alias[i], obj,
|
||||
cpu_alias_name,
|
||||
sysbus_mmio_get_region(busdev, 1),
|
||||
sysbus_mmio_get_region(gicbusdev, 1),
|
||||
0,
|
||||
EXYNOS4210_GIC_CPU_REGION_SIZE);
|
||||
memory_region_add_subregion(&s->cpu_container,
|
||||
@@ -326,7 +326,7 @@ static void exynos4210_gic_init(Object *obj)
|
||||
sprintf(dist_alias_name, "%s%x", dist_prefix, i);
|
||||
memory_region_init_alias(&s->dist_alias[i], obj,
|
||||
dist_alias_name,
|
||||
sysbus_mmio_get_region(busdev, 0),
|
||||
sysbus_mmio_get_region(gicbusdev, 0),
|
||||
0,
|
||||
EXYNOS4210_GIC_DIST_REGION_SIZE);
|
||||
memory_region_add_subregion(&s->dist_container,
|
||||
|
||||
@@ -38,50 +38,6 @@
|
||||
#include "monitor/monitor.h"
|
||||
#include "hw/intc/intc.h"
|
||||
|
||||
void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
ICPState *icp = ICP(cpu->intc);
|
||||
|
||||
assert(icp);
|
||||
assert(cs == icp->cs);
|
||||
|
||||
icp->output = NULL;
|
||||
icp->cs = NULL;
|
||||
}
|
||||
|
||||
void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ICPStateClass *icpc;
|
||||
|
||||
assert(icp);
|
||||
|
||||
cpu->intc = OBJECT(icp);
|
||||
icp->cs = cs;
|
||||
|
||||
icpc = ICP_GET_CLASS(icp);
|
||||
if (icpc->cpu_setup) {
|
||||
icpc->cpu_setup(icp, cpu);
|
||||
}
|
||||
|
||||
switch (PPC_INPUT(env)) {
|
||||
case PPC_FLAGS_INPUT_POWER7:
|
||||
icp->output = env->irq_inputs[POWER7_INPUT_INT];
|
||||
break;
|
||||
|
||||
case PPC_FLAGS_INPUT_970:
|
||||
icp->output = env->irq_inputs[PPC970_INPUT_INT];
|
||||
break;
|
||||
|
||||
default:
|
||||
error_report("XICS interrupt controller does not support this CPU "
|
||||
"bus model");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void icp_pic_print_info(ICPState *icp, Monitor *mon)
|
||||
{
|
||||
int cpu_index = icp->cs ? icp->cs->cpu_index : -1;
|
||||
@@ -325,6 +281,7 @@ static const VMStateDescription vmstate_icp_server = {
|
||||
static void icp_reset(void *dev)
|
||||
{
|
||||
ICPState *icp = ICP(dev);
|
||||
ICPStateClass *icpc = ICP_GET_CLASS(icp);
|
||||
|
||||
icp->xirr = 0;
|
||||
icp->pending_priority = 0xff;
|
||||
@@ -332,26 +289,58 @@ static void icp_reset(void *dev)
|
||||
|
||||
/* Make all outputs are deasserted */
|
||||
qemu_set_irq(icp->output, 0);
|
||||
|
||||
if (icpc->reset) {
|
||||
icpc->reset(icp);
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ICPState *icp = ICP(dev);
|
||||
ICPStateClass *icpc = ICP_GET_CLASS(dev);
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
Object *obj;
|
||||
Error *err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "xics", &err);
|
||||
obj = object_property_get_link(OBJECT(dev), ICP_PROP_XICS, &err);
|
||||
if (!obj) {
|
||||
error_setg(errp, "%s: required link 'xics' not found: %s",
|
||||
error_setg(errp, "%s: required link '" ICP_PROP_XICS "' not found: %s",
|
||||
__func__, error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
|
||||
icp->xics = XICS_FABRIC(obj);
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err);
|
||||
if (!obj) {
|
||||
error_setg(errp, "%s: required link '" ICP_PROP_CPU "' not found: %s",
|
||||
__func__, error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
|
||||
cpu = POWERPC_CPU(obj);
|
||||
cpu->intc = OBJECT(icp);
|
||||
icp->cs = CPU(obj);
|
||||
|
||||
env = &cpu->env;
|
||||
switch (PPC_INPUT(env)) {
|
||||
case PPC_FLAGS_INPUT_POWER7:
|
||||
icp->output = env->irq_inputs[POWER7_INPUT_INT];
|
||||
break;
|
||||
|
||||
case PPC_FLAGS_INPUT_970:
|
||||
icp->output = env->irq_inputs[PPC970_INPUT_INT];
|
||||
break;
|
||||
|
||||
default:
|
||||
error_setg(errp, "XICS interrupt controller does not support this CPU bus model");
|
||||
return;
|
||||
}
|
||||
|
||||
if (icpc->realize) {
|
||||
icpc->realize(dev, errp);
|
||||
icpc->realize(icp, errp);
|
||||
}
|
||||
|
||||
qemu_register_reset(icp_reset, dev);
|
||||
@@ -601,10 +590,8 @@ static void ics_simple_initfn(Object *obj)
|
||||
ics->offset = XICS_IRQ_BASE;
|
||||
}
|
||||
|
||||
static void ics_simple_realize(DeviceState *dev, Error **errp)
|
||||
static void ics_simple_realize(ICSState *ics, Error **errp)
|
||||
{
|
||||
ICSState *ics = ICS_SIMPLE(dev);
|
||||
|
||||
if (!ics->nr_irqs) {
|
||||
error_setg(errp, "Number of interrupts needs to be greater 0");
|
||||
return;
|
||||
@@ -612,7 +599,7 @@ static void ics_simple_realize(DeviceState *dev, Error **errp)
|
||||
ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
|
||||
ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs);
|
||||
|
||||
qemu_register_reset(ics_simple_reset, dev);
|
||||
qemu_register_reset(ics_simple_reset, ics);
|
||||
}
|
||||
|
||||
static Property ics_simple_properties[] = {
|
||||
@@ -649,9 +636,9 @@ static void ics_base_realize(DeviceState *dev, Error **errp)
|
||||
Object *obj;
|
||||
Error *err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "xics", &err);
|
||||
obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err);
|
||||
if (!obj) {
|
||||
error_setg(errp, "%s: required link 'xics' not found: %s",
|
||||
error_setg(errp, "%s: required link '" ICS_PROP_XICS "' not found: %s",
|
||||
__func__, error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
@@ -659,7 +646,7 @@ static void ics_base_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
|
||||
if (icsc->realize) {
|
||||
icsc->realize(dev, errp);
|
||||
icsc->realize(ics, errp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,25 +110,14 @@ static int icp_set_kvm_state(ICPState *icp, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void icp_kvm_reset(void *dev)
|
||||
static void icp_kvm_reset(ICPState *icp)
|
||||
{
|
||||
ICPState *icp = ICP(dev);
|
||||
|
||||
icp->xirr = 0;
|
||||
icp->pending_priority = 0xff;
|
||||
icp->mfrr = 0xff;
|
||||
|
||||
/* Make all outputs as deasserted only if the CPU thread is in use */
|
||||
if (icp->output) {
|
||||
qemu_set_irq(icp->output, 0);
|
||||
}
|
||||
|
||||
icp_set_kvm_state(icp, 1);
|
||||
}
|
||||
|
||||
static void icp_kvm_cpu_setup(ICPState *icp, PowerPCCPU *cpu)
|
||||
static void icp_kvm_realize(ICPState *icp, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUState *cs = icp->cs;
|
||||
KVMEnabledICP *enabled_icp;
|
||||
unsigned long vcpu_id = kvm_arch_vcpu_id(cs);
|
||||
int ret;
|
||||
@@ -150,35 +139,23 @@ static void icp_kvm_cpu_setup(ICPState *icp, PowerPCCPU *cpu)
|
||||
|
||||
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, kernel_xics_fd, vcpu_id);
|
||||
if (ret < 0) {
|
||||
error_report("Unable to connect CPU%ld to kernel XICS: %s", vcpu_id,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
error_setg(errp, "Unable to connect CPU%ld to kernel XICS: %s", vcpu_id,
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
enabled_icp = g_malloc(sizeof(*enabled_icp));
|
||||
enabled_icp->vcpu_id = vcpu_id;
|
||||
QLIST_INSERT_HEAD(&kvm_enabled_icps, enabled_icp, node);
|
||||
}
|
||||
|
||||
static void icp_kvm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
qemu_register_reset(icp_kvm_reset, dev);
|
||||
}
|
||||
|
||||
static void icp_kvm_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
qemu_unregister_reset(icp_kvm_reset, dev);
|
||||
}
|
||||
|
||||
static void icp_kvm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ICPStateClass *icpc = ICP_CLASS(klass);
|
||||
|
||||
dc->realize = icp_kvm_realize;
|
||||
dc->unrealize = icp_kvm_unrealize;
|
||||
icpc->pre_save = icp_get_kvm_state;
|
||||
icpc->post_load = icp_set_kvm_state;
|
||||
icpc->cpu_setup = icp_kvm_cpu_setup;
|
||||
icpc->realize = icp_kvm_realize;
|
||||
icpc->reset = icp_kvm_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo icp_kvm_info = {
|
||||
@@ -351,10 +328,8 @@ static void ics_kvm_reset(void *dev)
|
||||
ics_set_kvm_state(ics, 1);
|
||||
}
|
||||
|
||||
static void ics_kvm_realize(DeviceState *dev, Error **errp)
|
||||
static void ics_kvm_realize(ICSState *ics, Error **errp)
|
||||
{
|
||||
ICSState *ics = ICS_SIMPLE(dev);
|
||||
|
||||
if (!ics->nr_irqs) {
|
||||
error_setg(errp, "Number of interrupts needs to be greater 0");
|
||||
return;
|
||||
@@ -362,7 +337,7 @@ static void ics_kvm_realize(DeviceState *dev, Error **errp)
|
||||
ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
|
||||
ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs);
|
||||
|
||||
qemu_register_reset(ics_kvm_reset, dev);
|
||||
qemu_register_reset(ics_kvm_reset, ics);
|
||||
}
|
||||
|
||||
static void ics_kvm_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
@@ -159,11 +159,11 @@ static const MemoryRegionOps pnv_icp_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static void pnv_icp_realize(DeviceState *dev, Error **errp)
|
||||
static void pnv_icp_realize(ICPState *icp, Error **errp)
|
||||
{
|
||||
PnvICPState *icp = PNV_ICP(dev);
|
||||
PnvICPState *pnv_icp = PNV_ICP(icp);
|
||||
|
||||
memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops,
|
||||
memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops,
|
||||
icp, "icp-thread", 0x1000);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
common-obj-$(CONFIG_APPLESMC) += applesmc.o
|
||||
common-obj-$(CONFIG_MAX111X) += max111x.o
|
||||
common-obj-$(CONFIG_TMP105) += tmp105.o
|
||||
common-obj-$(CONFIG_TMP421) += tmp421.o
|
||||
common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o
|
||||
common-obj-$(CONFIG_SGA) += sga.o
|
||||
common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#ifndef DEBUG_PMU
|
||||
#define DEBUG_PMU 0
|
||||
@@ -350,7 +351,11 @@ static const Exynos4210PmuReg exynos4210_pmu_regs[] = {
|
||||
{"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000},
|
||||
{"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000},
|
||||
{"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000},
|
||||
{"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200},
|
||||
/*
|
||||
* PS_HOLD_CONTROL: reset value and manually toggle high the DATA bit.
|
||||
* DATA bit high, set usually by bootloader, keeps system on.
|
||||
*/
|
||||
{"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200 | BIT(8)},
|
||||
{"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001},
|
||||
{"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001},
|
||||
{"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000},
|
||||
@@ -397,6 +402,12 @@ typedef struct Exynos4210PmuState {
|
||||
uint32_t reg[PMU_NUM_OF_REGISTERS];
|
||||
} Exynos4210PmuState;
|
||||
|
||||
static void exynos4210_pmu_poweroff(void)
|
||||
{
|
||||
PRINT_DEBUG("QEMU PMU: PS_HOLD bit down, powering off\n");
|
||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||
}
|
||||
|
||||
static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
@@ -428,6 +439,13 @@ static void exynos4210_pmu_write(void *opaque, hwaddr offset,
|
||||
PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name,
|
||||
(uint32_t)offset, (uint32_t)val);
|
||||
s->reg[i] = val;
|
||||
if ((offset == PS_HOLD_CONTROL) && ((val & BIT(8)) == 0)) {
|
||||
/*
|
||||
* We are interested only in setting data bit
|
||||
* of PS_HOLD_CONTROL register to indicate power off request.
|
||||
*/
|
||||
exynos4210_pmu_poweroff();
|
||||
}
|
||||
return;
|
||||
}
|
||||
reg_p++;
|
||||
|
||||
@@ -491,9 +491,9 @@ static void setup_interrupt(IVShmemState *s, int vector, Error **errp)
|
||||
|
||||
static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
struct stat buf;
|
||||
size_t size;
|
||||
void *ptr;
|
||||
|
||||
if (s->ivshmem_bar2) {
|
||||
error_setg(errp, "server sent unexpected shared memory message");
|
||||
@@ -522,15 +522,13 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
|
||||
}
|
||||
|
||||
/* mmap the region and map into the BAR2 */
|
||||
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno, "Failed to mmap shared memory");
|
||||
close(fd);
|
||||
memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s),
|
||||
"ivshmem.bar2", size, true, fd, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
|
||||
"ivshmem.bar2", size, ptr);
|
||||
memory_region_set_fd(&s->server_bar2, fd);
|
||||
|
||||
s->ivshmem_bar2 = &s->server_bar2;
|
||||
}
|
||||
|
||||
|
||||
402
hw/misc/tmp421.c
Normal file
402
hw/misc/tmp421.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Texas Instruments TMP421 temperature sensor.
|
||||
*
|
||||
* Copyright (c) 2016 IBM Corporation.
|
||||
*
|
||||
* Largely inspired by :
|
||||
*
|
||||
* Texas Instruments TMP105 temperature sensor.
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Written by Andrzej Zaborowski <andrew@openedhand.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
|
||||
/* Manufacturer / Device ID's */
|
||||
#define TMP421_MANUFACTURER_ID 0x55
|
||||
#define TMP421_DEVICE_ID 0x21
|
||||
#define TMP422_DEVICE_ID 0x22
|
||||
#define TMP423_DEVICE_ID 0x23
|
||||
|
||||
typedef struct DeviceInfo {
|
||||
int model;
|
||||
const char *name;
|
||||
} DeviceInfo;
|
||||
|
||||
static const DeviceInfo devices[] = {
|
||||
{ TMP421_DEVICE_ID, "tmp421" },
|
||||
{ TMP422_DEVICE_ID, "tmp422" },
|
||||
{ TMP423_DEVICE_ID, "tmp423" },
|
||||
};
|
||||
|
||||
typedef struct TMP421State {
|
||||
/*< private >*/
|
||||
I2CSlave i2c;
|
||||
/*< public >*/
|
||||
|
||||
int16_t temperature[4];
|
||||
|
||||
uint8_t status;
|
||||
uint8_t config[2];
|
||||
uint8_t rate;
|
||||
|
||||
uint8_t len;
|
||||
uint8_t buf[2];
|
||||
uint8_t pointer;
|
||||
|
||||
} TMP421State;
|
||||
|
||||
typedef struct TMP421Class {
|
||||
I2CSlaveClass parent_class;
|
||||
DeviceInfo *dev;
|
||||
} TMP421Class;
|
||||
|
||||
#define TYPE_TMP421 "tmp421-generic"
|
||||
#define TMP421(obj) OBJECT_CHECK(TMP421State, (obj), TYPE_TMP421)
|
||||
|
||||
#define TMP421_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(TMP421Class, (klass), TYPE_TMP421)
|
||||
#define TMP421_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(TMP421Class, (obj), TYPE_TMP421)
|
||||
|
||||
/* the TMP421 registers */
|
||||
#define TMP421_STATUS_REG 0x08
|
||||
#define TMP421_STATUS_BUSY (1 << 7)
|
||||
#define TMP421_CONFIG_REG_1 0x09
|
||||
#define TMP421_CONFIG_RANGE (1 << 2)
|
||||
#define TMP421_CONFIG_SHUTDOWN (1 << 6)
|
||||
#define TMP421_CONFIG_REG_2 0x0A
|
||||
#define TMP421_CONFIG_RC (1 << 2)
|
||||
#define TMP421_CONFIG_LEN (1 << 3)
|
||||
#define TMP421_CONFIG_REN (1 << 4)
|
||||
#define TMP421_CONFIG_REN2 (1 << 5)
|
||||
#define TMP421_CONFIG_REN3 (1 << 6)
|
||||
|
||||
#define TMP421_CONVERSION_RATE_REG 0x0B
|
||||
#define TMP421_ONE_SHOT 0x0F
|
||||
|
||||
#define TMP421_RESET 0xFC
|
||||
#define TMP421_MANUFACTURER_ID_REG 0xFE
|
||||
#define TMP421_DEVICE_ID_REG 0xFF
|
||||
|
||||
#define TMP421_TEMP_MSB0 0x00
|
||||
#define TMP421_TEMP_MSB1 0x01
|
||||
#define TMP421_TEMP_MSB2 0x02
|
||||
#define TMP421_TEMP_MSB3 0x03
|
||||
#define TMP421_TEMP_LSB0 0x10
|
||||
#define TMP421_TEMP_LSB1 0x11
|
||||
#define TMP421_TEMP_LSB2 0x12
|
||||
#define TMP421_TEMP_LSB3 0x13
|
||||
|
||||
static const int32_t mins[2] = { -40000, -55000 };
|
||||
static const int32_t maxs[2] = { 127000, 150000 };
|
||||
|
||||
static void tmp421_get_temperature(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
TMP421State *s = TMP421(obj);
|
||||
bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE);
|
||||
int offset = ext_range * 64 * 256;
|
||||
int64_t value;
|
||||
int tempid;
|
||||
|
||||
if (sscanf(name, "temperature%d", &tempid) != 1) {
|
||||
error_setg(errp, "error reading %s: %m", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tempid >= 4 || tempid < 0) {
|
||||
error_setg(errp, "error reading %s", name);
|
||||
return;
|
||||
}
|
||||
|
||||
value = ((s->temperature[tempid] - offset) * 1000 + 128) / 256;
|
||||
|
||||
visit_type_int(v, name, &value, errp);
|
||||
}
|
||||
|
||||
/* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8
|
||||
* fixed point, so units are 1/256 centigrades. A simple ratio will do.
|
||||
*/
|
||||
static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
TMP421State *s = TMP421(obj);
|
||||
Error *local_err = NULL;
|
||||
int64_t temp;
|
||||
bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE);
|
||||
int offset = ext_range * 64 * 256;
|
||||
int tempid;
|
||||
|
||||
visit_type_int(v, name, &temp, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (temp >= maxs[ext_range] || temp < mins[ext_range]) {
|
||||
error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range",
|
||||
temp / 1000, temp % 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sscanf(name, "temperature%d", &tempid) != 1) {
|
||||
error_setg(errp, "error reading %s: %m", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tempid >= 4 || tempid < 0) {
|
||||
error_setg(errp, "error reading %s", name);
|
||||
return;
|
||||
}
|
||||
|
||||
s->temperature[tempid] = (int16_t) ((temp * 256 - 128) / 1000) + offset;
|
||||
}
|
||||
|
||||
static void tmp421_read(TMP421State *s)
|
||||
{
|
||||
TMP421Class *sc = TMP421_GET_CLASS(s);
|
||||
|
||||
s->len = 0;
|
||||
|
||||
switch (s->pointer) {
|
||||
case TMP421_MANUFACTURER_ID_REG:
|
||||
s->buf[s->len++] = TMP421_MANUFACTURER_ID;
|
||||
break;
|
||||
case TMP421_DEVICE_ID_REG:
|
||||
s->buf[s->len++] = sc->dev->model;
|
||||
break;
|
||||
case TMP421_CONFIG_REG_1:
|
||||
s->buf[s->len++] = s->config[0];
|
||||
break;
|
||||
case TMP421_CONFIG_REG_2:
|
||||
s->buf[s->len++] = s->config[1];
|
||||
break;
|
||||
case TMP421_CONVERSION_RATE_REG:
|
||||
s->buf[s->len++] = s->rate;
|
||||
break;
|
||||
case TMP421_STATUS_REG:
|
||||
s->buf[s->len++] = s->status;
|
||||
break;
|
||||
|
||||
/* FIXME: check for channel enablement in config registers */
|
||||
case TMP421_TEMP_MSB0:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 8);
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_MSB1:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 8);
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_MSB2:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 8);
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_MSB3:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 8);
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_LSB0:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_LSB1:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_LSB2:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0;
|
||||
break;
|
||||
case TMP421_TEMP_LSB3:
|
||||
s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tmp421_reset(I2CSlave *i2c);
|
||||
|
||||
static void tmp421_write(TMP421State *s)
|
||||
{
|
||||
switch (s->pointer) {
|
||||
case TMP421_CONVERSION_RATE_REG:
|
||||
s->rate = s->buf[0];
|
||||
break;
|
||||
case TMP421_CONFIG_REG_1:
|
||||
s->config[0] = s->buf[0];
|
||||
break;
|
||||
case TMP421_CONFIG_REG_2:
|
||||
s->config[1] = s->buf[0];
|
||||
break;
|
||||
case TMP421_RESET:
|
||||
tmp421_reset(I2C_SLAVE(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int tmp421_rx(I2CSlave *i2c)
|
||||
{
|
||||
TMP421State *s = TMP421(i2c);
|
||||
|
||||
if (s->len < 2) {
|
||||
return s->buf[s->len++];
|
||||
} else {
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static int tmp421_tx(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
TMP421State *s = TMP421(i2c);
|
||||
|
||||
if (s->len == 0) {
|
||||
/* first byte is the register pointer for a read or write
|
||||
* operation */
|
||||
s->pointer = data;
|
||||
s->len++;
|
||||
} else if (s->len == 1) {
|
||||
/* second byte is the data to write. The device only supports
|
||||
* one byte writes */
|
||||
s->buf[0] = data;
|
||||
tmp421_write(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmp421_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
TMP421State *s = TMP421(i2c);
|
||||
|
||||
if (event == I2C_START_RECV) {
|
||||
tmp421_read(s);
|
||||
}
|
||||
|
||||
s->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_tmp421 = {
|
||||
.name = "TMP421",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(len, TMP421State),
|
||||
VMSTATE_UINT8_ARRAY(buf, TMP421State, 2),
|
||||
VMSTATE_UINT8(pointer, TMP421State),
|
||||
VMSTATE_UINT8_ARRAY(config, TMP421State, 2),
|
||||
VMSTATE_UINT8(status, TMP421State),
|
||||
VMSTATE_UINT8(rate, TMP421State),
|
||||
VMSTATE_INT16_ARRAY(temperature, TMP421State, 4),
|
||||
VMSTATE_I2C_SLAVE(i2c, TMP421State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void tmp421_reset(I2CSlave *i2c)
|
||||
{
|
||||
TMP421State *s = TMP421(i2c);
|
||||
TMP421Class *sc = TMP421_GET_CLASS(s);
|
||||
|
||||
memset(s->temperature, 0, sizeof(s->temperature));
|
||||
s->pointer = 0;
|
||||
|
||||
s->config[0] = 0; /* TMP421_CONFIG_RANGE */
|
||||
|
||||
/* resistance correction and channel enablement */
|
||||
switch (sc->dev->model) {
|
||||
case TMP421_DEVICE_ID:
|
||||
s->config[1] = 0x1c;
|
||||
break;
|
||||
case TMP422_DEVICE_ID:
|
||||
s->config[1] = 0x3c;
|
||||
break;
|
||||
case TMP423_DEVICE_ID:
|
||||
s->config[1] = 0x7c;
|
||||
break;
|
||||
}
|
||||
|
||||
s->rate = 0x7; /* 8Hz */
|
||||
s->status = 0;
|
||||
}
|
||||
|
||||
static int tmp421_init(I2CSlave *i2c)
|
||||
{
|
||||
TMP421State *s = TMP421(i2c);
|
||||
|
||||
tmp421_reset(&s->i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tmp421_initfn(Object *obj)
|
||||
{
|
||||
object_property_add(obj, "temperature0", "int",
|
||||
tmp421_get_temperature,
|
||||
tmp421_set_temperature, NULL, NULL, NULL);
|
||||
object_property_add(obj, "temperature1", "int",
|
||||
tmp421_get_temperature,
|
||||
tmp421_set_temperature, NULL, NULL, NULL);
|
||||
object_property_add(obj, "temperature2", "int",
|
||||
tmp421_get_temperature,
|
||||
tmp421_set_temperature, NULL, NULL, NULL);
|
||||
object_property_add(obj, "temperature3", "int",
|
||||
tmp421_get_temperature,
|
||||
tmp421_set_temperature, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void tmp421_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
TMP421Class *sc = TMP421_CLASS(klass);
|
||||
|
||||
k->init = tmp421_init;
|
||||
k->event = tmp421_event;
|
||||
k->recv = tmp421_rx;
|
||||
k->send = tmp421_tx;
|
||||
dc->vmsd = &vmstate_tmp421;
|
||||
sc->dev = (DeviceInfo *) data;
|
||||
}
|
||||
|
||||
static const TypeInfo tmp421_info = {
|
||||
.name = TYPE_TMP421,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_size = sizeof(TMP421State),
|
||||
.class_size = sizeof(TMP421Class),
|
||||
.instance_init = tmp421_initfn,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void tmp421_register_types(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
type_register_static(&tmp421_info);
|
||||
for (i = 0; i < ARRAY_SIZE(devices); ++i) {
|
||||
TypeInfo ti = {
|
||||
.name = devices[i].name,
|
||||
.parent = TYPE_TMP421,
|
||||
.class_init = tmp421_class_init,
|
||||
.class_data = (void *) &devices[i],
|
||||
};
|
||||
type_register(&ti);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(tmp421_register_types)
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi-event.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "migration/misc.h"
|
||||
|
||||
#define VIRTIO_NET_VM_VERSION 11
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "qemu/bswap.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "migration/register.h"
|
||||
|
||||
#include "vmxnet3.h"
|
||||
#include "vmxnet_debug.h"
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
#define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100
|
||||
#define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1
|
||||
|
||||
typedef struct GenPCIERootPort {
|
||||
/*< private >*/
|
||||
PCIESlot parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
bool migrate_msix;
|
||||
} GenPCIERootPort;
|
||||
|
||||
static uint8_t gen_rp_aer_vector(const PCIDevice *d)
|
||||
{
|
||||
return 0;
|
||||
@@ -45,6 +53,13 @@ static void gen_rp_interrupts_uninit(PCIDevice *d)
|
||||
msix_uninit_exclusive_bar(d);
|
||||
}
|
||||
|
||||
static bool gen_rp_test_migrate_msix(void *opaque, int version_id)
|
||||
{
|
||||
GenPCIERootPort *rp = opaque;
|
||||
|
||||
return rp->migrate_msix;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rp_dev = {
|
||||
.name = "pcie-root-port",
|
||||
.version_id = 1,
|
||||
@@ -54,10 +69,18 @@ static const VMStateDescription vmstate_rp_dev = {
|
||||
VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
|
||||
VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
|
||||
PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
|
||||
VMSTATE_MSIX_TEST(parent_obj.parent_obj.parent_obj.parent_obj,
|
||||
GenPCIERootPort,
|
||||
gen_rp_test_migrate_msix),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property gen_rp_props[] = {
|
||||
DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void gen_rp_dev_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@@ -68,6 +91,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data)
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP;
|
||||
dc->desc = "PCI Express Root Port";
|
||||
dc->vmsd = &vmstate_rp_dev;
|
||||
dc->props = gen_rp_props;
|
||||
rpc->aer_vector = gen_rp_aer_vector;
|
||||
rpc->interrupts_init = gen_rp_interrupts_init;
|
||||
rpc->interrupts_uninit = gen_rp_interrupts_uninit;
|
||||
@@ -77,6 +101,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data)
|
||||
static const TypeInfo gen_rp_dev_info = {
|
||||
.name = TYPE_GEN_PCIE_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.instance_size = sizeof(GenPCIERootPort),
|
||||
.class_init = gen_rp_dev_class_init,
|
||||
};
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name,
|
||||
visit_type_uint32(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static Property mch_props[] = {
|
||||
static Property q35_host_props[] = {
|
||||
DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr,
|
||||
MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
|
||||
DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost,
|
||||
@@ -154,7 +154,7 @@ static void q35_host_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
hc->root_bus_path = q35_host_root_bus_path;
|
||||
dc->realize = q35_host_realize;
|
||||
dc->props = mch_props;
|
||||
dc->props = q35_host_props;
|
||||
/* Reason: needs to be wired up by pc_q35_init */
|
||||
dc->user_creatable = false;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
@@ -369,7 +369,7 @@ static void mch_update_smram(MCHPCIState *mch)
|
||||
tseg_size = 1024 * 1024 * 8;
|
||||
break;
|
||||
default:
|
||||
tseg_size = 0;
|
||||
tseg_size = 1024 * 1024 * (uint32_t)mch->ext_tseg_mbytes;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -392,6 +392,17 @@ static void mch_update_smram(MCHPCIState *mch)
|
||||
memory_region_transaction_commit();
|
||||
}
|
||||
|
||||
static void mch_update_ext_tseg_mbytes(MCHPCIState *mch)
|
||||
{
|
||||
PCIDevice *pd = PCI_DEVICE(mch);
|
||||
uint8_t *reg = pd->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES;
|
||||
|
||||
if (mch->ext_tseg_mbytes > 0 &&
|
||||
pci_get_word(reg) == MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY) {
|
||||
pci_set_word(reg, mch->ext_tseg_mbytes);
|
||||
}
|
||||
}
|
||||
|
||||
static void mch_write_config(PCIDevice *d,
|
||||
uint32_t address, uint32_t val, int len)
|
||||
{
|
||||
@@ -413,6 +424,11 @@ static void mch_write_config(PCIDevice *d,
|
||||
MCH_HOST_BRIDGE_SMRAM_SIZE)) {
|
||||
mch_update_smram(mch);
|
||||
}
|
||||
|
||||
if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES,
|
||||
MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) {
|
||||
mch_update_ext_tseg_mbytes(mch);
|
||||
}
|
||||
}
|
||||
|
||||
static void mch_update(MCHPCIState *mch)
|
||||
@@ -420,6 +436,7 @@ static void mch_update(MCHPCIState *mch)
|
||||
mch_update_pciexbar(mch);
|
||||
mch_update_pam(mch);
|
||||
mch_update_smram(mch);
|
||||
mch_update_ext_tseg_mbytes(mch);
|
||||
}
|
||||
|
||||
static int mch_post_load(void *opaque, int version_id)
|
||||
@@ -457,6 +474,11 @@ static void mch_reset(DeviceState *qdev)
|
||||
d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK;
|
||||
d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK;
|
||||
|
||||
if (mch->ext_tseg_mbytes > 0) {
|
||||
pci_set_word(d->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES,
|
||||
MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY);
|
||||
}
|
||||
|
||||
mch_update(mch);
|
||||
}
|
||||
|
||||
@@ -465,6 +487,12 @@ static void mch_realize(PCIDevice *d, Error **errp)
|
||||
int i;
|
||||
MCHPCIState *mch = MCH_PCI_DEVICE(d);
|
||||
|
||||
if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) {
|
||||
error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16,
|
||||
mch->ext_tseg_mbytes);
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup pci memory mapping */
|
||||
pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory,
|
||||
mch->pci_address_space);
|
||||
@@ -530,6 +558,12 @@ uint64_t mch_mcfg_base(void)
|
||||
return MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT;
|
||||
}
|
||||
|
||||
static Property mch_props[] = {
|
||||
DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes,
|
||||
16),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void mch_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
@@ -538,6 +572,7 @@ static void mch_class_init(ObjectClass *klass, void *data)
|
||||
k->realize = mch_realize;
|
||||
k->config_write = mch_write_config;
|
||||
dc->reset = mch_reset;
|
||||
dc->props = mch_props;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->desc = "Host bridge";
|
||||
dc->vmsd = &vmstate_mch;
|
||||
|
||||
@@ -118,18 +118,20 @@ static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp)
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
Object *obj;
|
||||
|
||||
obj = object_new(TYPE_PNV_ICP);
|
||||
object_property_add_child(OBJECT(cpu), "icp", obj, NULL);
|
||||
object_property_add_const_link(obj, "xics", OBJECT(xi), &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
object_property_set_bool(child, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(child, true, "realized", &local_err);
|
||||
obj = object_new(TYPE_PNV_ICP);
|
||||
object_property_add_child(child, "icp", obj, NULL);
|
||||
object_unref(obj);
|
||||
object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(xi),
|
||||
&error_abort);
|
||||
object_property_add_const_link(obj, ICP_PROP_CPU, child, &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
object_unparent(obj);
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
@@ -140,8 +142,6 @@ static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp)
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
xics_cpu_setup(xi, cpu, ICP(obj));
|
||||
}
|
||||
|
||||
static void pnv_core_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
@@ -474,7 +474,8 @@ static void pnv_psi_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
/* Create PSI interrupt control source */
|
||||
object_property_add_const_link(OBJECT(ics), "xics", obj, &error_abort);
|
||||
object_property_add_const_link(OBJECT(ics), ICS_PROP_XICS, obj,
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "migration/migration.h"
|
||||
#include "migration/misc.h"
|
||||
#include "migration/global_state.h"
|
||||
#include "migration/register.h"
|
||||
#include "mmu-hash64.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "qom/cpu.h"
|
||||
@@ -107,7 +109,8 @@ static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
|
||||
|
||||
obj = object_new(type_ics);
|
||||
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
||||
object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort);
|
||||
object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
|
||||
&error_abort);
|
||||
object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err);
|
||||
if (local_err) {
|
||||
goto error;
|
||||
@@ -2441,6 +2444,12 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus,
|
||||
return g_strdup_printf("disk@%"PRIX64, (uint64_t)id << 32);
|
||||
}
|
||||
|
||||
if (g_str_equal("pci-bridge", qdev_fw_name(dev))) {
|
||||
/* SLOF uses "pci" instead of "pci-bridge" for PCI bridges */
|
||||
PCIDevice *pcidev = CAST(PCIDevice, dev, TYPE_PCI_DEVICE);
|
||||
return g_strdup_printf("pci@%x", PCI_SLOT(pcidev->devfn));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2523,7 +2532,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
|
||||
Error **errp)
|
||||
{
|
||||
sPAPRDRConnector *drc;
|
||||
sPAPRDRConnectorClass *drck;
|
||||
uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
|
||||
int i, fdt_offset, fdt_size;
|
||||
void *fdt;
|
||||
@@ -2538,10 +2546,10 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
|
||||
fdt_offset = spapr_populate_memory_node(fdt, node, addr,
|
||||
SPAPR_MEMORY_BLOCK_SIZE);
|
||||
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp);
|
||||
spapr_drc_attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp);
|
||||
addr += SPAPR_MEMORY_BLOCK_SIZE;
|
||||
if (!dev->hotplugged) {
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
/* guests expect coldplugged LMBs to be pre-allocated */
|
||||
drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE);
|
||||
drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED);
|
||||
@@ -2554,7 +2562,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
|
||||
if (dedicated_hp_event_source) {
|
||||
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
|
||||
addr_start / SPAPR_MEMORY_BLOCK_SIZE);
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
|
||||
nr_lmbs,
|
||||
spapr_drc_index(drc));
|
||||
@@ -2615,8 +2622,11 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
if (mem_dev && !kvmppc_is_mem_backend_page_size_ok(mem_dev)) {
|
||||
error_setg(errp, "Memory backend has bad page size. "
|
||||
"Use 'memory-backend-file' with correct mem-path.");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(mem_dev);
|
||||
}
|
||||
|
||||
struct sPAPRDIMMState {
|
||||
@@ -2673,7 +2683,7 @@ static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms,
|
||||
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
|
||||
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
||||
g_assert(drc);
|
||||
if (drc->indicator_state != SPAPR_DR_INDICATOR_STATE_INACTIVE) {
|
||||
if (drc->dev) {
|
||||
avail_lmbs++;
|
||||
}
|
||||
addr += SPAPR_MEMORY_BLOCK_SIZE;
|
||||
@@ -2697,10 +2707,11 @@ void spapr_lmb_release(DeviceState *dev)
|
||||
* during the unplug process. In this case recover it. */
|
||||
if (ds == NULL) {
|
||||
ds = spapr_recover_pending_dimm_state(spapr, PC_DIMM(dev));
|
||||
if (ds->nr_lmbs) {
|
||||
return;
|
||||
}
|
||||
} else if (--ds->nr_lmbs) {
|
||||
/* The DRC being examined by the caller at least must be counted */
|
||||
g_assert(ds->nr_lmbs);
|
||||
}
|
||||
|
||||
if (--ds->nr_lmbs) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2738,7 +2749,6 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
|
||||
uint64_t addr_start, addr;
|
||||
int i;
|
||||
sPAPRDRConnector *drc;
|
||||
sPAPRDRConnectorClass *drck;
|
||||
sPAPRDIMMState *ds;
|
||||
|
||||
addr_start = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP,
|
||||
@@ -2758,14 +2768,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
|
||||
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
||||
g_assert(drc);
|
||||
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
drck->detach(drc, dev, errp);
|
||||
spapr_drc_detach(drc, dev, errp);
|
||||
addr += SPAPR_MEMORY_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
|
||||
addr_start / SPAPR_MEMORY_BLOCK_SIZE);
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
|
||||
nr_lmbs, spapr_drc_index(drc));
|
||||
out:
|
||||
@@ -2820,7 +2828,6 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
{
|
||||
int index;
|
||||
sPAPRDRConnector *drc;
|
||||
sPAPRDRConnectorClass *drck;
|
||||
Error *local_err = NULL;
|
||||
CPUCore *cc = CPU_CORE(dev);
|
||||
int smt = kvmppc_smt_threads();
|
||||
@@ -2838,8 +2845,7 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * smt);
|
||||
g_assert(drc);
|
||||
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
drck->detach(drc, dev, &local_err);
|
||||
spapr_drc_detach(drc, dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -2883,8 +2889,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
}
|
||||
|
||||
if (drc) {
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err);
|
||||
spapr_drc_attach(drc, dev, fdt, fdt_offset, !dev->hotplugged,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
g_free(fdt);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
@@ -53,9 +53,6 @@ static void spapr_cpu_reset(void *opaque)
|
||||
|
||||
static void spapr_cpu_destroy(PowerPCCPU *cpu)
|
||||
{
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
|
||||
xics_cpu_destroy(XICS_FABRIC(spapr), cpu);
|
||||
qemu_unregister_reset(spapr_cpu_reset, cpu);
|
||||
}
|
||||
|
||||
@@ -140,16 +137,7 @@ static void spapr_cpu_core_realize_child(Object *child, Error **errp)
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
CPUState *cs = CPU(child);
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
Object *obj;
|
||||
|
||||
obj = object_new(spapr->icp_type);
|
||||
object_property_add_child(OBJECT(cpu), "icp", obj, &error_abort);
|
||||
object_unref(obj);
|
||||
object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
goto error;
|
||||
}
|
||||
Object *obj = NULL;
|
||||
|
||||
object_property_set_bool(child, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
@@ -161,7 +149,17 @@ static void spapr_cpu_core_realize_child(Object *child, Error **errp)
|
||||
goto error;
|
||||
}
|
||||
|
||||
xics_cpu_setup(XICS_FABRIC(spapr), cpu, ICP(obj));
|
||||
obj = object_new(spapr->icp_type);
|
||||
object_property_add_child(child, "icp", obj, &error_abort);
|
||||
object_unref(obj);
|
||||
object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(spapr),
|
||||
&error_abort);
|
||||
object_property_add_const_link(obj, ICP_PROP_CPU, child, &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
|
||||
@@ -49,8 +49,6 @@ uint32_t spapr_drc_index(sPAPRDRConnector *drc)
|
||||
static uint32_t set_isolation_state(sPAPRDRConnector *drc,
|
||||
sPAPRDRIsolationState state)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
|
||||
trace_spapr_drc_set_isolation_state(spapr_drc_index(drc), state);
|
||||
|
||||
/* if the guest is configuring a device attached to this DRC, we
|
||||
@@ -105,7 +103,7 @@ static uint32_t set_isolation_state(sPAPRDRConnector *drc,
|
||||
uint32_t drc_index = spapr_drc_index(drc);
|
||||
if (drc->configured) {
|
||||
trace_spapr_drc_set_isolation_state_finalizing(drc_index);
|
||||
drck->detach(drc, DEVICE(drc->dev), NULL);
|
||||
spapr_drc_detach(drc, DEVICE(drc->dev), NULL);
|
||||
} else {
|
||||
trace_spapr_drc_set_isolation_state_deferring(drc_index);
|
||||
}
|
||||
@@ -116,19 +114,9 @@ static uint32_t set_isolation_state(sPAPRDRConnector *drc,
|
||||
return RTAS_OUT_SUCCESS;
|
||||
}
|
||||
|
||||
static uint32_t set_indicator_state(sPAPRDRConnector *drc,
|
||||
sPAPRDRIndicatorState state)
|
||||
{
|
||||
trace_spapr_drc_set_indicator_state(spapr_drc_index(drc), state);
|
||||
drc->indicator_state = state;
|
||||
return RTAS_OUT_SUCCESS;
|
||||
}
|
||||
|
||||
static uint32_t set_allocation_state(sPAPRDRConnector *drc,
|
||||
sPAPRDRAllocationState state)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
|
||||
trace_spapr_drc_set_allocation_state(spapr_drc_index(drc), state);
|
||||
|
||||
if (state == SPAPR_DR_ALLOCATION_STATE_USABLE) {
|
||||
@@ -140,17 +128,6 @@ static uint32_t set_allocation_state(sPAPRDRConnector *drc,
|
||||
if (!drc->dev) {
|
||||
return RTAS_OUT_NO_SUCH_INDICATOR;
|
||||
}
|
||||
if (drc->awaiting_release && drc->awaiting_allocation) {
|
||||
/* kernel is acknowledging a previous hotplug event
|
||||
* while we are already removing it.
|
||||
* it's safe to ignore awaiting_allocation here since we know the
|
||||
* situation is predicated on the guest either already having done
|
||||
* so (boot-time hotplug), or never being able to acquire in the
|
||||
* first place (hotplug followed by immediate unplug).
|
||||
*/
|
||||
drc->awaiting_allocation_skippable = true;
|
||||
return RTAS_OUT_NO_SUCH_INDICATOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PCI) {
|
||||
@@ -159,7 +136,7 @@ static uint32_t set_allocation_state(sPAPRDRConnector *drc,
|
||||
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
|
||||
uint32_t drc_index = spapr_drc_index(drc);
|
||||
trace_spapr_drc_set_allocation_state_finalizing(drc_index);
|
||||
drck->detach(drc, DEVICE(drc->dev), NULL);
|
||||
spapr_drc_detach(drc, DEVICE(drc->dev), NULL);
|
||||
} else if (drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE) {
|
||||
drc->awaiting_allocation = false;
|
||||
}
|
||||
@@ -167,9 +144,32 @@ static uint32_t set_allocation_state(sPAPRDRConnector *drc,
|
||||
return RTAS_OUT_SUCCESS;
|
||||
}
|
||||
|
||||
static const char *get_name(sPAPRDRConnector *drc)
|
||||
static const char *spapr_drc_name(sPAPRDRConnector *drc)
|
||||
{
|
||||
return drc->name;
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
|
||||
/* human-readable name for a DRC to encode into the DT
|
||||
* description. this is mainly only used within a guest in place
|
||||
* of the unique DRC index.
|
||||
*
|
||||
* in the case of VIO/PCI devices, it corresponds to a "location
|
||||
* code" that maps a logical device/function (DRC index) to a
|
||||
* physical (or virtual in the case of VIO) location in the system
|
||||
* by chaining together the "location label" for each
|
||||
* encapsulating component.
|
||||
*
|
||||
* since this is more to do with diagnosing physical hardware
|
||||
* issues than guest compatibility, we choose location codes/DRC
|
||||
* names that adhere to the documented format, but avoid encoding
|
||||
* the entire topology information into the label/code, instead
|
||||
* just using the location codes based on the labels for the
|
||||
* endpoints (VIO/PCI adaptor connectors), which is basically just
|
||||
* "C" followed by an integer ID.
|
||||
*
|
||||
* DRC names as documented by PAPR+ v2.7, 13.5.2.4
|
||||
* location codes as documented by PAPR+ v2.7, 12.3.1.5
|
||||
*/
|
||||
return g_strdup_printf("%s%d", drck->drc_name_prefix, drc->id);
|
||||
}
|
||||
|
||||
/* has the guest been notified of device attachment? */
|
||||
@@ -185,39 +185,25 @@ static void set_signalled(sPAPRDRConnector *drc)
|
||||
* based on the current allocation/indicator/power states
|
||||
* for the DR connector.
|
||||
*/
|
||||
static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state)
|
||||
static sPAPRDREntitySense physical_entity_sense(sPAPRDRConnector *drc)
|
||||
{
|
||||
if (drc->dev) {
|
||||
if (spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PCI &&
|
||||
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
|
||||
/* for logical DR, we return a state of UNUSABLE
|
||||
* iff the allocation state UNUSABLE.
|
||||
* Otherwise, report the state as USABLE/PRESENT,
|
||||
* as we would for PCI.
|
||||
*/
|
||||
*state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
|
||||
} else {
|
||||
/* this assumes all PCI devices are assigned to
|
||||
* a 'live insertion' power domain, where QEMU
|
||||
* manages power state automatically as opposed
|
||||
* to the guest. present, non-PCI resources are
|
||||
* unaffected by power state.
|
||||
*/
|
||||
*state = SPAPR_DR_ENTITY_SENSE_PRESENT;
|
||||
}
|
||||
} else {
|
||||
if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_PCI) {
|
||||
/* PCI devices, and only PCI devices, use EMPTY
|
||||
* in cases where we'd otherwise use UNUSABLE
|
||||
*/
|
||||
*state = SPAPR_DR_ENTITY_SENSE_EMPTY;
|
||||
} else {
|
||||
*state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
|
||||
}
|
||||
}
|
||||
/* this assumes all PCI devices are assigned to a 'live insertion'
|
||||
* power domain, where QEMU manages power state automatically as
|
||||
* opposed to the guest. present, non-PCI resources are unaffected
|
||||
* by power state.
|
||||
*/
|
||||
return drc->dev ? SPAPR_DR_ENTITY_SENSE_PRESENT
|
||||
: SPAPR_DR_ENTITY_SENSE_EMPTY;
|
||||
}
|
||||
|
||||
trace_spapr_drc_entity_sense(spapr_drc_index(drc), *state);
|
||||
return RTAS_OUT_SUCCESS;
|
||||
static sPAPRDREntitySense logical_entity_sense(sPAPRDRConnector *drc)
|
||||
{
|
||||
if (drc->dev
|
||||
&& (drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE)) {
|
||||
return SPAPR_DR_ENTITY_SENSE_PRESENT;
|
||||
} else {
|
||||
return SPAPR_DR_ENTITY_SENSE_UNUSABLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_get_index(Object *obj, Visitor *v, const char *name,
|
||||
@@ -228,13 +214,6 @@ static void prop_get_index(Object *obj, Visitor *v, const char *name,
|
||||
visit_type_uint32(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static char *prop_get_name(Object *obj, Error **errp)
|
||||
{
|
||||
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
return g_strdup(drck->get_name(drc));
|
||||
}
|
||||
|
||||
static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
@@ -311,8 +290,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
|
||||
} while (fdt_depth != 0);
|
||||
}
|
||||
|
||||
static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
|
||||
int fdt_start_offset, bool coldplug, Error **errp)
|
||||
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
|
||||
int fdt_start_offset, bool coldplug, Error **errp)
|
||||
{
|
||||
trace_spapr_drc_attach(spapr_drc_index(drc));
|
||||
|
||||
@@ -335,7 +314,7 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
|
||||
if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_PCI) {
|
||||
drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED;
|
||||
}
|
||||
drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE;
|
||||
drc->dr_indicator = SPAPR_DR_INDICATOR_ACTIVE;
|
||||
|
||||
drc->dev = d;
|
||||
drc->fdt = fdt;
|
||||
@@ -363,7 +342,7 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static void detach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
|
||||
void spapr_drc_detach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
|
||||
{
|
||||
trace_spapr_drc_detach(spapr_drc_index(drc));
|
||||
|
||||
@@ -401,14 +380,12 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
|
||||
}
|
||||
|
||||
if (drc->awaiting_allocation) {
|
||||
if (!drc->awaiting_allocation_skippable) {
|
||||
drc->awaiting_release = true;
|
||||
trace_spapr_drc_awaiting_allocation(spapr_drc_index(drc));
|
||||
return;
|
||||
}
|
||||
drc->awaiting_release = true;
|
||||
trace_spapr_drc_awaiting_allocation(spapr_drc_index(drc));
|
||||
return;
|
||||
}
|
||||
|
||||
drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE;
|
||||
drc->dr_indicator = SPAPR_DR_INDICATOR_INACTIVE;
|
||||
|
||||
/* Calling release callbacks based on spapr_drc_type(drc). */
|
||||
switch (spapr_drc_type(drc)) {
|
||||
@@ -428,7 +405,6 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
|
||||
}
|
||||
|
||||
drc->awaiting_release = false;
|
||||
drc->awaiting_allocation_skippable = false;
|
||||
g_free(drc->fdt);
|
||||
drc->fdt = NULL;
|
||||
drc->fdt_start_offset = 0;
|
||||
@@ -445,7 +421,6 @@ static void reset(DeviceState *d)
|
||||
{
|
||||
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
sPAPRDREntitySense state;
|
||||
|
||||
trace_spapr_drc_reset(spapr_drc_index(drc));
|
||||
|
||||
@@ -467,7 +442,7 @@ static void reset(DeviceState *d)
|
||||
* force removal if we are
|
||||
*/
|
||||
if (drc->awaiting_release) {
|
||||
drck->detach(drc, DEVICE(drc->dev), NULL);
|
||||
spapr_drc_detach(drc, DEVICE(drc->dev), NULL);
|
||||
}
|
||||
|
||||
/* non-PCI devices may be awaiting a transition to UNUSABLE */
|
||||
@@ -477,8 +452,7 @@ static void reset(DeviceState *d)
|
||||
}
|
||||
}
|
||||
|
||||
drck->entity_sense(drc, &state);
|
||||
if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
|
||||
if (drck->dr_entity_sense(drc) == SPAPR_DR_ENTITY_SENSE_PRESENT) {
|
||||
drck->set_signalled(drc);
|
||||
}
|
||||
}
|
||||
@@ -488,8 +462,7 @@ static bool spapr_drc_needed(void *opaque)
|
||||
sPAPRDRConnector *drc = (sPAPRDRConnector *)opaque;
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
bool rc = false;
|
||||
sPAPRDREntitySense value;
|
||||
drck->entity_sense(drc, &value);
|
||||
sPAPRDREntitySense value = drck->dr_entity_sense(drc);
|
||||
|
||||
/* If no dev is plugged in there is no need to migrate the DRC state */
|
||||
if (value != SPAPR_DR_ENTITY_SENSE_PRESENT) {
|
||||
@@ -524,7 +497,7 @@ static const VMStateDescription vmstate_spapr_drc = {
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_UINT32(isolation_state, sPAPRDRConnector),
|
||||
VMSTATE_UINT32(allocation_state, sPAPRDRConnector),
|
||||
VMSTATE_UINT32(indicator_state, sPAPRDRConnector),
|
||||
VMSTATE_UINT32(dr_indicator, sPAPRDRConnector),
|
||||
VMSTATE_BOOL(configured, sPAPRDRConnector),
|
||||
VMSTATE_BOOL(awaiting_release, sPAPRDRConnector),
|
||||
VMSTATE_BOOL(awaiting_allocation, sPAPRDRConnector),
|
||||
@@ -596,45 +569,6 @@ sPAPRDRConnector *spapr_dr_connector_new(Object *owner, const char *type,
|
||||
object_property_set_bool(OBJECT(drc), true, "realized", NULL);
|
||||
g_free(prop_name);
|
||||
|
||||
/* human-readable name for a DRC to encode into the DT
|
||||
* description. this is mainly only used within a guest in place
|
||||
* of the unique DRC index.
|
||||
*
|
||||
* in the case of VIO/PCI devices, it corresponds to a
|
||||
* "location code" that maps a logical device/function (DRC index)
|
||||
* to a physical (or virtual in the case of VIO) location in the
|
||||
* system by chaining together the "location label" for each
|
||||
* encapsulating component.
|
||||
*
|
||||
* since this is more to do with diagnosing physical hardware
|
||||
* issues than guest compatibility, we choose location codes/DRC
|
||||
* names that adhere to the documented format, but avoid encoding
|
||||
* the entire topology information into the label/code, instead
|
||||
* just using the location codes based on the labels for the
|
||||
* endpoints (VIO/PCI adaptor connectors), which is basically
|
||||
* just "C" followed by an integer ID.
|
||||
*
|
||||
* DRC names as documented by PAPR+ v2.7, 13.5.2.4
|
||||
* location codes as documented by PAPR+ v2.7, 12.3.1.5
|
||||
*/
|
||||
switch (spapr_drc_type(drc)) {
|
||||
case SPAPR_DR_CONNECTOR_TYPE_CPU:
|
||||
drc->name = g_strdup_printf("CPU %d", id);
|
||||
break;
|
||||
case SPAPR_DR_CONNECTOR_TYPE_PHB:
|
||||
drc->name = g_strdup_printf("PHB %d", id);
|
||||
break;
|
||||
case SPAPR_DR_CONNECTOR_TYPE_VIO:
|
||||
case SPAPR_DR_CONNECTOR_TYPE_PCI:
|
||||
drc->name = g_strdup_printf("C%d", id);
|
||||
break;
|
||||
case SPAPR_DR_CONNECTOR_TYPE_LMB:
|
||||
drc->name = g_strdup_printf("LMB %d", id);
|
||||
break;
|
||||
default:
|
||||
g_assert(false);
|
||||
}
|
||||
|
||||
/* PCI slot always start in a USABLE state, and stay there */
|
||||
if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_PCI) {
|
||||
drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE;
|
||||
@@ -650,7 +584,6 @@ static void spapr_dr_connector_instance_init(Object *obj)
|
||||
object_property_add_uint32_ptr(obj, "id", &drc->id, NULL);
|
||||
object_property_add(obj, "index", "uint32", prop_get_index,
|
||||
NULL, NULL, NULL, NULL);
|
||||
object_property_add_str(obj, "name", prop_get_name, NULL, NULL);
|
||||
object_property_add(obj, "fdt", "struct", prop_get_fdt,
|
||||
NULL, NULL, NULL, NULL);
|
||||
}
|
||||
@@ -664,12 +597,7 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
|
||||
dk->realize = realize;
|
||||
dk->unrealize = unrealize;
|
||||
drck->set_isolation_state = set_isolation_state;
|
||||
drck->set_indicator_state = set_indicator_state;
|
||||
drck->set_allocation_state = set_allocation_state;
|
||||
drck->get_name = get_name;
|
||||
drck->entity_sense = entity_sense;
|
||||
drck->attach = attach;
|
||||
drck->detach = detach;
|
||||
drck->release_pending = release_pending;
|
||||
drck->set_signalled = set_signalled;
|
||||
/*
|
||||
@@ -678,12 +606,27 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
|
||||
dk->user_creatable = false;
|
||||
}
|
||||
|
||||
static void spapr_drc_physical_class_init(ObjectClass *k, void *data)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
|
||||
|
||||
drck->dr_entity_sense = physical_entity_sense;
|
||||
}
|
||||
|
||||
static void spapr_drc_logical_class_init(ObjectClass *k, void *data)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
|
||||
|
||||
drck->dr_entity_sense = logical_entity_sense;
|
||||
}
|
||||
|
||||
static void spapr_drc_cpu_class_init(ObjectClass *k, void *data)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
|
||||
|
||||
drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU;
|
||||
drck->typename = "CPU";
|
||||
drck->drc_name_prefix = "CPU ";
|
||||
}
|
||||
|
||||
static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
|
||||
@@ -692,6 +635,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
|
||||
|
||||
drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI;
|
||||
drck->typename = "28";
|
||||
drck->drc_name_prefix = "C";
|
||||
}
|
||||
|
||||
static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
|
||||
@@ -700,6 +644,7 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
|
||||
|
||||
drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB;
|
||||
drck->typename = "MEM";
|
||||
drck->drc_name_prefix = "LMB ";
|
||||
}
|
||||
|
||||
static const TypeInfo spapr_dr_connector_info = {
|
||||
@@ -716,6 +661,7 @@ static const TypeInfo spapr_drc_physical_info = {
|
||||
.name = TYPE_SPAPR_DRC_PHYSICAL,
|
||||
.parent = TYPE_SPAPR_DR_CONNECTOR,
|
||||
.instance_size = sizeof(sPAPRDRConnector),
|
||||
.class_init = spapr_drc_physical_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
@@ -723,6 +669,7 @@ static const TypeInfo spapr_drc_logical_info = {
|
||||
.name = TYPE_SPAPR_DRC_LOGICAL,
|
||||
.parent = TYPE_SPAPR_DR_CONNECTOR,
|
||||
.instance_size = sizeof(sPAPRDRConnector),
|
||||
.class_init = spapr_drc_logical_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
@@ -846,7 +793,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
|
||||
g_array_append_val(drc_power_domains, drc_power_domain);
|
||||
|
||||
/* ibm,drc-names */
|
||||
drc_names = g_string_append(drc_names, drck->get_name(drc));
|
||||
drc_names = g_string_append(drc_names, spapr_drc_name(drc));
|
||||
drc_names = g_string_insert_len(drc_names, -1, "\0", 1);
|
||||
|
||||
/* ibm,drc-types */
|
||||
@@ -905,74 +852,78 @@ out:
|
||||
* RTAS calls
|
||||
*/
|
||||
|
||||
static bool sensor_type_is_dr(uint32_t sensor_type)
|
||||
static uint32_t rtas_set_isolation_state(uint32_t idx, uint32_t state)
|
||||
{
|
||||
switch (sensor_type) {
|
||||
case RTAS_SENSOR_TYPE_ISOLATION_STATE:
|
||||
case RTAS_SENSOR_TYPE_DR:
|
||||
case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
|
||||
return true;
|
||||
sPAPRDRConnector *drc = spapr_drc_by_index(idx);
|
||||
sPAPRDRConnectorClass *drck;
|
||||
|
||||
if (!drc) {
|
||||
return RTAS_OUT_PARAM_ERROR;
|
||||
}
|
||||
|
||||
return false;
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
return drck->set_isolation_state(drc, state);
|
||||
}
|
||||
|
||||
static uint32_t rtas_set_allocation_state(uint32_t idx, uint32_t state)
|
||||
{
|
||||
sPAPRDRConnector *drc = spapr_drc_by_index(idx);
|
||||
sPAPRDRConnectorClass *drck;
|
||||
|
||||
if (!drc) {
|
||||
return RTAS_OUT_PARAM_ERROR;
|
||||
}
|
||||
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
return drck->set_allocation_state(drc, state);
|
||||
}
|
||||
|
||||
static uint32_t rtas_set_dr_indicator(uint32_t idx, uint32_t state)
|
||||
{
|
||||
sPAPRDRConnector *drc = spapr_drc_by_index(idx);
|
||||
|
||||
if (!drc) {
|
||||
return RTAS_OUT_PARAM_ERROR;
|
||||
}
|
||||
|
||||
trace_spapr_drc_set_dr_indicator(idx, state);
|
||||
drc->dr_indicator = state;
|
||||
return RTAS_OUT_SUCCESS;
|
||||
}
|
||||
|
||||
static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
target_ulong args, uint32_t nret,
|
||||
target_ulong rets)
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
uint32_t sensor_type;
|
||||
uint32_t sensor_index;
|
||||
uint32_t sensor_state;
|
||||
uint32_t type, idx, state;
|
||||
uint32_t ret = RTAS_OUT_SUCCESS;
|
||||
sPAPRDRConnector *drc;
|
||||
sPAPRDRConnectorClass *drck;
|
||||
|
||||
if (nargs != 3 || nret != 1) {
|
||||
ret = RTAS_OUT_PARAM_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sensor_type = rtas_ld(args, 0);
|
||||
sensor_index = rtas_ld(args, 1);
|
||||
sensor_state = rtas_ld(args, 2);
|
||||
type = rtas_ld(args, 0);
|
||||
idx = rtas_ld(args, 1);
|
||||
state = rtas_ld(args, 2);
|
||||
|
||||
if (!sensor_type_is_dr(sensor_type)) {
|
||||
goto out_unimplemented;
|
||||
}
|
||||
|
||||
/* if this is a DR sensor we can assume sensor_index == drc_index */
|
||||
drc = spapr_drc_by_index(sensor_index);
|
||||
if (!drc) {
|
||||
trace_spapr_rtas_set_indicator_invalid(sensor_index);
|
||||
ret = RTAS_OUT_PARAM_ERROR;
|
||||
goto out;
|
||||
}
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
|
||||
switch (sensor_type) {
|
||||
switch (type) {
|
||||
case RTAS_SENSOR_TYPE_ISOLATION_STATE:
|
||||
ret = drck->set_isolation_state(drc, sensor_state);
|
||||
ret = rtas_set_isolation_state(idx, state);
|
||||
break;
|
||||
case RTAS_SENSOR_TYPE_DR:
|
||||
ret = drck->set_indicator_state(drc, sensor_state);
|
||||
ret = rtas_set_dr_indicator(idx, state);
|
||||
break;
|
||||
case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
|
||||
ret = drck->set_allocation_state(drc, sensor_state);
|
||||
ret = rtas_set_allocation_state(idx, state);
|
||||
break;
|
||||
default:
|
||||
goto out_unimplemented;
|
||||
ret = RTAS_OUT_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
out:
|
||||
rtas_st(rets, 0, ret);
|
||||
return;
|
||||
|
||||
out_unimplemented:
|
||||
/* currently only DR-related sensors are implemented */
|
||||
trace_spapr_rtas_set_indicator_not_supported(sensor_index, sensor_type);
|
||||
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
@@ -1010,7 +961,7 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
goto out;
|
||||
}
|
||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
ret = drck->entity_sense(drc, &sensor_state);
|
||||
sensor_state = drck->dr_entity_sense(drc);
|
||||
|
||||
out:
|
||||
rtas_st(rets, 0, ret);
|
||||
|
||||
@@ -1344,31 +1344,6 @@ static int spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev,
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
|
||||
sPAPRPHBState *phb,
|
||||
PCIDevice *pdev,
|
||||
Error **errp)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
DeviceState *dev = DEVICE(pdev);
|
||||
void *fdt = NULL;
|
||||
int fdt_start_offset = 0, fdt_size;
|
||||
|
||||
fdt = create_device_tree(&fdt_size);
|
||||
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
|
||||
if (!fdt_start_offset) {
|
||||
error_setg(errp, "Failed to create pci child device tree node");
|
||||
goto out;
|
||||
}
|
||||
|
||||
drck->attach(drc, DEVICE(pdev),
|
||||
fdt, fdt_start_offset, !dev->hotplugged, errp);
|
||||
out:
|
||||
if (*errp) {
|
||||
g_free(fdt);
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback to be called during DRC release. */
|
||||
void spapr_phb_remove_pci_device_cb(DeviceState *dev)
|
||||
{
|
||||
@@ -1386,16 +1361,6 @@ void spapr_phb_remove_pci_device_cb(DeviceState *dev)
|
||||
object_unparent(OBJECT(dev));
|
||||
}
|
||||
|
||||
static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
|
||||
sPAPRPHBState *phb,
|
||||
PCIDevice *pdev,
|
||||
Error **errp)
|
||||
{
|
||||
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||
|
||||
drck->detach(drc, DEVICE(pdev), errp);
|
||||
}
|
||||
|
||||
static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb,
|
||||
uint32_t busnr,
|
||||
int32_t devfn)
|
||||
@@ -1432,6 +1397,8 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
|
||||
Error *local_err = NULL;
|
||||
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
|
||||
uint32_t slotnr = PCI_SLOT(pdev->devfn);
|
||||
void *fdt = NULL;
|
||||
int fdt_start_offset, fdt_size;
|
||||
|
||||
/* if DR is disabled we don't need to do anything in the case of
|
||||
* hotplug or coldplug callbacks
|
||||
@@ -1441,10 +1408,10 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
|
||||
* we need to let them know it's not enabled
|
||||
*/
|
||||
if (plugged_dev->hotplugged) {
|
||||
error_setg(errp, QERR_BUS_NO_HOTPLUG,
|
||||
error_setg(&local_err, QERR_BUS_NO_HOTPLUG,
|
||||
object_get_typename(OBJECT(phb)));
|
||||
}
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_assert(drc);
|
||||
@@ -1455,16 +1422,23 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
|
||||
*/
|
||||
if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] &&
|
||||
PCI_FUNC(pdev->devfn) != 0) {
|
||||
error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s,"
|
||||
error_setg(&local_err, "PCI: slot %d function 0 already ocuppied by %s,"
|
||||
" additional functions can no longer be exposed to guest.",
|
||||
slotnr, bus->devices[PCI_DEVFN(slotnr, 0)]->name);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spapr_phb_add_pci_device(drc, phb, pdev, &local_err);
|
||||
fdt = create_device_tree(&fdt_size);
|
||||
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
|
||||
if (!fdt_start_offset) {
|
||||
error_setg(&local_err, "Failed to create pci child device tree node");
|
||||
goto out;
|
||||
}
|
||||
|
||||
spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset,
|
||||
!plugged_dev->hotplugged, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If this is function 0, signal hotplug for all the device functions.
|
||||
@@ -1481,13 +1455,19 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
|
||||
func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus),
|
||||
PCI_DEVFN(slotnr, i));
|
||||
func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc);
|
||||
func_drck->entity_sense(func_drc, &state);
|
||||
state = func_drck->dr_entity_sense(func_drc);
|
||||
|
||||
if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
|
||||
spapr_hotplug_req_add_by_index(func_drc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
g_free(fdt);
|
||||
}
|
||||
}
|
||||
|
||||
static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
|
||||
@@ -1522,7 +1502,7 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
|
||||
func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus),
|
||||
PCI_DEVFN(slotnr, i));
|
||||
func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc);
|
||||
func_drck->entity_sense(func_drc, &state);
|
||||
state = func_drck->dr_entity_sense(func_drc);
|
||||
if (state == SPAPR_DR_ENTITY_SENSE_PRESENT
|
||||
&& !func_drck->release_pending(func_drc)) {
|
||||
error_setg(errp,
|
||||
@@ -1534,7 +1514,7 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
|
||||
}
|
||||
}
|
||||
|
||||
spapr_phb_remove_pci_device(drc, phb, pdev, &local_err);
|
||||
spapr_drc_detach(drc, DEVICE(pdev), &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -1548,7 +1528,7 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
|
||||
func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus),
|
||||
PCI_DEVFN(slotnr, i));
|
||||
func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc);
|
||||
func_drck->entity_sense(func_drc, &state);
|
||||
state = func_drck->dr_entity_sense(func_drc);
|
||||
if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
|
||||
spapr_hotplug_req_remove_by_index(func_drc);
|
||||
}
|
||||
|
||||
@@ -293,12 +293,9 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu,
|
||||
target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
target_ulong ret = 0;
|
||||
qemu_system_guest_panicked(NULL);
|
||||
|
||||
qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, false, NULL,
|
||||
&error_abort);
|
||||
|
||||
rtas_st(rets, 0, ret);
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
|
||||
@@ -39,12 +39,11 @@ spapr_iommu_ddw_reset(uint64_t buid, uint32_t cfgaddr) "buid=%"PRIx64" addr=%"PR
|
||||
spapr_drc_set_isolation_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: %"PRIx32
|
||||
spapr_drc_set_isolation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32
|
||||
spapr_drc_set_isolation_state_deferring(uint32_t index) "drc: 0x%"PRIx32
|
||||
spapr_drc_set_indicator_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x"
|
||||
spapr_drc_set_dr_indicator(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x"
|
||||
spapr_drc_set_allocation_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x"
|
||||
spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32
|
||||
spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32
|
||||
spapr_drc_set_configured_skipping(uint32_t index) "drc: 0x%"PRIx32", isolated device"
|
||||
spapr_drc_entity_sense(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x"
|
||||
spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32
|
||||
spapr_drc_detach(uint32_t index) "drc: 0x%"PRIx32
|
||||
spapr_drc_awaiting_isolated(uint32_t index) "drc: 0x%"PRIx32
|
||||
@@ -61,8 +60,6 @@ spapr_ovec_parse_vector(int vector, int byte, uint16_t vec_len, uint8_t entry) "
|
||||
spapr_ovec_populate_dt(int byte, uint16_t vec_len, uint8_t entry) "encoding guest vector byte %3d / %3d: 0x%.2x"
|
||||
|
||||
# hw/ppc/spapr_rtas.c
|
||||
spapr_rtas_set_indicator_invalid(uint32_t index) "sensor index: 0x%"PRIx32
|
||||
spapr_rtas_set_indicator_not_supported(uint32_t index, uint32_t type) "sensor index: 0x%"PRIx32", type: %"PRIu32
|
||||
spapr_rtas_get_sensor_state_not_supported(uint32_t index, uint32_t type) "sensor index: 0x%"PRIx32", type: %"PRIu32
|
||||
spapr_rtas_get_sensor_state_invalid(uint32_t index) "sensor index: 0x%"PRIx32
|
||||
spapr_rtas_ibm_configure_connector_invalid(uint32_t index) "DRC index: 0x%"PRIx32
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "hw/qdev.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/s390x/ioinst.h"
|
||||
@@ -432,6 +433,11 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We don't support MIDA. */
|
||||
if (ccw.flags & CCW_FLAG_MIDA) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ccw.flags & CCW_FLAG_SUSPEND) {
|
||||
return suspend_allowed ? -EINPROGRESS : -EINVAL;
|
||||
}
|
||||
@@ -1800,13 +1806,26 @@ void subch_device_save(SubchDev *s, QEMUFile *f)
|
||||
int subch_device_load(SubchDev *s, QEMUFile *f)
|
||||
{
|
||||
SubchDev *old_s;
|
||||
Error *err = NULL;
|
||||
uint16_t old_schid = s->schid;
|
||||
uint16_t old_devno = s->devno;
|
||||
int i;
|
||||
|
||||
s->cssid = qemu_get_byte(f);
|
||||
s->ssid = qemu_get_byte(f);
|
||||
s->schid = qemu_get_be16(f);
|
||||
s->devno = qemu_get_be16(f);
|
||||
if (s->devno != old_devno) {
|
||||
/* Only possible if machine < 2.7 (no css_dev_path) */
|
||||
|
||||
error_setg(&err, "%x != %x", old_devno, s->devno);
|
||||
error_append_hint(&err, "Devno mismatch, tried to load wrong section!"
|
||||
" Likely reason: some sequences of plug and unplug"
|
||||
" can break migration for machine versions prior to"
|
||||
" 2.7 (known design flaw).\n");
|
||||
error_report_err(err);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Re-assign subch. */
|
||||
if (old_schid != s->schid) {
|
||||
old_s = channel_subsys.css[s->cssid]->sch_set[s->ssid]->sch[old_schid];
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "hw/s390x/storage-keys.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "migration/register.h"
|
||||
|
||||
#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */
|
||||
#define S390_SKEYS_SAVE_FLAG_EOS 0x01
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ipl.h"
|
||||
#include "hw/s390x/s390-virtio-ccw.h"
|
||||
#include "hw/s390x/css-bridge.h"
|
||||
#include "migration/register.h"
|
||||
|
||||
static const char *const reset_dev_types[] = {
|
||||
TYPE_VIRTUAL_CSS_BRIDGE,
|
||||
|
||||
@@ -1279,9 +1279,13 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
|
||||
SubchDev *s = ccw_dev->sch;
|
||||
VirtIODevice *vdev = virtio_ccw_get_vdev(s);
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
s->driver_data = dev;
|
||||
subch_device_load(s, f);
|
||||
ret = subch_device_load(s, f);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
/* Re-fill subch_id after loading the subchannel states.*/
|
||||
if (ck->refill_ids) {
|
||||
ck->refill_ids(ccw_dev);
|
||||
|
||||
@@ -11,4 +11,5 @@ obj-$(CONFIG_PSERIES) += spapr_vscsi.o
|
||||
ifeq ($(CONFIG_VIRTIO),y)
|
||||
obj-y += virtio-scsi.o virtio-scsi-dataplane.o
|
||||
obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.o
|
||||
obj-$(CONFIG_VHOST_USER_SCSI) += vhost-scsi-common.o vhost-user-scsi.o
|
||||
endif
|
||||
|
||||
@@ -63,6 +63,7 @@ typedef struct MegasasCmd {
|
||||
|
||||
hwaddr pa;
|
||||
hwaddr pa_size;
|
||||
uint32_t dcmd_opcode;
|
||||
union mfi_frame *frame;
|
||||
SCSIRequest *req;
|
||||
QEMUSGList qsg;
|
||||
@@ -309,9 +310,11 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
|
||||
PCIDevice *pcid = PCI_DEVICE(cmd->state);
|
||||
uint32_t pa_hi = 0, pa_lo;
|
||||
hwaddr pa;
|
||||
int frame_sense_len;
|
||||
|
||||
if (sense_len > cmd->frame->header.sense_len) {
|
||||
sense_len = cmd->frame->header.sense_len;
|
||||
frame_sense_len = cmd->frame->header.sense_len;
|
||||
if (sense_len > frame_sense_len) {
|
||||
sense_len = frame_sense_len;
|
||||
}
|
||||
if (sense_len) {
|
||||
pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo);
|
||||
@@ -511,6 +514,7 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
|
||||
cmd->context &= (uint64_t)0xFFFFFFFF;
|
||||
}
|
||||
cmd->count = count;
|
||||
cmd->dcmd_opcode = -1;
|
||||
s->busy++;
|
||||
|
||||
if (s->consumer_pa) {
|
||||
@@ -605,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s)
|
||||
static void megasas_abort_command(MegasasCmd *cmd)
|
||||
{
|
||||
/* Never abort internal commands. */
|
||||
if (cmd->dcmd_opcode != -1) {
|
||||
return;
|
||||
}
|
||||
if (cmd->req != NULL) {
|
||||
scsi_req_cancel(cmd->req);
|
||||
}
|
||||
@@ -673,15 +680,16 @@ out:
|
||||
static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
|
||||
{
|
||||
dma_addr_t iov_pa, iov_size;
|
||||
int iov_count;
|
||||
|
||||
cmd->flags = le16_to_cpu(cmd->frame->header.flags);
|
||||
if (!cmd->frame->header.sge_count) {
|
||||
iov_count = cmd->frame->header.sge_count;
|
||||
if (!iov_count) {
|
||||
trace_megasas_dcmd_zero_sge(cmd->index);
|
||||
cmd->iov_size = 0;
|
||||
return 0;
|
||||
} else if (cmd->frame->header.sge_count > 1) {
|
||||
trace_megasas_dcmd_invalid_sge(cmd->index,
|
||||
cmd->frame->header.sge_count);
|
||||
} else if (iov_count > 1) {
|
||||
trace_megasas_dcmd_invalid_sge(cmd->index, iov_count);
|
||||
cmd->iov_size = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1012,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
|
||||
uint64_t pd_size;
|
||||
uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
|
||||
uint8_t cmdbuf[6];
|
||||
SCSIRequest *req;
|
||||
size_t len, resid;
|
||||
|
||||
if (!cmd->iov_buf) {
|
||||
@@ -1021,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
|
||||
info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
|
||||
info->vpd_page83[0] = 0x7f;
|
||||
megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
|
||||
req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
||||
if (!req) {
|
||||
cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
||||
if (!cmd->req) {
|
||||
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
||||
"PD get info std inquiry");
|
||||
g_free(cmd->iov_buf);
|
||||
@@ -1031,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
|
||||
}
|
||||
trace_megasas_dcmd_internal_submit(cmd->index,
|
||||
"PD get info std inquiry", lun);
|
||||
len = scsi_req_enqueue(req);
|
||||
len = scsi_req_enqueue(cmd->req);
|
||||
if (len > 0) {
|
||||
cmd->iov_size = len;
|
||||
scsi_req_continue(req);
|
||||
scsi_req_continue(cmd->req);
|
||||
}
|
||||
return MFI_STAT_INVALID_STATUS;
|
||||
} else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
|
||||
megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
|
||||
req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
||||
if (!req) {
|
||||
cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
|
||||
if (!cmd->req) {
|
||||
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
||||
"PD get info vpd inquiry");
|
||||
return MFI_STAT_FLASH_ALLOC_FAIL;
|
||||
}
|
||||
trace_megasas_dcmd_internal_submit(cmd->index,
|
||||
"PD get info vpd inquiry", lun);
|
||||
len = scsi_req_enqueue(req);
|
||||
len = scsi_req_enqueue(cmd->req);
|
||||
if (len > 0) {
|
||||
cmd->iov_size = len;
|
||||
scsi_req_continue(req);
|
||||
scsi_req_continue(cmd->req);
|
||||
}
|
||||
return MFI_STAT_INVALID_STATUS;
|
||||
}
|
||||
@@ -1212,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
|
||||
struct mfi_ld_info *info = cmd->iov_buf;
|
||||
size_t dcmd_size = sizeof(struct mfi_ld_info);
|
||||
uint8_t cdb[6];
|
||||
SCSIRequest *req;
|
||||
ssize_t len, resid;
|
||||
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
|
||||
uint64_t ld_size;
|
||||
@@ -1221,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
|
||||
cmd->iov_buf = g_malloc0(dcmd_size);
|
||||
info = cmd->iov_buf;
|
||||
megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
|
||||
req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
|
||||
if (!req) {
|
||||
cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
|
||||
if (!cmd->req) {
|
||||
trace_megasas_dcmd_req_alloc_failed(cmd->index,
|
||||
"LD get info vpd inquiry");
|
||||
g_free(cmd->iov_buf);
|
||||
@@ -1231,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
|
||||
}
|
||||
trace_megasas_dcmd_internal_submit(cmd->index,
|
||||
"LD get info vpd inquiry", lun);
|
||||
len = scsi_req_enqueue(req);
|
||||
len = scsi_req_enqueue(cmd->req);
|
||||
if (len > 0) {
|
||||
cmd->iov_size = len;
|
||||
scsi_req_continue(req);
|
||||
scsi_req_continue(cmd->req);
|
||||
}
|
||||
return MFI_STAT_INVALID_STATUS;
|
||||
}
|
||||
@@ -1559,22 +1565,21 @@ static const struct dcmd_cmd_tbl_t {
|
||||
|
||||
static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
|
||||
{
|
||||
int opcode;
|
||||
int retval = 0;
|
||||
size_t len;
|
||||
const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
|
||||
|
||||
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
||||
trace_megasas_handle_dcmd(cmd->index, opcode);
|
||||
cmd->dcmd_opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
||||
trace_megasas_handle_dcmd(cmd->index, cmd->dcmd_opcode);
|
||||
if (megasas_map_dcmd(s, cmd) < 0) {
|
||||
return MFI_STAT_MEMORY_NOT_AVAILABLE;
|
||||
}
|
||||
while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) {
|
||||
while (cmdptr->opcode != -1 && cmdptr->opcode != cmd->dcmd_opcode) {
|
||||
cmdptr++;
|
||||
}
|
||||
len = cmd->iov_size;
|
||||
if (cmdptr->opcode == -1) {
|
||||
trace_megasas_dcmd_unhandled(cmd->index, opcode, len);
|
||||
trace_megasas_dcmd_unhandled(cmd->index, cmd->dcmd_opcode, len);
|
||||
retval = megasas_dcmd_dummy(s, cmd);
|
||||
} else {
|
||||
trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len);
|
||||
@@ -1587,15 +1592,14 @@ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
|
||||
}
|
||||
|
||||
static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
|
||||
SCSIRequest *req)
|
||||
SCSIRequest *req, size_t resid)
|
||||
{
|
||||
int opcode;
|
||||
int retval = MFI_STAT_OK;
|
||||
int lun = req->lun;
|
||||
|
||||
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
||||
trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun);
|
||||
switch (opcode) {
|
||||
trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun);
|
||||
cmd->iov_size -= resid;
|
||||
switch (cmd->dcmd_opcode) {
|
||||
case MFI_DCMD_PD_GET_INFO:
|
||||
retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
|
||||
break;
|
||||
@@ -1603,7 +1607,7 @@ static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
|
||||
retval = megasas_ld_get_info_submit(req->dev, lun, cmd);
|
||||
break;
|
||||
default:
|
||||
trace_megasas_dcmd_internal_invalid(cmd->index, opcode);
|
||||
trace_megasas_dcmd_internal_invalid(cmd->index, cmd->dcmd_opcode);
|
||||
retval = MFI_STAT_INVALID_DCMD;
|
||||
break;
|
||||
}
|
||||
@@ -1647,43 +1651,42 @@ static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write)
|
||||
}
|
||||
|
||||
static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
||||
bool is_logical)
|
||||
int frame_cmd)
|
||||
{
|
||||
uint8_t *cdb;
|
||||
int target_id, lun_id, cdb_len;
|
||||
bool is_write;
|
||||
struct SCSIDevice *sdev = NULL;
|
||||
bool is_logical = (frame_cmd == MFI_CMD_LD_SCSI_IO);
|
||||
|
||||
cdb = cmd->frame->pass.cdb;
|
||||
target_id = cmd->frame->header.target_id;
|
||||
lun_id = cmd->frame->header.lun_id;
|
||||
cdb_len = cmd->frame->header.cdb_len;
|
||||
|
||||
if (is_logical) {
|
||||
if (cmd->frame->header.target_id >= MFI_MAX_LD ||
|
||||
cmd->frame->header.lun_id != 0) {
|
||||
if (target_id >= MFI_MAX_LD || lun_id != 0) {
|
||||
trace_megasas_scsi_target_not_present(
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
||||
mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id);
|
||||
return MFI_STAT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
|
||||
cmd->frame->header.lun_id);
|
||||
sdev = scsi_device_find(&s->bus, 0, target_id, lun_id);
|
||||
|
||||
cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
|
||||
trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd],
|
||||
is_logical, cmd->frame->header.target_id,
|
||||
cmd->frame->header.lun_id, sdev, cmd->iov_size);
|
||||
trace_megasas_handle_scsi(mfi_frame_desc[frame_cmd], is_logical,
|
||||
target_id, lun_id, sdev, cmd->iov_size);
|
||||
|
||||
if (!sdev || (megasas_is_jbod(s) && is_logical)) {
|
||||
trace_megasas_scsi_target_not_present(
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
||||
mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id);
|
||||
return MFI_STAT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (cmd->frame->header.cdb_len > 16) {
|
||||
if (cdb_len > 16) {
|
||||
trace_megasas_scsi_invalid_cdb_len(
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id,
|
||||
cmd->frame->header.cdb_len);
|
||||
mfi_frame_desc[frame_cmd], is_logical,
|
||||
target_id, lun_id, cdb_len);
|
||||
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
|
||||
cmd->frame->header.scsi_status = CHECK_CONDITION;
|
||||
s->event_count++;
|
||||
@@ -1697,12 +1700,10 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
||||
return MFI_STAT_SCSI_DONE_WITH_ERROR;
|
||||
}
|
||||
|
||||
cmd->req = scsi_req_new(sdev, cmd->index,
|
||||
cmd->frame->header.lun_id, cdb, cmd);
|
||||
cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cmd);
|
||||
if (!cmd->req) {
|
||||
trace_megasas_scsi_req_alloc_failed(
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
||||
mfi_frame_desc[frame_cmd], target_id, lun_id);
|
||||
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
|
||||
cmd->frame->header.scsi_status = BUSY;
|
||||
s->event_count++;
|
||||
@@ -1723,43 +1724,41 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
|
||||
return MFI_STAT_INVALID_STATUS;
|
||||
}
|
||||
|
||||
static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
|
||||
static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd)
|
||||
{
|
||||
uint32_t lba_count, lba_start_hi, lba_start_lo;
|
||||
uint64_t lba_start;
|
||||
bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE);
|
||||
bool is_write = (frame_cmd == MFI_CMD_LD_WRITE);
|
||||
uint8_t cdb[16];
|
||||
int len;
|
||||
struct SCSIDevice *sdev = NULL;
|
||||
int target_id, lun_id, cdb_len;
|
||||
|
||||
lba_count = le32_to_cpu(cmd->frame->io.header.data_len);
|
||||
lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo);
|
||||
lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
|
||||
lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
|
||||
|
||||
if (cmd->frame->header.target_id < MFI_MAX_LD &&
|
||||
cmd->frame->header.lun_id == 0) {
|
||||
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
|
||||
cmd->frame->header.lun_id);
|
||||
target_id = cmd->frame->header.target_id;
|
||||
lun_id = cmd->frame->header.lun_id;
|
||||
cdb_len = cmd->frame->header.cdb_len;
|
||||
|
||||
if (target_id < MFI_MAX_LD && lun_id == 0) {
|
||||
sdev = scsi_device_find(&s->bus, 0, target_id, lun_id);
|
||||
}
|
||||
|
||||
trace_megasas_handle_io(cmd->index,
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
||||
cmd->frame->header.target_id,
|
||||
cmd->frame->header.lun_id,
|
||||
mfi_frame_desc[frame_cmd], target_id, lun_id,
|
||||
(unsigned long)lba_start, (unsigned long)lba_count);
|
||||
if (!sdev) {
|
||||
trace_megasas_io_target_not_present(cmd->index,
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
||||
mfi_frame_desc[frame_cmd], target_id, lun_id);
|
||||
return MFI_STAT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (cmd->frame->header.cdb_len > 16) {
|
||||
if (cdb_len > 16) {
|
||||
trace_megasas_scsi_invalid_cdb_len(
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd], 1,
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id,
|
||||
cmd->frame->header.cdb_len);
|
||||
mfi_frame_desc[frame_cmd], 1, target_id, lun_id, cdb_len);
|
||||
megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
|
||||
cmd->frame->header.scsi_status = CHECK_CONDITION;
|
||||
s->event_count++;
|
||||
@@ -1776,11 +1775,10 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
|
||||
|
||||
megasas_encode_lba(cdb, lba_start, lba_count, is_write);
|
||||
cmd->req = scsi_req_new(sdev, cmd->index,
|
||||
cmd->frame->header.lun_id, cdb, cmd);
|
||||
lun_id, cdb, cmd);
|
||||
if (!cmd->req) {
|
||||
trace_megasas_scsi_req_alloc_failed(
|
||||
mfi_frame_desc[cmd->frame->header.frame_cmd],
|
||||
cmd->frame->header.target_id, cmd->frame->header.lun_id);
|
||||
mfi_frame_desc[frame_cmd], target_id, lun_id);
|
||||
megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
|
||||
cmd->frame->header.scsi_status = BUSY;
|
||||
s->event_count++;
|
||||
@@ -1797,23 +1795,11 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
|
||||
return MFI_STAT_INVALID_STATUS;
|
||||
}
|
||||
|
||||
static int megasas_finish_internal_command(MegasasCmd *cmd,
|
||||
SCSIRequest *req, size_t resid)
|
||||
{
|
||||
int retval = MFI_STAT_INVALID_CMD;
|
||||
|
||||
if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
|
||||
cmd->iov_size -= resid;
|
||||
retval = megasas_finish_internal_dcmd(cmd, req);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static QEMUSGList *megasas_get_sg_list(SCSIRequest *req)
|
||||
{
|
||||
MegasasCmd *cmd = req->hba_private;
|
||||
|
||||
if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
|
||||
if (cmd->dcmd_opcode != -1) {
|
||||
return NULL;
|
||||
} else {
|
||||
return &cmd->qsg;
|
||||
@@ -1824,18 +1810,16 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
|
||||
{
|
||||
MegasasCmd *cmd = req->hba_private;
|
||||
uint8_t *buf;
|
||||
uint32_t opcode;
|
||||
|
||||
trace_megasas_io_complete(cmd->index, len);
|
||||
|
||||
if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) {
|
||||
if (cmd->dcmd_opcode != -1) {
|
||||
scsi_req_continue(req);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = scsi_req_get_buf(req);
|
||||
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
|
||||
if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
|
||||
if (cmd->dcmd_opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
|
||||
struct mfi_pd_info *info = cmd->iov_buf;
|
||||
|
||||
if (info->inquiry_data[0] == 0x7f) {
|
||||
@@ -1846,7 +1830,7 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
|
||||
memcpy(info->vpd_page83, buf, len);
|
||||
}
|
||||
scsi_req_continue(req);
|
||||
} else if (opcode == MFI_DCMD_LD_GET_INFO) {
|
||||
} else if (cmd->dcmd_opcode == MFI_DCMD_LD_GET_INFO) {
|
||||
struct mfi_ld_info *info = cmd->iov_buf;
|
||||
|
||||
if (cmd->iov_buf) {
|
||||
@@ -1868,11 +1852,11 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status,
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->req == NULL) {
|
||||
if (cmd->dcmd_opcode != -1) {
|
||||
/*
|
||||
* Internal command complete
|
||||
*/
|
||||
cmd_status = megasas_finish_internal_command(cmd, req, resid);
|
||||
cmd_status = megasas_finish_internal_dcmd(cmd, req, resid);
|
||||
if (cmd_status == MFI_STAT_INVALID_STATUS) {
|
||||
return;
|
||||
}
|
||||
@@ -1943,6 +1927,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
|
||||
{
|
||||
uint8_t frame_status = MFI_STAT_INVALID_CMD;
|
||||
uint64_t frame_context;
|
||||
int frame_cmd;
|
||||
MegasasCmd *cmd;
|
||||
|
||||
/*
|
||||
@@ -1961,7 +1946,8 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
|
||||
s->event_count++;
|
||||
return;
|
||||
}
|
||||
switch (cmd->frame->header.frame_cmd) {
|
||||
frame_cmd = cmd->frame->header.frame_cmd;
|
||||
switch (frame_cmd) {
|
||||
case MFI_CMD_INIT:
|
||||
frame_status = megasas_init_firmware(s, cmd);
|
||||
break;
|
||||
@@ -1972,18 +1958,15 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
|
||||
frame_status = megasas_handle_abort(s, cmd);
|
||||
break;
|
||||
case MFI_CMD_PD_SCSI_IO:
|
||||
frame_status = megasas_handle_scsi(s, cmd, 0);
|
||||
break;
|
||||
case MFI_CMD_LD_SCSI_IO:
|
||||
frame_status = megasas_handle_scsi(s, cmd, 1);
|
||||
frame_status = megasas_handle_scsi(s, cmd, frame_cmd);
|
||||
break;
|
||||
case MFI_CMD_LD_READ:
|
||||
case MFI_CMD_LD_WRITE:
|
||||
frame_status = megasas_handle_io(s, cmd);
|
||||
frame_status = megasas_handle_io(s, cmd, frame_cmd);
|
||||
break;
|
||||
default:
|
||||
trace_megasas_unhandled_frame_cmd(cmd->index,
|
||||
cmd->frame->header.frame_cmd);
|
||||
trace_megasas_unhandled_frame_cmd(cmd->index, frame_cmd);
|
||||
s->event_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <linux/vhost.h>
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/migration.h"
|
||||
|
||||
205
hw/scsi/vhost-user-scsi.c
Normal file
205
hw/scsi/vhost-user-scsi.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* vhost-user-scsi host device
|
||||
*
|
||||
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Felipe Franciosi <felipe@nutanix.com>
|
||||
*
|
||||
* This work is largely based on the "vhost-scsi" implementation by:
|
||||
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
* Nicholas Bellinger <nab@risingtidesystems.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/typedefs.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/fw-path-provider.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/virtio/vhost.h"
|
||||
#include "hw/virtio/vhost-backend.h"
|
||||
#include "hw/virtio/vhost-user-scsi.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "chardev/char-fe.h"
|
||||
|
||||
/* Features supported by the host application */
|
||||
static const int user_feature_bits[] = {
|
||||
VIRTIO_F_NOTIFY_ON_EMPTY,
|
||||
VIRTIO_RING_F_INDIRECT_DESC,
|
||||
VIRTIO_RING_F_EVENT_IDX,
|
||||
VIRTIO_SCSI_F_HOTPLUG,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
||||
static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status)
|
||||
{
|
||||
VHostUserSCSI *s = (VHostUserSCSI *)vdev;
|
||||
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
|
||||
bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running;
|
||||
|
||||
if (vsc->dev.started == start) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (start) {
|
||||
int ret;
|
||||
|
||||
ret = vhost_scsi_common_start(vsc);
|
||||
if (ret < 0) {
|
||||
error_report("unable to start vhost-user-scsi: %s", strerror(-ret));
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
vhost_scsi_common_stop(vsc);
|
||||
}
|
||||
}
|
||||
|
||||
static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
}
|
||||
|
||||
static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
|
||||
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
|
||||
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
|
||||
Error *err = NULL;
|
||||
int ret;
|
||||
|
||||
if (!vs->conf.chardev.chr) {
|
||||
error_setg(errp, "vhost-user-scsi: missing chardev");
|
||||
return;
|
||||
}
|
||||
|
||||
virtio_scsi_common_realize(dev, vhost_dummy_handle_output,
|
||||
vhost_dummy_handle_output,
|
||||
vhost_dummy_handle_output, &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
vsc->dev.nvqs = 2 + vs->conf.num_queues;
|
||||
vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs);
|
||||
vsc->dev.vq_index = 0;
|
||||
vsc->dev.backend_features = 0;
|
||||
|
||||
ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev,
|
||||
VHOST_BACKEND_TYPE_USER, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s",
|
||||
strerror(-ret));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Channel and lun both are 0 for bootable vhost-user-scsi disk */
|
||||
vsc->channel = 0;
|
||||
vsc->lun = 0;
|
||||
vsc->target = vs->conf.boot_tpgt;
|
||||
}
|
||||
|
||||
static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
|
||||
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
|
||||
|
||||
/* This will stop the vhost backend. */
|
||||
vhost_user_scsi_set_status(vdev, 0);
|
||||
|
||||
vhost_dev_cleanup(&vsc->dev);
|
||||
g_free(vsc->dev.vqs);
|
||||
|
||||
virtio_scsi_common_unrealize(dev, errp);
|
||||
}
|
||||
|
||||
static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev,
|
||||
uint64_t features, Error **errp)
|
||||
{
|
||||
VHostUserSCSI *s = VHOST_USER_SCSI(vdev);
|
||||
|
||||
/* Turn on predefined features supported by this device */
|
||||
features |= s->host_features;
|
||||
|
||||
return vhost_scsi_common_get_features(vdev, features, errp);
|
||||
}
|
||||
|
||||
static Property vhost_user_scsi_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev),
|
||||
DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
|
||||
DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1),
|
||||
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
|
||||
0xFFFF),
|
||||
DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
|
||||
DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features,
|
||||
VIRTIO_SCSI_F_HOTPLUG,
|
||||
true),
|
||||
DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features,
|
||||
VIRTIO_SCSI_F_CHANGE,
|
||||
true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_vhost_scsi = {
|
||||
.name = "virtio-scsi",
|
||||
.minimum_version_id = 1,
|
||||
.version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_VIRTIO_DEVICE,
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static void vhost_user_scsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
|
||||
|
||||
dc->props = vhost_user_scsi_properties;
|
||||
dc->vmsd = &vmstate_vhost_scsi;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
vdc->realize = vhost_user_scsi_realize;
|
||||
vdc->unrealize = vhost_user_scsi_unrealize;
|
||||
vdc->get_features = vhost_user_scsi_get_features;
|
||||
vdc->set_config = vhost_scsi_common_set_config;
|
||||
vdc->set_status = vhost_user_scsi_set_status;
|
||||
fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
|
||||
}
|
||||
|
||||
static void vhost_user_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
|
||||
|
||||
vsc->feature_bits = user_feature_bits;
|
||||
|
||||
/* Add the bootindex property for this object */
|
||||
device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
|
||||
DEVICE(vsc), NULL);
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_user_scsi_info = {
|
||||
.name = TYPE_VHOST_USER_SCSI,
|
||||
.parent = TYPE_VHOST_SCSI_COMMON,
|
||||
.instance_size = sizeof(VHostUserSCSI),
|
||||
.class_init = vhost_user_scsi_class_init,
|
||||
.instance_init = vhost_user_scsi_instance_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_FW_PATH_PROVIDER },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void virtio_register_types(void)
|
||||
{
|
||||
type_register_static(&vhost_user_scsi_info);
|
||||
}
|
||||
|
||||
type_init(virtio_register_types)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user