Compare commits
	
		
			363 Commits
		
	
	
		
			pull-ui-20
			...
			pull-docs-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 166d434685 | ||
|  | 9ca019c1dd | ||
|  | e7c83a885f | ||
|  | 9514f2648c | ||
|  | 7d1730b7d9 | ||
|  | 1bbe5dc66b | ||
|  | c8c0a1a784 | ||
|  | a57aaa4e74 | ||
|  | f3a6339a5b | ||
|  | 1e0228fd20 | ||
|  | 43ddc182e2 | ||
|  | 5db53e353d | ||
|  | e13886e3a7 | ||
|  | aa488fe3bb | ||
|  | 39ae2474e3 | ||
|  | 14790f730a | ||
|  | a25dc805e2 | ||
|  | a5d8235545 | ||
|  | a73c98e159 | ||
|  | 7c14b3ac07 | ||
|  | 7ecdaa4a96 | ||
|  | da6d674e50 | ||
|  | 1004102a77 | ||
|  | f797c07507 | ||
|  | c98c9eba88 | ||
|  | ccc11b0279 | ||
|  | 44d7ce0ef3 | ||
|  | a4f5c5b723 | ||
|  | dbb74759fa | ||
|  | 241999bf4c | ||
|  | 45ba9f761b | ||
|  | 6e86d90352 | ||
|  | 8b20aefac4 | ||
|  | 373442ea3a | ||
|  | 54a5ba13a9 | ||
|  | 105d86ff38 | ||
|  | a2a7862ca9 | ||
|  | b8bbdb886e | ||
|  | 4bae2b397f | ||
|  | 6181478f63 | ||
|  | f5507e0448 | ||
|  | 6c608953a5 | ||
|  | 3d74ee7dca | ||
|  | 79cad2faac | ||
|  | d34d5b3bbd | ||
|  | 6b591ad613 | ||
|  | 8ea1d05632 | ||
|  | 4729b3a41d | ||
|  | 6048018ef6 | ||
|  | 25ac5bbec4 | ||
|  | a6e3707ece | ||
|  | a70716eb2c | ||
|  | d000b477f2 | ||
|  | 8dc52350f9 | ||
|  | 51654aa52a | ||
|  | 5ee4f3c2c7 | ||
|  | 98a3331a55 | ||
|  | 59ebb6e451 | ||
|  | 3219de458c | ||
|  | 1c1df0198b | ||
|  | ee1ac3a182 | ||
|  | 7eddb5ddac | ||
|  | 9b9fbe8a4e | ||
|  | 39c11580f3 | ||
|  | 8f2d7c3411 | ||
|  | 2dc120beb8 | ||
|  | 8bf69b499a | ||
|  | 857e479552 | ||
|  | 543a7a161f | ||
|  | db6cdfbeba | ||
|  | 46ffd0c031 | ||
|  | 975896fc88 | ||
|  | eaa92c76ce | ||
|  | f4c36bdab6 | ||
|  | 1ab17f9f5c | ||
|  | d045c466d9 | ||
|  | 37d1e4d9bf | ||
|  | 32c813e6c2 | ||
|  | 0072d2a9fc | ||
|  | ba3186c4e4 | ||
|  | 3b1d816984 | ||
|  | d992f2f136 | ||
|  | a5a08302d4 | ||
|  | 5e3a549498 | ||
|  | 685783c5b6 | ||
|  | bd5d2353aa | ||
|  | 70f2e64e4d | ||
|  | 4577b09a27 | ||
|  | 6b4e463ff3 | ||
|  | 6528a4c1f2 | ||
|  | 6d3f4c6d1d | ||
|  | 28f997a82c | ||
|  | 2421f381dc | ||
|  | f62ab6bb8f | ||
|  | d7941f4eed | ||
|  | 1d393bdeae | ||
|  | ef503a8417 | ||
|  | 69785a229d | ||
|  | 6959e4523e | ||
|  | d185cf0ec6 | ||
|  | a8a4d15c1c | ||
|  | 680c7f9606 | ||
|  | 01a5650179 | ||
|  | 5696c6e350 | ||
|  | 2d6b86af14 | ||
|  | 4e4bf5c42c | ||
|  | 52cdbc5869 | ||
|  | becc347e1c | ||
|  | 70b27f3643 | ||
|  | 7dad9ee646 | ||
|  | 2c3b44da07 | ||
|  | c6ccc2c5e6 | ||
|  | 5a1dad9d5a | ||
|  | 6f993f3fca | ||
|  | f6a7240442 | ||
|  | 43421ea05f | ||
|  | dfac03dcd4 | ||
|  | 2b12baf0e3 | ||
|  | fe8ee082db | ||
|  | 63f495beb4 | ||
|  | 92f2b88cea | ||
|  | 5842b55fd4 | ||
|  | df1d8a1f29 | ||
|  | ca759f9e38 | ||
|  | 4881658a4b | ||
|  | a67cf27727 | ||
|  | c22edfebff | ||
|  | 062ba099e0 | ||
|  | c3b9a07a33 | ||
|  | b0706b7167 | ||
|  | e72184455c | ||
|  | 0336cbf853 | ||
|  | e3b9ca8109 | ||
|  | 857baec1d9 | ||
|  | f0aff0f124 | ||
|  | 08e73c48b0 | ||
|  | 372579427a | ||
|  | 2f16960660 | ||
|  | e5143e30fb | ||
|  | 8d04fb55de | ||
|  | 791158d93b | ||
|  | 6546706d28 | ||
|  | 8d4e9146b3 | ||
|  | 2093714314 | ||
|  | 4ec667042d | ||
|  | 6ac3d7e845 | ||
|  | c6489dd921 | ||
|  | 5522924718 | ||
|  | 9f94f84ce7 | ||
|  | f738f296ea | ||
|  | 5f706fdc16 | ||
|  | b1914b824a | ||
|  | 069097dad3 | ||
|  | 0708afa704 | ||
|  | e61cc6b5c6 | ||
|  | 797b608638 | ||
|  | d2256070d2 | ||
|  | 47e13dfd86 | ||
|  | ba690c7171 | ||
|  | 409422cd83 | ||
|  | 94b5024b1f | ||
|  | a8f159d45b | ||
|  | e70dc7f854 | ||
|  | d92d886a3b | ||
|  | 24e0131f37 | ||
|  | 414a8ce57e | ||
|  | 75cdcd1553 | ||
|  | f46bfdbfc8 | ||
|  | f17fd4fdf0 | ||
|  | 4fcdf65ae2 | ||
|  | 606caa0a2a | ||
|  | dab9cc9237 | ||
|  | 753f8da0e0 | ||
|  | 17f942560e | ||
|  | 466dea14e6 | ||
|  | e591591b32 | ||
|  | d2734d2629 | ||
|  | 0b742797aa | ||
|  | a6b4373fa2 | ||
|  | 18aec47967 | ||
|  | 019144b286 | ||
|  | 3403e5eb88 | ||
|  | 4baef2679e | ||
|  | 717adf9609 | ||
|  | b30d188677 | ||
|  | 4295f879be | ||
|  | bc7c08a2c3 | ||
|  | 73245450b3 | ||
|  | 8ee8409eff | ||
|  | 694baf57ae | ||
|  | 2d896b454a | ||
|  | 4f72b8d2a6 | ||
|  | 6ebc069d67 | ||
|  | c4fe9700e6 | ||
|  | 10f25e4844 | ||
|  | c2b2e158cc | ||
|  | d0d1cd70d1 | ||
|  | 35c7cb4caf | ||
|  | 7c81e4e9db | ||
|  | bbf1028a0a | ||
|  | dfad9ec4e9 | ||
|  | 8978b34af3 | ||
|  | 0abfc4b885 | ||
|  | 363e13f86e | ||
|  | cd17ba51f5 | ||
|  | ca6b6e1e68 | ||
|  | 4b32e11a59 | ||
|  | 4d96f329cc | ||
|  | 9eaaf97168 | ||
|  | a68931ea5f | ||
|  | ff9d38963e | ||
|  | b25f23e7db | ||
|  | d3be4b57ce | ||
|  | d081a49af8 | ||
|  | fb6971c110 | ||
|  | df58713396 | ||
|  | 0a4c774086 | ||
|  | 87684b4c40 | ||
|  | c5514d0e4b | ||
|  | f2d672c248 | ||
|  | 535455fdee | ||
|  | 8aba384298 | ||
|  | 1ea69c0e25 | ||
|  | c67ae9333c | ||
|  | c96a1c0ba6 | ||
|  | 38690a1ca7 | ||
|  | fb38ebfbfe | ||
|  | 5065908361 | ||
|  | 18aa49ecf4 | ||
|  | 86cf1e9fe8 | ||
|  | 7659ca1a3e | ||
|  | e0aee726bf | ||
|  | a8d411abac | ||
|  | fd425037d2 | ||
|  | 2e6d856835 | ||
|  | 9ee6f678f4 | ||
|  | 5b929608b9 | ||
|  | f4af7d4438 | ||
|  | 5283c27fc5 | ||
|  | 2635531f20 | ||
|  | c09cec683b | ||
|  | 62d897ca8b | ||
|  | a63f1dfc62 | ||
|  | 80b8c1ee05 | ||
|  | a34011881c | ||
|  | a68a614673 | ||
|  | fe93e3e6ec | ||
|  | d4ccd87e68 | ||
|  | 2770deede0 | ||
|  | 802fc7abd0 | ||
|  | f6b99afdc3 | ||
|  | a4a68476de | ||
|  | 917950d7f5 | ||
|  | be07ad5842 | ||
|  | 115debf26c | ||
|  | ff9006ddbf | ||
|  | f844616bf6 | ||
|  | d3473e147a | ||
|  | 62be393423 | ||
|  | 51b58561c1 | ||
|  | 6e85fce022 | ||
|  | bed9e5ceb1 | ||
|  | 2e2a1b4648 | ||
|  | eb90ab9437 | ||
|  | 08944be1d9 | ||
|  | e295a154c2 | ||
|  | 854e67fea6 | ||
|  | 5fc00480ab | ||
|  | 3f35c3b166 | ||
|  | 796b288f7b | ||
|  | 6135c5e126 | ||
|  | 90ab48eb07 | ||
|  | b166099712 | ||
|  | 31eb1202d3 | ||
|  | 1d56010482 | ||
|  | 81aa2a0fb5 | ||
|  | d4e799292c | ||
|  | 4317142020 | ||
|  | d5895fcb1d | ||
|  | a1cf5fac2b | ||
|  | f778a82f0c | ||
|  | a64aa5785d | ||
|  | fb8b660e17 | ||
|  | 8f2d75e81d | ||
|  | 720b8dc052 | ||
|  | 664cc623bf | ||
|  | 2a7ae4ee50 | ||
|  | 7e465513c1 | ||
|  | a27fa28f03 | ||
|  | e0319b0302 | ||
|  | 2059839baa | ||
|  | a0775e28cd | ||
|  | a7b91d35ba | ||
|  | 1ace7ceac5 | ||
|  | f8c6e1cbc3 | ||
|  | c05df34a87 | ||
|  | 480cff6322 | ||
|  | fed20a70e3 | ||
|  | 91bcea4899 | ||
|  | bd451435c0 | ||
|  | a153bf52b3 | ||
|  | b9e413dd37 | ||
|  | 1919631e6b | ||
|  | 9d45665448 | ||
|  | 2f47da5f7f | ||
|  | 0836c72f70 | ||
|  | b20123a28b | ||
|  | e5c67ab552 | ||
|  | a9d9235567 | ||
|  | ff82911cd3 | ||
|  | c4c497d27f | ||
|  | bf88c1247f | ||
|  | 934ebf48c0 | ||
|  | 35f106e684 | ||
|  | 0c330a734b | ||
|  | c2b38b277a | ||
|  | b856256179 | ||
|  | 31fb4444a4 | ||
|  | 7569c54642 | ||
|  | 0aeebc73b7 | ||
|  | 558ff1b6ef | ||
|  | 2992d6b49c | ||
|  | 72a810f411 | ||
|  | 898248a329 | ||
|  | f89b60f6e5 | ||
|  | 95ed56939e | ||
|  | 26f670a244 | ||
|  | d710e1e7bd | ||
|  | 56f9e46b84 | ||
|  | c8f21dbfc3 | ||
|  | 6753e4ed15 | ||
|  | fea346f569 | ||
|  | 1ede77dfd2 | ||
|  | 5d42ff913b | ||
|  | 378af96155 | ||
|  | ed6f72b827 | ||
|  | d514cfd763 | ||
|  | 5baf2741b4 | ||
|  | 88b86983f3 | ||
|  | 22f2dbe7ea | ||
|  | 7e58326ad7 | ||
|  | 6c441e1d61 | ||
|  | bc535e59c4 | ||
|  | 6e9055641b | ||
|  | 046ab7e9be | ||
|  | 3b40f0e53c | ||
|  | dfbd90e5b9 | ||
|  | 4a4b88fbe1 | ||
|  | 3213835720 | ||
|  | d4e9b75aa0 | ||
|  | b4b9862b53 | ||
|  | 97cd965c07 | ||
|  | ca0176ad83 | ||
|  | 991976f751 | ||
|  | c611c76417 | ||
|  | 5eba0404b9 | ||
|  | 91047df38d | ||
|  | 9796d0ac8f | ||
|  | e6a830d6eb | ||
|  | 1d8280c18f | ||
|  | 79c0f397fe | ||
|  | 0793169870 | ||
|  | 4bb571d857 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -107,6 +107,7 @@ docs/qemu-ga-ref.info* | |||||||
| docs/qemu-qmp-ref.info* | docs/qemu-qmp-ref.info* | ||||||
| /qemu-ga-qapi.texi | /qemu-ga-qapi.texi | ||||||
| /qemu-qapi.texi | /qemu-qapi.texi | ||||||
|  | /version.texi | ||||||
| *.tps | *.tps | ||||||
| .stgit-* | .stgit-* | ||||||
| cscope.* | cscope.* | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								.shippable.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.shippable.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | language: c | ||||||
|  | env: | ||||||
|  |   matrix: | ||||||
|  |     - IMAGE=debian-armhf-cross | ||||||
|  |       TARGET_LIST=arm-softmmu,arm-linux-user | ||||||
|  |     - IMAGE=debian-arm64-cross | ||||||
|  |       TARGET_LIST=aarch64-softmmu,aarch64-linux-user | ||||||
|  | build: | ||||||
|  |   pre_ci: | ||||||
|  |     - make docker-image-${IMAGE} | ||||||
|  |   pre_ci_boot: | ||||||
|  |     image_name: qemu | ||||||
|  |     image_tag: ${IMAGE} | ||||||
|  |     pull: false | ||||||
|  |     options: "-e HOME=/root" | ||||||
|  |   ci: | ||||||
|  |     - unset CC | ||||||
|  |     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} | ||||||
|  |     - make -j2 | ||||||
| @@ -116,3 +116,10 @@ if (a == 1) { | |||||||
| Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read. | Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read. | ||||||
| Besides, good compilers already warn users when '==' is mis-typed as '=', | Besides, good compilers already warn users when '==' is mis-typed as '=', | ||||||
| even when the constant is on the right. | even when the constant is on the right. | ||||||
|  |  | ||||||
|  | 7. Comment style | ||||||
|  |  | ||||||
|  | We use traditional C-style /* */ comments and avoid // comments. | ||||||
|  |  | ||||||
|  | Rationale: The // form is valid in C99, so this is purely a matter of | ||||||
|  | consistency of style. The checkpatch script will warn you about this. | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								MAINTAINERS
									
									
									
									
									
								
							| @@ -561,20 +561,19 @@ F: hw/lm32/milkymist.c | |||||||
| M68K Machines | M68K Machines | ||||||
| ------------- | ------------- | ||||||
| an5206 | an5206 | ||||||
| S: Orphan | M: Thomas Huth <huth@tuxfamily.org> | ||||||
|  | S: Odd Fixes | ||||||
| F: hw/m68k/an5206.c | F: hw/m68k/an5206.c | ||||||
| F: hw/m68k/mcf5206.c | F: hw/m68k/mcf5206.c | ||||||
|  |  | ||||||
| dummy_m68k |  | ||||||
| S: Orphan |  | ||||||
| F: hw/m68k/dummy_m68k.c |  | ||||||
|  |  | ||||||
| mcf5208 | mcf5208 | ||||||
| S: Orphan | M: Thomas Huth <huth@tuxfamily.org> | ||||||
|  | S: Odd Fixes | ||||||
| F: hw/m68k/mcf5208.c | F: hw/m68k/mcf5208.c | ||||||
| F: hw/m68k/mcf_intc.c | F: hw/m68k/mcf_intc.c | ||||||
| F: hw/char/mcf_uart.c | F: hw/char/mcf_uart.c | ||||||
| F: hw/net/mcf_fec.c | F: hw/net/mcf_fec.c | ||||||
|  | F: include/hw/m68k/mcf*.h | ||||||
|  |  | ||||||
| MicroBlaze Machines | MicroBlaze Machines | ||||||
| ------------------- | ------------------- | ||||||
| @@ -1801,9 +1800,14 @@ F: docs/block-replication.txt | |||||||
| Build and test automation | Build and test automation | ||||||
| ------------------------- | ------------------------- | ||||||
| M: Alex Bennée <alex.bennee@linaro.org> | M: Alex Bennée <alex.bennee@linaro.org> | ||||||
|  | M: Fam Zheng <famz@redhat.com> | ||||||
| L: qemu-devel@nongnu.org | L: qemu-devel@nongnu.org | ||||||
| S: Supported | S: Maintained | ||||||
| F: .travis.yml | F: .travis.yml | ||||||
|  | F: .shippable.yml | ||||||
|  | F: tests/docker/ | ||||||
|  | W: https://travis-ci.org/qemu/qemu | ||||||
|  | W: http://patchew.org/QEMU/ | ||||||
|  |  | ||||||
| Documentation | Documentation | ||||||
| ------------- | ------------- | ||||||
| @@ -1812,9 +1816,3 @@ M: Daniel P. Berrange <berrange@redhat.com> | |||||||
| S: Odd Fixes | S: Odd Fixes | ||||||
| F: docs/build-system.txt | F: docs/build-system.txt | ||||||
|  |  | ||||||
| Docker testing |  | ||||||
| -------------- |  | ||||||
| Docker based testing framework and cases |  | ||||||
| M: Fam Zheng <famz@redhat.com> |  | ||||||
| S: Maintained |  | ||||||
| F: tests/docker/ |  | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Makefile
									
									
									
									
									
								
							| @@ -516,7 +516,7 @@ distclean: clean | |||||||
| 	rm -f qemu-doc.vr qemu-doc.txt | 	rm -f qemu-doc.vr qemu-doc.txt | ||||||
| 	rm -f config.log | 	rm -f config.log | ||||||
| 	rm -f linux-headers/asm | 	rm -f linux-headers/asm | ||||||
| 	rm -f qemu-ga-qapi.texi qemu-qapi.texi | 	rm -f qemu-ga-qapi.texi qemu-qapi.texi version.texi | ||||||
| 	rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7 | 	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.txt docs/qemu-ga-ref.txt | ||||||
| 	rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf | 	rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf | ||||||
| @@ -663,21 +663,24 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \ | |||||||
|  |  | ||||||
| # documentation | # documentation | ||||||
| MAKEINFO=makeinfo | MAKEINFO=makeinfo | ||||||
| MAKEINFOFLAGS=--no-split --number-sections -D 'VERSION $(VERSION)' | MAKEINFOFLAGS=--no-split --number-sections | ||||||
| TEXIFLAG=$(if $(V),,--quiet) --command='@set VERSION $(VERSION)' | TEXIFLAG=$(if $(V),,--quiet) | ||||||
|  |  | ||||||
| %.html: %.texi | version.texi: $(SRC_PATH)/VERSION | ||||||
|  | 	$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@") | ||||||
|  |  | ||||||
|  | %.html: %.texi version.texi | ||||||
| 	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ | 	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ | ||||||
| 	--html $< -o $@,"GEN","$@") | 	--html $< -o $@,"GEN","$@") | ||||||
|  |  | ||||||
| %.info: %.texi | %.info: %.texi version.texi | ||||||
| 	$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@") | 	$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@") | ||||||
|  |  | ||||||
| %.txt: %.texi | %.txt: %.texi version.texi | ||||||
| 	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ | 	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ | ||||||
| 	--plaintext $< -o $@,"GEN","$@") | 	--plaintext $< -o $@,"GEN","$@") | ||||||
|  |  | ||||||
| %.pdf: %.texi | %.pdf: %.texi version.texi | ||||||
| 	$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I . $< -o $@,"GEN","$@") | 	$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I . $< -o $@,"GEN","$@") | ||||||
|  |  | ||||||
| qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool | qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool | ||||||
|   | |||||||
| @@ -9,12 +9,8 @@ chardev-obj-y = chardev/ | |||||||
| ####################################################################### | ####################################################################### | ||||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | # block-obj-y is code used by both qemu system emulation and qemu-img | ||||||
|  |  | ||||||
| block-obj-y = async.o thread-pool.o |  | ||||||
| block-obj-y += nbd/ | block-obj-y += nbd/ | ||||||
| block-obj-y += block.o blockjob.o | block-obj-y += block.o blockjob.o | ||||||
| block-obj-y += main-loop.o iohandler.o qemu-timer.o |  | ||||||
| block-obj-$(CONFIG_POSIX) += aio-posix.o |  | ||||||
| block-obj-$(CONFIG_WIN32) += aio-win32.o |  | ||||||
| block-obj-y += block/ | block-obj-y += block/ | ||||||
| block-obj-y += qemu-io-cmds.o | block-obj-y += qemu-io-cmds.o | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | block-obj-$(CONFIG_REPLICATION) += replication.o | ||||||
| @@ -125,6 +121,7 @@ trace-events-subdirs += crypto | |||||||
| trace-events-subdirs += io | trace-events-subdirs += io | ||||||
| trace-events-subdirs += migration | trace-events-subdirs += migration | ||||||
| trace-events-subdirs += block | trace-events-subdirs += block | ||||||
|  | trace-events-subdirs += backends | ||||||
| trace-events-subdirs += hw/block | trace-events-subdirs += hw/block | ||||||
| trace-events-subdirs += hw/block/dataplane | trace-events-subdirs += hw/block/dataplane | ||||||
| trace-events-subdirs += hw/char | trace-events-subdirs += hw/char | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| common-obj-y += rng.o rng-egd.o | common-obj-y += rng.o rng-egd.o | ||||||
| common-obj-$(CONFIG_POSIX) += rng-random.o | common-obj-$(CONFIG_POSIX) += rng-random.o | ||||||
|  |  | ||||||
| common-obj-y += msmouse.o testdev.o | common-obj-y += msmouse.o wctablet.o testdev.o | ||||||
| common-obj-$(CONFIG_BRLAPI) += baum.o | common-obj-$(CONFIG_BRLAPI) += baum.o | ||||||
| baum.o-cflags := $(SDL_CFLAGS) | baum.o-cflags := $(SDL_CFLAGS) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								backends/trace-events
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								backends/trace-events
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | # See docs/tracing.txt for syntax documentation. | ||||||
|  |  | ||||||
|  | # backends/wctablet.c | ||||||
|  | wct_init(void) "" | ||||||
|  | wct_cmd_re(void) "" | ||||||
|  | wct_cmd_st(void) "" | ||||||
|  | wct_cmd_sp(void) "" | ||||||
|  | wct_cmd_ts(int input) "0x%02x" | ||||||
|  | wct_cmd_other(const char *cmd) "%s" | ||||||
|  | wct_speed(int speed) "%d" | ||||||
							
								
								
									
										369
									
								
								backends/wctablet.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								backends/wctablet.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,369 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Wacom Penpartner serial tablet emulation | ||||||
|  |  * | ||||||
|  |  * some protocol details: | ||||||
|  |  *   http://linuxwacom.sourceforge.net/wiki/index.php/Serial_Protocol_IV | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2016 Anatoli Huseu1 | ||||||
|  |  * Copyright (c) 2016,17 Gerd Hoffmann | ||||||
|  |  * | ||||||
|  |  * 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 <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/time.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
|  | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
|  | #include "sysemu/char.h" | ||||||
|  | #include "ui/console.h" | ||||||
|  | #include "ui/input.h" | ||||||
|  | #include "trace.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define WC_OUTPUT_BUF_MAX_LEN 512 | ||||||
|  | #define WC_COMMAND_MAX_LEN 60 | ||||||
|  |  | ||||||
|  | #define WC_L7(n) ((n) & 127) | ||||||
|  | #define WC_M7(n) (((n) >> 7) & 127) | ||||||
|  | #define WC_H2(n) ((n) >> 14) | ||||||
|  |  | ||||||
|  | #define WC_L4(n) ((n) & 15) | ||||||
|  | #define WC_H4(n) (((n) >> 4) & 15) | ||||||
|  |  | ||||||
|  | /* Model string and config string */ | ||||||
|  | #define WC_MODEL_STRING_LENGTH 18 | ||||||
|  | uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,"; | ||||||
|  |  | ||||||
|  | #define WC_CONFIG_STRING_LENGTH 8 | ||||||
|  | uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0"; | ||||||
|  |  | ||||||
|  | #define WC_FULL_CONFIG_STRING_LENGTH 61 | ||||||
|  | uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = { | ||||||
|  |     0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c, | ||||||
|  |     0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30, | ||||||
|  |     0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c, | ||||||
|  |     0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c, | ||||||
|  |     0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a, | ||||||
|  |     0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52, | ||||||
|  |     0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d, | ||||||
|  |     0x0a, 0x45, 0x37, 0x29 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* This structure is used to save private info for Wacom Tablet. */ | ||||||
|  | typedef struct { | ||||||
|  |     Chardev parent; | ||||||
|  |     QemuInputHandlerState *hs; | ||||||
|  |  | ||||||
|  |     /* Query string from serial */ | ||||||
|  |     uint8_t query[100]; | ||||||
|  |     int query_index; | ||||||
|  |  | ||||||
|  |     /* Command to be sent to serial port */ | ||||||
|  |     uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN]; | ||||||
|  |     int outlen; | ||||||
|  |  | ||||||
|  |     int line_speed; | ||||||
|  |     bool send_events; | ||||||
|  |     int axis[INPUT_AXIS__MAX]; | ||||||
|  |     bool btns[INPUT_BUTTON__MAX]; | ||||||
|  |  | ||||||
|  | } TabletChardev; | ||||||
|  |  | ||||||
|  | #define TYPE_CHARDEV_WCTABLET "chardev-wctablet" | ||||||
|  | #define WCTABLET_CHARDEV(obj)                                      \ | ||||||
|  |     OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void wctablet_chr_accept_input(Chardev *chr); | ||||||
|  |  | ||||||
|  | static void wctablet_shift_input(TabletChardev *tablet, int count) | ||||||
|  | { | ||||||
|  |     tablet->query_index -= count; | ||||||
|  |     memmove(tablet->query, tablet->query + count, tablet->query_index); | ||||||
|  |     tablet->query[tablet->query_index] = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count) | ||||||
|  | { | ||||||
|  |     if (tablet->outlen + count > sizeof(tablet->outbuf)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(tablet->outbuf + tablet->outlen, buf, count); | ||||||
|  |     tablet->outlen += count; | ||||||
|  |     wctablet_chr_accept_input(CHARDEV(tablet)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_reset(TabletChardev *tablet) | ||||||
|  | { | ||||||
|  |     /* clear buffers */ | ||||||
|  |     tablet->query_index = 0; | ||||||
|  |     tablet->outlen = 0; | ||||||
|  |     /* reset state */ | ||||||
|  |     tablet->send_events = false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_queue_event(TabletChardev *tablet) | ||||||
|  | { | ||||||
|  |     uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 }; | ||||||
|  |  | ||||||
|  |     if (tablet->line_speed != 9600) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int newX = tablet->axis[INPUT_AXIS_X] * 0.1537; | ||||||
|  |     int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152; | ||||||
|  |  | ||||||
|  |     codes[0] = codes[0] | WC_H2(newX); | ||||||
|  |     codes[1] = codes[1] | WC_M7(newX); | ||||||
|  |     codes[2] = codes[2] | WC_L7(newX); | ||||||
|  |  | ||||||
|  |     codes[3] = codes[3] | WC_H2(nexY); | ||||||
|  |     codes[4] = codes[4] | WC_M7(nexY); | ||||||
|  |     codes[5] = codes[5] | WC_L7(nexY); | ||||||
|  |  | ||||||
|  |     if (tablet->btns[INPUT_BUTTON_LEFT]) { | ||||||
|  |         codes[0] = 0xa0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     wctablet_queue_output(tablet, codes, 7); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_input_event(DeviceState *dev, QemuConsole *src, | ||||||
|  |                                 InputEvent *evt) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = (TabletChardev *)dev; | ||||||
|  |     InputMoveEvent *move; | ||||||
|  |     InputBtnEvent *btn; | ||||||
|  |  | ||||||
|  |     switch (evt->type) { | ||||||
|  |     case INPUT_EVENT_KIND_ABS: | ||||||
|  |         move = evt->u.abs.data; | ||||||
|  |         tablet->axis[move->axis] = move->value; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case INPUT_EVENT_KIND_BTN: | ||||||
|  |         btn = evt->u.btn.data; | ||||||
|  |         tablet->btns[btn->button] = btn->down; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         /* keep gcc happy */ | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_input_sync(DeviceState *dev) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = (TabletChardev *)dev; | ||||||
|  |  | ||||||
|  |     if (tablet->send_events) { | ||||||
|  |         wctablet_queue_event(tablet); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static QemuInputHandler wctablet_handler = { | ||||||
|  |     .name  = "QEMU Wacome Pen Tablet", | ||||||
|  |     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, | ||||||
|  |     .event = wctablet_input_event, | ||||||
|  |     .sync  = wctablet_input_sync, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void wctablet_chr_accept_input(Chardev *chr) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = WCTABLET_CHARDEV(chr); | ||||||
|  |     int len, canWrite; | ||||||
|  |  | ||||||
|  |     canWrite = qemu_chr_be_can_write(chr); | ||||||
|  |     len = canWrite; | ||||||
|  |     if (len > tablet->outlen) { | ||||||
|  |         len = tablet->outlen; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (len) { | ||||||
|  |         qemu_chr_be_write(chr, tablet->outbuf, len); | ||||||
|  |         tablet->outlen -= len; | ||||||
|  |         if (tablet->outlen) { | ||||||
|  |             memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int wctablet_chr_write(struct Chardev *chr, | ||||||
|  |                               const uint8_t *buf, int len) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = WCTABLET_CHARDEV(chr); | ||||||
|  |     unsigned int i, clen; | ||||||
|  |     char *pos; | ||||||
|  |  | ||||||
|  |     if (tablet->line_speed != 9600) { | ||||||
|  |         return len; | ||||||
|  |     } | ||||||
|  |     for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) { | ||||||
|  |         tablet->query[tablet->query_index++] = buf[i]; | ||||||
|  |     } | ||||||
|  |     tablet->query[tablet->query_index] = 0; | ||||||
|  |  | ||||||
|  |     while (tablet->query_index > 0 && (tablet->query[0] == '@'  || | ||||||
|  |                                        tablet->query[0] == '\r' || | ||||||
|  |                                        tablet->query[0] == '\n')) { | ||||||
|  |         wctablet_shift_input(tablet, 1); | ||||||
|  |     } | ||||||
|  |     if (!tablet->query_index) { | ||||||
|  |         return len; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (strncmp((char *)tablet->query, "~#", 2) == 0) { | ||||||
|  |         /* init / detect sequence */ | ||||||
|  |         trace_wct_init(); | ||||||
|  |         wctablet_shift_input(tablet, 2); | ||||||
|  |         wctablet_queue_output(tablet, WC_MODEL_STRING, | ||||||
|  |                               WC_MODEL_STRING_LENGTH); | ||||||
|  |         return len; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* detect line */ | ||||||
|  |     pos = strchr((char *)tablet->query, '\r'); | ||||||
|  |     if (!pos) { | ||||||
|  |         pos = strchr((char *)tablet->query, '\n'); | ||||||
|  |     } | ||||||
|  |     if (!pos) { | ||||||
|  |         return len; | ||||||
|  |     } | ||||||
|  |     clen = pos - (char *)tablet->query; | ||||||
|  |  | ||||||
|  |     /* process commands */ | ||||||
|  |     if (strncmp((char *)tablet->query, "RE", 2) == 0 && | ||||||
|  |         clen == 2) { | ||||||
|  |         trace_wct_cmd_re(); | ||||||
|  |         wctablet_shift_input(tablet, 3); | ||||||
|  |         wctablet_queue_output(tablet, WC_CONFIG_STRING, | ||||||
|  |                               WC_CONFIG_STRING_LENGTH); | ||||||
|  |  | ||||||
|  |     } else if (strncmp((char *)tablet->query, "ST", 2) == 0 && | ||||||
|  |                clen == 2) { | ||||||
|  |         trace_wct_cmd_st(); | ||||||
|  |         wctablet_shift_input(tablet, 3); | ||||||
|  |         tablet->send_events = true; | ||||||
|  |         wctablet_queue_event(tablet); | ||||||
|  |  | ||||||
|  |     } else if (strncmp((char *)tablet->query, "SP", 2) == 0 && | ||||||
|  |                clen == 2) { | ||||||
|  |         trace_wct_cmd_sp(); | ||||||
|  |         wctablet_shift_input(tablet, 3); | ||||||
|  |         tablet->send_events = false; | ||||||
|  |  | ||||||
|  |     } else if (strncmp((char *)tablet->query, "TS", 2) == 0 && | ||||||
|  |                clen == 3) { | ||||||
|  |         unsigned int input = tablet->query[2]; | ||||||
|  |         uint8_t codes[7] = { | ||||||
|  |             0xa3, | ||||||
|  |             ((input & 0x80) == 0) ? 0x7e : 0x7f, | ||||||
|  |             (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7), | ||||||
|  |             0x03, | ||||||
|  |             0x7f, | ||||||
|  |             0x7f, | ||||||
|  |             0x00, | ||||||
|  |         }; | ||||||
|  |         trace_wct_cmd_ts(input); | ||||||
|  |         wctablet_shift_input(tablet, 4); | ||||||
|  |         wctablet_queue_output(tablet, codes, 7); | ||||||
|  |  | ||||||
|  |     } else { | ||||||
|  |         tablet->query[clen] = 0; /* terminate line for printing */ | ||||||
|  |         trace_wct_cmd_other((char *)tablet->query); | ||||||
|  |         wctablet_shift_input(tablet, clen + 1); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = WCTABLET_CHARDEV(chr); | ||||||
|  |     QEMUSerialSetParams *ssp; | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case CHR_IOCTL_SERIAL_SET_PARAMS: | ||||||
|  |         ssp = arg; | ||||||
|  |         if (tablet->line_speed != ssp->speed) { | ||||||
|  |             trace_wct_speed(ssp->speed); | ||||||
|  |             wctablet_reset(tablet); | ||||||
|  |             tablet->line_speed = ssp->speed; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         return -ENOTSUP; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_chr_finalize(Object *obj) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = WCTABLET_CHARDEV(obj); | ||||||
|  |  | ||||||
|  |     qemu_input_handler_unregister(tablet->hs); | ||||||
|  |     g_free(tablet); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_chr_open(Chardev *chr, | ||||||
|  |                               ChardevBackend *backend, | ||||||
|  |                               bool *be_opened, | ||||||
|  |                               Error **errp) | ||||||
|  | { | ||||||
|  |     TabletChardev *tablet = WCTABLET_CHARDEV(chr); | ||||||
|  |  | ||||||
|  |     *be_opened = true; | ||||||
|  |  | ||||||
|  |     /* init state machine */ | ||||||
|  |     memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH); | ||||||
|  |     tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH; | ||||||
|  |     tablet->query_index = 0; | ||||||
|  |  | ||||||
|  |     tablet->hs = qemu_input_handler_register((DeviceState *)tablet, | ||||||
|  |                                              &wctablet_handler); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wctablet_chr_class_init(ObjectClass *oc, void *data) | ||||||
|  | { | ||||||
|  |     ChardevClass *cc = CHARDEV_CLASS(oc); | ||||||
|  |  | ||||||
|  |     cc->open = wctablet_chr_open; | ||||||
|  |     cc->chr_write = wctablet_chr_write; | ||||||
|  |     cc->chr_ioctl = wctablet_chr_ioctl; | ||||||
|  |     cc->chr_accept_input = wctablet_chr_accept_input; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const TypeInfo wctablet_type_info = { | ||||||
|  |     .name = TYPE_CHARDEV_WCTABLET, | ||||||
|  |     .parent = TYPE_CHARDEV, | ||||||
|  |     .instance_size = sizeof(TabletChardev), | ||||||
|  |     .instance_finalize = wctablet_chr_finalize, | ||||||
|  |     .class_init = wctablet_chr_class_init, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void register_types(void) | ||||||
|  | { | ||||||
|  |      type_register_static(&wctablet_type_info); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type_init(register_types); | ||||||
							
								
								
									
										263
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								block.c
									
									
									
									
									
								
							| @@ -588,21 +588,20 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, | |||||||
|     return drv; |     return drv; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int find_image_format(BdrvChild *file, const char *filename, | static int find_image_format(BlockBackend *file, const char *filename, | ||||||
|                              BlockDriver **pdrv, Error **errp) |                              BlockDriver **pdrv, Error **errp) | ||||||
| { | { | ||||||
|     BlockDriverState *bs = file->bs; |  | ||||||
|     BlockDriver *drv; |     BlockDriver *drv; | ||||||
|     uint8_t buf[BLOCK_PROBE_BUF_SIZE]; |     uint8_t buf[BLOCK_PROBE_BUF_SIZE]; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|  |  | ||||||
|     /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ |     /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ | ||||||
|     if (bdrv_is_sg(bs) || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { |     if (blk_is_sg(file) || !blk_is_inserted(file) || blk_getlength(file) == 0) { | ||||||
|         *pdrv = &bdrv_raw; |         *pdrv = &bdrv_raw; | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = bdrv_pread(file, 0, buf, sizeof(buf)); |     ret = blk_pread(file, 0, buf, sizeof(buf)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         error_setg_errno(errp, -ret, "Could not read image for determining its " |         error_setg_errno(errp, -ret, "Could not read image for determining its " | ||||||
|                          "format"); |                          "format"); | ||||||
| @@ -926,6 +925,95 @@ out: | |||||||
|     g_free(gen_node_name); |     g_free(gen_node_name); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, | ||||||
|  |                             const char *node_name, QDict *options, | ||||||
|  |                             int open_flags, Error **errp) | ||||||
|  | { | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     bdrv_assign_node_name(bs, node_name, &local_err); | ||||||
|  |     if (local_err) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bs->drv = drv; | ||||||
|  |     bs->read_only = !(bs->open_flags & BDRV_O_RDWR); | ||||||
|  |     bs->opaque = g_malloc0(drv->instance_size); | ||||||
|  |  | ||||||
|  |     if (drv->bdrv_file_open) { | ||||||
|  |         assert(!drv->bdrv_needs_filename || bs->filename[0]); | ||||||
|  |         ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); | ||||||
|  |     } else if (drv->bdrv_open) { | ||||||
|  |         ret = drv->bdrv_open(bs, options, open_flags, &local_err); | ||||||
|  |     } else { | ||||||
|  |         ret = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ret < 0) { | ||||||
|  |         if (local_err) { | ||||||
|  |             error_propagate(errp, local_err); | ||||||
|  |         } else if (bs->filename[0]) { | ||||||
|  |             error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); | ||||||
|  |         } else { | ||||||
|  |             error_setg_errno(errp, -ret, "Could not open image"); | ||||||
|  |         } | ||||||
|  |         goto free_and_fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = refresh_total_sectors(bs, bs->total_sectors); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         error_setg_errno(errp, -ret, "Could not refresh total sector count"); | ||||||
|  |         goto free_and_fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bdrv_refresh_limits(bs, &local_err); | ||||||
|  |     if (local_err) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         ret = -EINVAL; | ||||||
|  |         goto free_and_fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     assert(bdrv_opt_mem_align(bs) != 0); | ||||||
|  |     assert(bdrv_min_mem_align(bs) != 0); | ||||||
|  |     assert(is_power_of_2(bs->bl.request_alignment)); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  | free_and_fail: | ||||||
|  |     /* FIXME Close bs first if already opened*/ | ||||||
|  |     g_free(bs->opaque); | ||||||
|  |     bs->opaque = NULL; | ||||||
|  |     bs->drv = NULL; | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, | ||||||
|  |                                        int flags, Error **errp) | ||||||
|  | { | ||||||
|  |     BlockDriverState *bs; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     bs = bdrv_new(); | ||||||
|  |     bs->open_flags = flags; | ||||||
|  |     bs->explicit_options = qdict_new(); | ||||||
|  |     bs->options = qdict_new(); | ||||||
|  |     bs->opaque = NULL; | ||||||
|  |  | ||||||
|  |     update_options_from_flags(bs->options, flags); | ||||||
|  |  | ||||||
|  |     ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         QDECREF(bs->explicit_options); | ||||||
|  |         QDECREF(bs->options); | ||||||
|  |         bdrv_unref(bs); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return bs; | ||||||
|  | } | ||||||
|  |  | ||||||
| QemuOptsList bdrv_runtime_opts = { | QemuOptsList bdrv_runtime_opts = { | ||||||
|     .name = "bdrv_common", |     .name = "bdrv_common", | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), |     .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), | ||||||
| @@ -974,7 +1062,7 @@ QemuOptsList bdrv_runtime_opts = { | |||||||
|  * |  * | ||||||
|  * Removes all processed options from *options. |  * Removes all processed options from *options. | ||||||
|  */ |  */ | ||||||
| static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, | static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, | ||||||
|                             QDict *options, Error **errp) |                             QDict *options, Error **errp) | ||||||
| { | { | ||||||
|     int ret, open_flags; |     int ret, open_flags; | ||||||
| @@ -1005,7 +1093,7 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, | |||||||
|     assert(drv != NULL); |     assert(drv != NULL); | ||||||
|  |  | ||||||
|     if (file != NULL) { |     if (file != NULL) { | ||||||
|         filename = file->bs->filename; |         filename = blk_bs(file)->filename; | ||||||
|     } else { |     } else { | ||||||
|         filename = qdict_get_try_str(options, "filename"); |         filename = qdict_get_try_str(options, "filename"); | ||||||
|     } |     } | ||||||
| @@ -1020,14 +1108,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, | |||||||
|     trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, |     trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, | ||||||
|                            drv->format_name); |                            drv->format_name); | ||||||
|  |  | ||||||
|     node_name = qemu_opt_get(opts, "node-name"); |  | ||||||
|     bdrv_assign_node_name(bs, node_name, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         goto fail_opts; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->read_only = !(bs->open_flags & BDRV_O_RDWR); |     bs->read_only = !(bs->open_flags & BDRV_O_RDWR); | ||||||
|  |  | ||||||
|     if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { |     if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { | ||||||
| @@ -1093,62 +1173,19 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, | |||||||
|     } |     } | ||||||
|     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename); |     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename); | ||||||
|  |  | ||||||
|     bs->drv = drv; |  | ||||||
|     bs->opaque = g_malloc0(drv->instance_size); |  | ||||||
|  |  | ||||||
|     /* Open the image, either directly or using a protocol */ |     /* Open the image, either directly or using a protocol */ | ||||||
|     open_flags = bdrv_open_flags(bs, bs->open_flags); |     open_flags = bdrv_open_flags(bs, bs->open_flags); | ||||||
|     if (drv->bdrv_file_open) { |     node_name = qemu_opt_get(opts, "node-name"); | ||||||
|         assert(file == NULL); |  | ||||||
|         assert(!drv->bdrv_needs_filename || filename != NULL); |  | ||||||
|         ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); |  | ||||||
|     } else { |  | ||||||
|         if (file == NULL) { |  | ||||||
|             error_setg(errp, "Can't use '%s' as a block driver for the " |  | ||||||
|                        "protocol level", drv->format_name); |  | ||||||
|             ret = -EINVAL; |  | ||||||
|             goto free_and_fail; |  | ||||||
|         } |  | ||||||
|         bs->file = file; |  | ||||||
|         ret = drv->bdrv_open(bs, options, open_flags, &local_err); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |     assert(!drv->bdrv_file_open || file == NULL); | ||||||
|  |     ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         if (local_err) { |         goto fail_opts; | ||||||
|             error_propagate(errp, local_err); |  | ||||||
|         } else if (bs->filename[0]) { |  | ||||||
|             error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); |  | ||||||
|         } else { |  | ||||||
|             error_setg_errno(errp, -ret, "Could not open image"); |  | ||||||
|     } |     } | ||||||
|         goto free_and_fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = refresh_total_sectors(bs, bs->total_sectors); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         error_setg_errno(errp, -ret, "Could not refresh total sector count"); |  | ||||||
|         goto free_and_fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bdrv_refresh_limits(bs, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         goto free_and_fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     assert(bdrv_opt_mem_align(bs) != 0); |  | ||||||
|     assert(bdrv_min_mem_align(bs) != 0); |  | ||||||
|     assert(is_power_of_2(bs->bl.request_alignment)); |  | ||||||
|  |  | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| free_and_fail: |  | ||||||
|     bs->file = NULL; |  | ||||||
|     g_free(bs->opaque); |  | ||||||
|     bs->opaque = NULL; |  | ||||||
|     bs->drv = NULL; |  | ||||||
| fail_opts: | fail_opts: | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     return ret; |     return ret; | ||||||
| @@ -1169,13 +1206,13 @@ static QDict *parse_json_filename(const char *filename, Error **errp) | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (qobject_type(options_obj) != QTYPE_QDICT) { |     options = qobject_to_qdict(options_obj); | ||||||
|  |     if (!options) { | ||||||
|         qobject_decref(options_obj); |         qobject_decref(options_obj); | ||||||
|         error_setg(errp, "Invalid JSON object given"); |         error_setg(errp, "Invalid JSON object given"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     options = qobject_to_qdict(options_obj); |  | ||||||
|     qdict_flatten(options); |     qdict_flatten(options); | ||||||
|  |  | ||||||
|     return options; |     return options; | ||||||
| @@ -1368,8 +1405,19 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (child->bs->inherits_from == parent) { |     if (child->bs->inherits_from == parent) { | ||||||
|  |         BdrvChild *c; | ||||||
|  |  | ||||||
|  |         /* Remove inherits_from only when the last reference between parent and | ||||||
|  |          * child->bs goes away. */ | ||||||
|  |         QLIST_FOREACH(c, &parent->children, next) { | ||||||
|  |             if (c != child && c->bs == child->bs) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (c == NULL) { | ||||||
|             child->bs->inherits_from = NULL; |             child->bs->inherits_from = NULL; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bdrv_root_unref_child(child); |     bdrv_root_unref_child(child); | ||||||
| } | } | ||||||
| @@ -1543,28 +1591,12 @@ free_exit: | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | static BlockDriverState * | ||||||
|  * Opens a disk image whose options are given as BlockdevRef in another block | bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, | ||||||
|  * device's options. |                    BlockDriverState *parent, const BdrvChildRole *child_role, | ||||||
|  * |  | ||||||
|  * If allow_none is true, no image will be opened if filename is false and no |  | ||||||
|  * BlockdevRef is given. NULL will be returned, but errp remains unset. |  | ||||||
|  * |  | ||||||
|  * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. |  | ||||||
|  * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict |  | ||||||
|  * itself, all options starting with "${bdref_key}." are considered part of the |  | ||||||
|  * BlockdevRef. |  | ||||||
|  * |  | ||||||
|  * The BlockdevRef will be removed from the options QDict. |  | ||||||
|  */ |  | ||||||
| BdrvChild *bdrv_open_child(const char *filename, |  | ||||||
|                            QDict *options, const char *bdref_key, |  | ||||||
|                            BlockDriverState* parent, |  | ||||||
|                            const BdrvChildRole *child_role, |  | ||||||
|                    bool allow_none, Error **errp) |                    bool allow_none, Error **errp) | ||||||
| { | { | ||||||
|     BdrvChild *c = NULL; |     BlockDriverState *bs = NULL; | ||||||
|     BlockDriverState *bs; |  | ||||||
|     QDict *image_options; |     QDict *image_options; | ||||||
|     char *bdref_key_dot; |     char *bdref_key_dot; | ||||||
|     const char *reference; |     const char *reference; | ||||||
| @@ -1591,11 +1623,40 @@ BdrvChild *bdrv_open_child(const char *filename, | |||||||
|         goto done; |         goto done; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     c = bdrv_attach_child(parent, bs, bdref_key, child_role); |  | ||||||
|  |  | ||||||
| done: | done: | ||||||
|     qdict_del(options, bdref_key); |     qdict_del(options, bdref_key); | ||||||
|     return c; |     return bs; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Opens a disk image whose options are given as BlockdevRef in another block | ||||||
|  |  * device's options. | ||||||
|  |  * | ||||||
|  |  * If allow_none is true, no image will be opened if filename is false and no | ||||||
|  |  * BlockdevRef is given. NULL will be returned, but errp remains unset. | ||||||
|  |  * | ||||||
|  |  * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. | ||||||
|  |  * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict | ||||||
|  |  * itself, all options starting with "${bdref_key}." are considered part of the | ||||||
|  |  * BlockdevRef. | ||||||
|  |  * | ||||||
|  |  * The BlockdevRef will be removed from the options QDict. | ||||||
|  |  */ | ||||||
|  | BdrvChild *bdrv_open_child(const char *filename, | ||||||
|  |                            QDict *options, const char *bdref_key, | ||||||
|  |                            BlockDriverState *parent, | ||||||
|  |                            const BdrvChildRole *child_role, | ||||||
|  |                            bool allow_none, Error **errp) | ||||||
|  | { | ||||||
|  |     BlockDriverState *bs; | ||||||
|  |  | ||||||
|  |     bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role, | ||||||
|  |                             allow_none, errp); | ||||||
|  |     if (bs == NULL) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return bdrv_attach_child(parent, bs, bdref_key, child_role); | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||||||
| @@ -1691,7 +1752,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | |||||||
|                                            Error **errp) |                                            Error **errp) | ||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
|     BdrvChild *file = NULL; |     BlockBackend *file = NULL; | ||||||
|     BlockDriverState *bs; |     BlockDriverState *bs; | ||||||
|     BlockDriver *drv = NULL; |     BlockDriver *drv = NULL; | ||||||
|     const char *drvname; |     const char *drvname; | ||||||
| @@ -1789,13 +1850,25 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | |||||||
|         qdict_del(options, "backing"); |         qdict_del(options, "backing"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Open image file without format layer */ |     /* Open image file without format layer. This BlockBackend is only used for | ||||||
|  |      * probing, the block drivers will do their own bdrv_open_child() for the | ||||||
|  |      * same BDS, which is why we put the node name back into options. */ | ||||||
|     if ((flags & BDRV_O_PROTOCOL) == 0) { |     if ((flags & BDRV_O_PROTOCOL) == 0) { | ||||||
|         file = bdrv_open_child(filename, options, "file", bs, |         BlockDriverState *file_bs; | ||||||
|  |  | ||||||
|  |         file_bs = bdrv_open_child_bs(filename, options, "file", bs, | ||||||
|                                      &child_file, true, &local_err); |                                      &child_file, true, &local_err); | ||||||
|         if (local_err) { |         if (local_err) { | ||||||
|             goto fail; |             goto fail; | ||||||
|         } |         } | ||||||
|  |         if (file_bs != NULL) { | ||||||
|  |             file = blk_new(); | ||||||
|  |             blk_insert_bs(file, file_bs); | ||||||
|  |             bdrv_unref(file_bs); | ||||||
|  |  | ||||||
|  |             qdict_put(options, "file", | ||||||
|  |                       qstring_from_str(bdrv_get_node_name(file_bs))); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Image format probing */ |     /* Image format probing */ | ||||||
| @@ -1835,8 +1908,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (file && (bs->file != file)) { |     if (file) { | ||||||
|         bdrv_unref_child(bs, file); |         blk_unref(file); | ||||||
|         file = NULL; |         file = NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1898,8 +1971,9 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | |||||||
|     return bs; |     return bs; | ||||||
|  |  | ||||||
| fail: | fail: | ||||||
|     if (file != NULL) { |     blk_unref(file); | ||||||
|         bdrv_unref_child(bs, file); |     if (bs->file != NULL) { | ||||||
|  |         bdrv_unref_child(bs, bs->file); | ||||||
|     } |     } | ||||||
|     QDECREF(snapshot_options); |     QDECREF(snapshot_options); | ||||||
|     QDECREF(bs->explicit_options); |     QDECREF(bs->explicit_options); | ||||||
| @@ -2626,8 +2700,9 @@ exit: | |||||||
| /** | /** | ||||||
|  * Truncate file to 'offset' bytes (needed only for file protocols) |  * Truncate file to 'offset' bytes (needed only for file protocols) | ||||||
|  */ |  */ | ||||||
| int bdrv_truncate(BlockDriverState *bs, int64_t offset) | int bdrv_truncate(BdrvChild *child, int64_t offset) | ||||||
| { | { | ||||||
|  |     BlockDriverState *bs = child->bs; | ||||||
|     BlockDriver *drv = bs->drv; |     BlockDriver *drv = bs->drv; | ||||||
|     int ret; |     int ret; | ||||||
|     if (!drv) |     if (!drv) | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | |||||||
|         retry = false; |         retry = false; | ||||||
|         QLIST_FOREACH(req, &job->inflight_reqs, list) { |         QLIST_FOREACH(req, &job->inflight_reqs, list) { | ||||||
|             if (end > req->start && start < req->end) { |             if (end > req->start && start < req->end) { | ||||||
|                 qemu_co_queue_wait(&req->wait_queue); |                 qemu_co_queue_wait(&req->wait_queue, NULL); | ||||||
|                 retry = true; |                 retry = true; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -405,12 +405,6 @@ out: | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void error_callback_bh(void *opaque) |  | ||||||
| { |  | ||||||
|     Coroutine *co = opaque; |  | ||||||
|     qemu_coroutine_enter(co); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int inject_error(BlockDriverState *bs, BlkdebugRule *rule) | static int inject_error(BlockDriverState *bs, BlkdebugRule *rule) | ||||||
| { | { | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |     BDRVBlkdebugState *s = bs->opaque; | ||||||
| @@ -423,8 +417,7 @@ static int inject_error(BlockDriverState *bs, BlkdebugRule *rule) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!immediately) { |     if (!immediately) { | ||||||
|         aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), error_callback_bh, |         aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self()); | ||||||
|                                 qemu_coroutine_self()); |  | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -670,7 +663,7 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) | |||||||
|  |  | ||||||
| static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) | static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) | ||||||
| { | { | ||||||
|     return bdrv_truncate(bs->file->bs, offset); |     return bdrv_truncate(bs->file, offset); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ static int64_t blkreplay_getlength(BlockDriverState *bs) | |||||||
| static void blkreplay_bh_cb(void *opaque) | static void blkreplay_bh_cb(void *opaque) | ||||||
| { | { | ||||||
|     Request *req = opaque; |     Request *req = opaque; | ||||||
|     qemu_coroutine_enter(req->co); |     aio_co_wake(req->co); | ||||||
|     qemu_bh_delete(req->bh); |     qemu_bh_delete(req->bh); | ||||||
|     g_free(req); |     g_free(req); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -880,7 +880,6 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | |||||||
| { | { | ||||||
|     QEMUIOVector qiov; |     QEMUIOVector qiov; | ||||||
|     struct iovec iov; |     struct iovec iov; | ||||||
|     Coroutine *co; |  | ||||||
|     BlkRwCo rwco; |     BlkRwCo rwco; | ||||||
|  |  | ||||||
|     iov = (struct iovec) { |     iov = (struct iovec) { | ||||||
| @@ -897,9 +896,14 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | |||||||
|         .ret    = NOT_DONE, |         .ret    = NOT_DONE, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     co = qemu_coroutine_create(co_entry, &rwco); |     if (qemu_in_coroutine()) { | ||||||
|  |         /* Fast-path if already in coroutine context */ | ||||||
|  |         co_entry(&rwco); | ||||||
|  |     } else { | ||||||
|  |         Coroutine *co = qemu_coroutine_create(co_entry, &rwco); | ||||||
|         qemu_coroutine_enter(co); |         qemu_coroutine_enter(co); | ||||||
|         BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE); |         BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return rwco.ret; |     return rwco.ret; | ||||||
| } | } | ||||||
| @@ -979,7 +983,6 @@ static void blk_aio_complete(BlkAioEmAIOCB *acb) | |||||||
| static void blk_aio_complete_bh(void *opaque) | static void blk_aio_complete_bh(void *opaque) | ||||||
| { | { | ||||||
|     BlkAioEmAIOCB *acb = opaque; |     BlkAioEmAIOCB *acb = opaque; | ||||||
|  |  | ||||||
|     assert(acb->has_returned); |     assert(acb->has_returned); | ||||||
|     blk_aio_complete(acb); |     blk_aio_complete(acb); | ||||||
| } | } | ||||||
| @@ -1602,7 +1605,7 @@ int blk_truncate(BlockBackend *blk, int64_t offset) | |||||||
|         return -ENOMEDIUM; |         return -ENOMEDIUM; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bdrv_truncate(blk_bs(blk), offset); |     return bdrv_truncate(blk->root, offset); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blk_pdiscard_entry(void *opaque) | static void blk_pdiscard_entry(void *opaque) | ||||||
|   | |||||||
| @@ -104,6 +104,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     struct bochs_header bochs; |     struct bochs_header bochs; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bs->read_only = true; /* no write support yet */ |     bs->read_only = true; /* no write support yet */ | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); |     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); | ||||||
|   | |||||||
| @@ -66,6 +66,12 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     uint32_t offsets_size, max_compressed_block_size = 1, i; |     uint32_t offsets_size, max_compressed_block_size = 1, i; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bs->read_only = true; |     bs->read_only = true; | ||||||
|  |  | ||||||
|     /* read header */ |     /* read header */ | ||||||
|   | |||||||
| @@ -300,6 +300,12 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|     QCryptoBlockOpenOptions *open_opts = NULL; |     QCryptoBlockOpenOptions *open_opts = NULL; | ||||||
|     unsigned int cflags = 0; |     unsigned int cflags = 0; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort); |     opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort); | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
| @@ -383,7 +389,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset) | |||||||
|  |  | ||||||
|     offset += payload_offset; |     offset += payload_offset; | ||||||
|  |  | ||||||
|     return bdrv_truncate(bs->file->bs, offset); |     return bdrv_truncate(bs->file, offset); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void block_crypto_close(BlockDriverState *bs) | static void block_crypto_close(BlockDriverState *bs) | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -135,6 +135,7 @@ typedef struct BDRVCURLState { | |||||||
|     char *cookie; |     char *cookie; | ||||||
|     bool accept_range; |     bool accept_range; | ||||||
|     AioContext *aio_context; |     AioContext *aio_context; | ||||||
|  |     QemuMutex mutex; | ||||||
|     char *username; |     char *username; | ||||||
|     char *password; |     char *password; | ||||||
|     char *proxyusername; |     char *proxyusername; | ||||||
| @@ -333,6 +334,7 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, | |||||||
|     return FIND_RET_NONE; |     return FIND_RET_NONE; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with s->mutex held.  */ | ||||||
| static void curl_multi_check_completion(BDRVCURLState *s) | static void curl_multi_check_completion(BDRVCURLState *s) | ||||||
| { | { | ||||||
|     int msgs_in_queue; |     int msgs_in_queue; | ||||||
| @@ -374,7 +376,9 @@ static void curl_multi_check_completion(BDRVCURLState *s) | |||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  |                     qemu_mutex_unlock(&s->mutex); | ||||||
|                     acb->common.cb(acb->common.opaque, -EPROTO); |                     acb->common.cb(acb->common.opaque, -EPROTO); | ||||||
|  |                     qemu_mutex_lock(&s->mutex); | ||||||
|                     qemu_aio_unref(acb); |                     qemu_aio_unref(acb); | ||||||
|                     state->acb[i] = NULL; |                     state->acb[i] = NULL; | ||||||
|                 } |                 } | ||||||
| @@ -386,9 +390,9 @@ static void curl_multi_check_completion(BDRVCURLState *s) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void curl_multi_do(void *arg) | /* Called with s->mutex held.  */ | ||||||
|  | static void curl_multi_do_locked(CURLState *s) | ||||||
| { | { | ||||||
|     CURLState *s = (CURLState *)arg; |  | ||||||
|     CURLSocket *socket, *next_socket; |     CURLSocket *socket, *next_socket; | ||||||
|     int running; |     int running; | ||||||
|     int r; |     int r; | ||||||
| @@ -406,12 +410,23 @@ static void curl_multi_do(void *arg) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void curl_multi_do(void *arg) | ||||||
|  | { | ||||||
|  |     CURLState *s = (CURLState *)arg; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&s->s->mutex); | ||||||
|  |     curl_multi_do_locked(s); | ||||||
|  |     qemu_mutex_unlock(&s->s->mutex); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void curl_multi_read(void *arg) | static void curl_multi_read(void *arg) | ||||||
| { | { | ||||||
|     CURLState *s = (CURLState *)arg; |     CURLState *s = (CURLState *)arg; | ||||||
|  |  | ||||||
|     curl_multi_do(arg); |     qemu_mutex_lock(&s->s->mutex); | ||||||
|  |     curl_multi_do_locked(s); | ||||||
|     curl_multi_check_completion(s->s); |     curl_multi_check_completion(s->s); | ||||||
|  |     qemu_mutex_unlock(&s->s->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void curl_multi_timeout_do(void *arg) | static void curl_multi_timeout_do(void *arg) | ||||||
| @@ -424,9 +439,11 @@ static void curl_multi_timeout_do(void *arg) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&s->mutex); | ||||||
|     curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); |     curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); | ||||||
|  |  | ||||||
|     curl_multi_check_completion(s); |     curl_multi_check_completion(s); | ||||||
|  |     qemu_mutex_unlock(&s->mutex); | ||||||
| #else | #else | ||||||
|     abort(); |     abort(); | ||||||
| #endif | #endif | ||||||
| @@ -759,6 +776,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     curl_easy_cleanup(state->curl); |     curl_easy_cleanup(state->curl); | ||||||
|     state->curl = NULL; |     state->curl = NULL; | ||||||
|  |  | ||||||
|  |     qemu_mutex_init(&s->mutex); | ||||||
|     curl_attach_aio_context(bs, bdrv_get_aio_context(bs)); |     curl_attach_aio_context(bs, bdrv_get_aio_context(bs)); | ||||||
|  |  | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
| @@ -784,13 +802,17 @@ static void curl_readv_bh_cb(void *p) | |||||||
| { | { | ||||||
|     CURLState *state; |     CURLState *state; | ||||||
|     int running; |     int running; | ||||||
|  |     int ret = -EINPROGRESS; | ||||||
|  |  | ||||||
|     CURLAIOCB *acb = p; |     CURLAIOCB *acb = p; | ||||||
|     BDRVCURLState *s = acb->common.bs->opaque; |     BlockDriverState *bs = acb->common.bs; | ||||||
|  |     BDRVCURLState *s = bs->opaque; | ||||||
|  |  | ||||||
|     size_t start = acb->sector_num * BDRV_SECTOR_SIZE; |     size_t start = acb->sector_num * BDRV_SECTOR_SIZE; | ||||||
|     size_t end; |     size_t end; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&s->mutex); | ||||||
|  |  | ||||||
|     // In case we have the requested data already (e.g. read-ahead), |     // In case we have the requested data already (e.g. read-ahead), | ||||||
|     // we can just call the callback and be done. |     // we can just call the callback and be done. | ||||||
|     switch (curl_find_buf(s, start, acb->nb_sectors * BDRV_SECTOR_SIZE, acb)) { |     switch (curl_find_buf(s, start, acb->nb_sectors * BDRV_SECTOR_SIZE, acb)) { | ||||||
| @@ -798,7 +820,7 @@ static void curl_readv_bh_cb(void *p) | |||||||
|             qemu_aio_unref(acb); |             qemu_aio_unref(acb); | ||||||
|             // fall through |             // fall through | ||||||
|         case FIND_RET_WAIT: |         case FIND_RET_WAIT: | ||||||
|             return; |             goto out; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
| @@ -806,9 +828,8 @@ static void curl_readv_bh_cb(void *p) | |||||||
|     // No cache found, so let's start a new request |     // No cache found, so let's start a new request | ||||||
|     state = curl_init_state(acb->common.bs, s); |     state = curl_init_state(acb->common.bs, s); | ||||||
|     if (!state) { |     if (!state) { | ||||||
|         acb->common.cb(acb->common.opaque, -EIO); |         ret = -EIO; | ||||||
|         qemu_aio_unref(acb); |         goto out; | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     acb->start = 0; |     acb->start = 0; | ||||||
| @@ -822,9 +843,8 @@ static void curl_readv_bh_cb(void *p) | |||||||
|     state->orig_buf = g_try_malloc(state->buf_len); |     state->orig_buf = g_try_malloc(state->buf_len); | ||||||
|     if (state->buf_len && state->orig_buf == NULL) { |     if (state->buf_len && state->orig_buf == NULL) { | ||||||
|         curl_clean_state(state); |         curl_clean_state(state); | ||||||
|         acb->common.cb(acb->common.opaque, -ENOMEM); |         ret = -ENOMEM; | ||||||
|         qemu_aio_unref(acb); |         goto out; | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|     state->acb[0] = acb; |     state->acb[0] = acb; | ||||||
|  |  | ||||||
| @@ -837,6 +857,13 @@ static void curl_readv_bh_cb(void *p) | |||||||
|  |  | ||||||
|     /* Tell curl it needs to kick things off */ |     /* Tell curl it needs to kick things off */ | ||||||
|     curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); |     curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); | ||||||
|  |  | ||||||
|  | out: | ||||||
|  |     qemu_mutex_unlock(&s->mutex); | ||||||
|  |     if (ret != -EINPROGRESS) { | ||||||
|  |         acb->common.cb(acb->common.opaque, ret); | ||||||
|  |         qemu_aio_unref(acb); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *curl_aio_readv(BlockDriverState *bs, | static BlockAIOCB *curl_aio_readv(BlockDriverState *bs, | ||||||
| @@ -861,6 +888,7 @@ static void curl_close(BlockDriverState *bs) | |||||||
|  |  | ||||||
|     DPRINTF("CURL: Close\n"); |     DPRINTF("CURL: Close\n"); | ||||||
|     curl_detach_aio_context(bs); |     curl_detach_aio_context(bs); | ||||||
|  |     qemu_mutex_destroy(&s->mutex); | ||||||
|  |  | ||||||
|     g_free(s->cookie); |     g_free(s->cookie); | ||||||
|     g_free(s->url); |     g_free(s->url); | ||||||
|   | |||||||
| @@ -413,6 +413,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     block_module_load_one("dmg-bz2"); |     block_module_load_one("dmg-bz2"); | ||||||
|     bs->read_only = true; |     bs->read_only = true; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1591,18 +1591,17 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | |||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ftruncate(fd, total_size) != 0) { |  | ||||||
|         result = -errno; |  | ||||||
|         error_setg_errno(errp, -result, "Could not resize file"); |  | ||||||
|         goto out_close; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     switch (prealloc) { |     switch (prealloc) { | ||||||
| #ifdef CONFIG_POSIX_FALLOCATE | #ifdef CONFIG_POSIX_FALLOCATE | ||||||
|     case PREALLOC_MODE_FALLOC: |     case PREALLOC_MODE_FALLOC: | ||||||
|         /* posix_fallocate() doesn't set errno. */ |         /* | ||||||
|  |          * Truncating before posix_fallocate() makes it about twice slower on | ||||||
|  |          * file systems that do not support fallocate(), trying to check if a | ||||||
|  |          * block is allocated before allocating it, so don't do that here. | ||||||
|  |          */ | ||||||
|         result = -posix_fallocate(fd, 0, total_size); |         result = -posix_fallocate(fd, 0, total_size); | ||||||
|         if (result != 0) { |         if (result != 0) { | ||||||
|  |             /* posix_fallocate() doesn't set errno. */ | ||||||
|             error_setg_errno(errp, -result, |             error_setg_errno(errp, -result, | ||||||
|                              "Could not preallocate data for the new file"); |                              "Could not preallocate data for the new file"); | ||||||
|         } |         } | ||||||
| @@ -1610,6 +1609,17 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | |||||||
| #endif | #endif | ||||||
|     case PREALLOC_MODE_FULL: |     case PREALLOC_MODE_FULL: | ||||||
|     { |     { | ||||||
|  |         /* | ||||||
|  |          * Knowing the final size from the beginning could allow the file | ||||||
|  |          * system driver to do less allocations and possibly avoid | ||||||
|  |          * fragmentation of the file. | ||||||
|  |          */ | ||||||
|  |         if (ftruncate(fd, total_size) != 0) { | ||||||
|  |             result = -errno; | ||||||
|  |             error_setg_errno(errp, -result, "Could not resize file"); | ||||||
|  |             goto out_close; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         int64_t num = 0, left = total_size; |         int64_t num = 0, left = total_size; | ||||||
|         buf = g_malloc0(65536); |         buf = g_malloc0(65536); | ||||||
|  |  | ||||||
| @@ -1636,6 +1646,10 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PREALLOC_MODE_OFF: |     case PREALLOC_MODE_OFF: | ||||||
|  |         if (ftruncate(fd, total_size) != 0) { | ||||||
|  |             result = -errno; | ||||||
|  |             error_setg_errno(errp, -result, "Could not resize file"); | ||||||
|  |         } | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         result = -EINVAL; |         result = -EINVAL; | ||||||
|   | |||||||
| @@ -698,13 +698,6 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, | |||||||
|     return qemu_gluster_glfs_init(gconf, errp); |     return qemu_gluster_glfs_init(gconf, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qemu_gluster_complete_aio(void *opaque) |  | ||||||
| { |  | ||||||
|     GlusterAIOCB *acb = (GlusterAIOCB *)opaque; |  | ||||||
|  |  | ||||||
|     qemu_coroutine_enter(acb->coroutine); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * AIO callback routine called from GlusterFS thread. |  * AIO callback routine called from GlusterFS thread. | ||||||
|  */ |  */ | ||||||
| @@ -720,7 +713,7 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) | |||||||
|         acb->ret = -EIO; /* Partial read/write - fail it */ |         acb->ret = -EIO; /* Partial read/write - fail it */ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     aio_bh_schedule_oneshot(acb->aio_context, qemu_gluster_complete_aio, acb); |     aio_co_schedule(acb->aio_context, acb->coroutine); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags) | static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags) | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								block/io.c
									
									
									
									
									
								
							| @@ -189,7 +189,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) | |||||||
|     bdrv_dec_in_flight(bs); |     bdrv_dec_in_flight(bs); | ||||||
|     bdrv_drained_begin(bs); |     bdrv_drained_begin(bs); | ||||||
|     data->done = true; |     data->done = true; | ||||||
|     qemu_coroutine_enter(co); |     aio_co_wake(co); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) | static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) | ||||||
| @@ -539,7 +539,7 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self) | |||||||
|                  * (instead of producing a deadlock in the former case). */ |                  * (instead of producing a deadlock in the former case). */ | ||||||
|                 if (!req->waiting_for) { |                 if (!req->waiting_for) { | ||||||
|                     self->waiting_for = req; |                     self->waiting_for = req; | ||||||
|                     qemu_co_queue_wait(&req->wait_queue); |                     qemu_co_queue_wait(&req->wait_queue, NULL); | ||||||
|                     self->waiting_for = NULL; |                     self->waiting_for = NULL; | ||||||
|                     retry = true; |                     retry = true; | ||||||
|                     waited = true; |                     waited = true; | ||||||
| @@ -813,7 +813,7 @@ static void bdrv_co_io_em_complete(void *opaque, int ret) | |||||||
|     CoroutineIOCompletion *co = opaque; |     CoroutineIOCompletion *co = opaque; | ||||||
|  |  | ||||||
|     co->ret = ret; |     co->ret = ret; | ||||||
|     qemu_coroutine_enter(co->coroutine); |     aio_co_wake(co->coroutine); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, | static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, | ||||||
| @@ -2080,6 +2080,11 @@ void bdrv_aio_cancel(BlockAIOCB *acb) | |||||||
|         if (acb->aiocb_info->get_aio_context) { |         if (acb->aiocb_info->get_aio_context) { | ||||||
|             aio_poll(acb->aiocb_info->get_aio_context(acb), true); |             aio_poll(acb->aiocb_info->get_aio_context(acb), true); | ||||||
|         } else if (acb->bs) { |         } else if (acb->bs) { | ||||||
|  |             /* qemu_aio_ref and qemu_aio_unref are not thread-safe, so | ||||||
|  |              * assert that we're not using an I/O thread.  Thread-safe | ||||||
|  |              * code should use bdrv_aio_cancel_async exclusively. | ||||||
|  |              */ | ||||||
|  |             assert(bdrv_get_aio_context(acb->bs) == qemu_get_aio_context()); | ||||||
|             aio_poll(bdrv_get_aio_context(acb->bs), true); |             aio_poll(bdrv_get_aio_context(acb->bs), true); | ||||||
|         } else { |         } else { | ||||||
|             abort(); |             abort(); | ||||||
| @@ -2239,35 +2244,6 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, | |||||||
|     return &acb->common; |     return &acb->common; | ||||||
| } | } | ||||||
|  |  | ||||||
| void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, |  | ||||||
|                    BlockCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BlockAIOCB *acb; |  | ||||||
|  |  | ||||||
|     acb = g_malloc(aiocb_info->aiocb_size); |  | ||||||
|     acb->aiocb_info = aiocb_info; |  | ||||||
|     acb->bs = bs; |  | ||||||
|     acb->cb = cb; |  | ||||||
|     acb->opaque = opaque; |  | ||||||
|     acb->refcnt = 1; |  | ||||||
|     return acb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qemu_aio_ref(void *p) |  | ||||||
| { |  | ||||||
|     BlockAIOCB *acb = p; |  | ||||||
|     acb->refcnt++; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qemu_aio_unref(void *p) |  | ||||||
| { |  | ||||||
|     BlockAIOCB *acb = p; |  | ||||||
|     assert(acb->refcnt > 0); |  | ||||||
|     if (--acb->refcnt == 0) { |  | ||||||
|         g_free(acb); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /**************************************************************/ | /**************************************************************/ | ||||||
| /* Coroutine block device emulation */ | /* Coroutine block device emulation */ | ||||||
|  |  | ||||||
| @@ -2299,7 +2275,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) | |||||||
|  |  | ||||||
|     /* Wait until any previous flushes are completed */ |     /* Wait until any previous flushes are completed */ | ||||||
|     while (bs->active_flush_req) { |     while (bs->active_flush_req) { | ||||||
|         qemu_co_queue_wait(&bs->flush_queue); |         qemu_co_queue_wait(&bs->flush_queue, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->active_flush_req = true; |     bs->active_flush_req = true; | ||||||
|   | |||||||
							
								
								
									
										401
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										401
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -58,6 +58,7 @@ typedef struct IscsiLun { | |||||||
|     int events; |     int events; | ||||||
|     QEMUTimer *nop_timer; |     QEMUTimer *nop_timer; | ||||||
|     QEMUTimer *event_timer; |     QEMUTimer *event_timer; | ||||||
|  |     QemuMutex mutex; | ||||||
|     struct scsi_inquiry_logical_block_provisioning lbp; |     struct scsi_inquiry_logical_block_provisioning lbp; | ||||||
|     struct scsi_inquiry_block_limits bl; |     struct scsi_inquiry_block_limits bl; | ||||||
|     unsigned char *zeroblock; |     unsigned char *zeroblock; | ||||||
| @@ -165,8 +166,9 @@ iscsi_schedule_bh(IscsiAIOCB *acb) | |||||||
| static void iscsi_co_generic_bh_cb(void *opaque) | static void iscsi_co_generic_bh_cb(void *opaque) | ||||||
| { | { | ||||||
|     struct IscsiTask *iTask = opaque; |     struct IscsiTask *iTask = opaque; | ||||||
|  |  | ||||||
|     iTask->complete = 1; |     iTask->complete = 1; | ||||||
|     qemu_coroutine_enter(iTask->co); |     aio_co_wake(iTask->co); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void iscsi_retry_timer_expired(void *opaque) | static void iscsi_retry_timer_expired(void *opaque) | ||||||
| @@ -174,7 +176,7 @@ static void iscsi_retry_timer_expired(void *opaque) | |||||||
|     struct IscsiTask *iTask = opaque; |     struct IscsiTask *iTask = opaque; | ||||||
|     iTask->complete = 1; |     iTask->complete = 1; | ||||||
|     if (iTask->co) { |     if (iTask->co) { | ||||||
|         qemu_coroutine_enter(iTask->co); |         aio_co_wake(iTask->co); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -251,6 +253,7 @@ static int iscsi_translate_sense(struct scsi_sense *sense) | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called (via iscsi_service) with QemuMutex held.  */ | ||||||
| static void | static void | ||||||
| iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, | iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, | ||||||
|                         void *command_data, void *opaque) |                         void *command_data, void *opaque) | ||||||
| @@ -351,6 +354,7 @@ static const AIOCBInfo iscsi_aiocb_info = { | |||||||
| static void iscsi_process_read(void *arg); | static void iscsi_process_read(void *arg); | ||||||
| static void iscsi_process_write(void *arg); | static void iscsi_process_write(void *arg); | ||||||
|  |  | ||||||
|  | /* Called with QemuMutex held.  */ | ||||||
| static void | static void | ||||||
| iscsi_set_events(IscsiLun *iscsilun) | iscsi_set_events(IscsiLun *iscsilun) | ||||||
| { | { | ||||||
| @@ -394,8 +398,10 @@ iscsi_process_read(void *arg) | |||||||
|     IscsiLun *iscsilun = arg; |     IscsiLun *iscsilun = arg; | ||||||
|     struct iscsi_context *iscsi = iscsilun->iscsi; |     struct iscsi_context *iscsi = iscsilun->iscsi; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     iscsi_service(iscsi, POLLIN); |     iscsi_service(iscsi, POLLIN); | ||||||
|     iscsi_set_events(iscsilun); |     iscsi_set_events(iscsilun); | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -404,8 +410,10 @@ iscsi_process_write(void *arg) | |||||||
|     IscsiLun *iscsilun = arg; |     IscsiLun *iscsilun = arg; | ||||||
|     struct iscsi_context *iscsi = iscsilun->iscsi; |     struct iscsi_context *iscsi = iscsilun->iscsi; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     iscsi_service(iscsi, POLLOUT); |     iscsi_service(iscsi, POLLOUT); | ||||||
|     iscsi_set_events(iscsilun); |     iscsi_set_events(iscsilun); | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun) | static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun) | ||||||
| @@ -584,6 +592,7 @@ iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | |||||||
|     uint64_t lba; |     uint64_t lba; | ||||||
|     uint32_t num_sectors; |     uint32_t num_sectors; | ||||||
|     bool fua = flags & BDRV_REQ_FUA; |     bool fua = flags & BDRV_REQ_FUA; | ||||||
|  |     int r = 0; | ||||||
|  |  | ||||||
|     if (fua) { |     if (fua) { | ||||||
|         assert(iscsilun->dpofua); |         assert(iscsilun->dpofua); | ||||||
| @@ -599,6 +608,7 @@ iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | |||||||
|     lba = sector_qemu2lun(sector_num, iscsilun); |     lba = sector_qemu2lun(sector_num, iscsilun); | ||||||
|     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); |     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); | ||||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); |     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
| retry: | retry: | ||||||
|     if (iscsilun->use_16_for_rw) { |     if (iscsilun->use_16_for_rw) { | ||||||
| #if LIBISCSI_API_VERSION >= (20160603) | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
| @@ -635,7 +645,9 @@ retry: | |||||||
| #endif | #endif | ||||||
|     while (!iTask.complete) { |     while (!iTask.complete) { | ||||||
|         iscsi_set_events(iscsilun); |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
| @@ -650,12 +662,15 @@ retry: | |||||||
|  |  | ||||||
|     if (iTask.status != SCSI_STATUS_GOOD) { |     if (iTask.status != SCSI_STATUS_GOOD) { | ||||||
|         iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors); |         iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors); | ||||||
|         return iTask.err_code; |         r = iTask.err_code; | ||||||
|  |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_allocmap_set_allocated(iscsilun, sector_num, nb_sectors); |     iscsi_allocmap_set_allocated(iscsilun, sector_num, nb_sectors); | ||||||
|  |  | ||||||
|     return 0; | out_unlock: | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |     return r; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -688,18 +703,21 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
| retry: | retry: | ||||||
|     if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, |     if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, | ||||||
|                                   sector_qemu2lun(sector_num, iscsilun), |                                   sector_qemu2lun(sector_num, iscsilun), | ||||||
|                                   8 + 16, iscsi_co_generic_cb, |                                   8 + 16, iscsi_co_generic_cb, | ||||||
|                                   &iTask) == NULL) { |                                   &iTask) == NULL) { | ||||||
|         ret = -ENOMEM; |         ret = -ENOMEM; | ||||||
|         goto out; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (!iTask.complete) { |     while (!iTask.complete) { | ||||||
|         iscsi_set_events(iscsilun); |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.do_retry) { |     if (iTask.do_retry) { | ||||||
| @@ -716,20 +734,20 @@ retry: | |||||||
|          * because the device is busy or the cmd is not |          * because the device is busy or the cmd is not | ||||||
|          * supported) we pretend all blocks are allocated |          * supported) we pretend all blocks are allocated | ||||||
|          * for backwards compatibility */ |          * for backwards compatibility */ | ||||||
|         goto out; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lbas = scsi_datain_unmarshall(iTask.task); |     lbas = scsi_datain_unmarshall(iTask.task); | ||||||
|     if (lbas == NULL) { |     if (lbas == NULL) { | ||||||
|         ret = -EIO; |         ret = -EIO; | ||||||
|         goto out; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lbasd = &lbas->descriptors[0]; |     lbasd = &lbas->descriptors[0]; | ||||||
|  |  | ||||||
|     if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { |     if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { | ||||||
|         ret = -EIO; |         ret = -EIO; | ||||||
|         goto out; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); |     *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); | ||||||
| @@ -751,6 +769,8 @@ retry: | |||||||
|     if (*pnum > nb_sectors) { |     if (*pnum > nb_sectors) { | ||||||
|         *pnum = nb_sectors; |         *pnum = nb_sectors; | ||||||
|     } |     } | ||||||
|  | out_unlock: | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
| out: | out: | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -813,6 +833,7 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | |||||||
|     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); |     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); | ||||||
|  |  | ||||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); |     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
| retry: | retry: | ||||||
|     if (iscsilun->use_16_for_rw) { |     if (iscsilun->use_16_for_rw) { | ||||||
| #if LIBISCSI_API_VERSION >= (20160603) | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
| @@ -850,7 +871,9 @@ retry: | |||||||
| #endif | #endif | ||||||
|     while (!iTask.complete) { |     while (!iTask.complete) { | ||||||
|         iscsi_set_events(iscsilun); |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
| @@ -862,6 +885,7 @@ retry: | |||||||
|         iTask.complete = 0; |         iTask.complete = 0; | ||||||
|         goto retry; |         goto retry; | ||||||
|     } |     } | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |  | ||||||
|     if (iTask.status != SCSI_STATUS_GOOD) { |     if (iTask.status != SCSI_STATUS_GOOD) { | ||||||
|         return iTask.err_code; |         return iTask.err_code; | ||||||
| @@ -876,6 +900,7 @@ static int coroutine_fn iscsi_co_flush(BlockDriverState *bs) | |||||||
|     struct IscsiTask iTask; |     struct IscsiTask iTask; | ||||||
|  |  | ||||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); |     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
| retry: | retry: | ||||||
|     if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0, |     if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0, | ||||||
|                                       0, iscsi_co_generic_cb, &iTask) == NULL) { |                                       0, iscsi_co_generic_cb, &iTask) == NULL) { | ||||||
| @@ -884,7 +909,9 @@ retry: | |||||||
|  |  | ||||||
|     while (!iTask.complete) { |     while (!iTask.complete) { | ||||||
|         iscsi_set_events(iscsilun); |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
| @@ -896,6 +923,7 @@ retry: | |||||||
|         iTask.complete = 0; |         iTask.complete = 0; | ||||||
|         goto retry; |         goto retry; | ||||||
|     } |     } | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |  | ||||||
|     if (iTask.status != SCSI_STATUS_GOOD) { |     if (iTask.status != SCSI_STATUS_GOOD) { | ||||||
|         return iTask.err_code; |         return iTask.err_code; | ||||||
| @@ -905,6 +933,7 @@ retry: | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
|  | /* Called (via iscsi_service) with QemuMutex held.  */ | ||||||
| static void | static void | ||||||
| iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | ||||||
|                      void *command_data, void *opaque) |                      void *command_data, void *opaque) | ||||||
| @@ -1029,6 +1058,7 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | |||||||
|     acb->task->expxferlen = acb->ioh->dxfer_len; |     acb->task->expxferlen = acb->ioh->dxfer_len; | ||||||
|  |  | ||||||
|     data.size = 0; |     data.size = 0; | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     if (acb->task->xfer_dir == SCSI_XFER_WRITE) { |     if (acb->task->xfer_dir == SCSI_XFER_WRITE) { | ||||||
|         if (acb->ioh->iovec_count == 0) { |         if (acb->ioh->iovec_count == 0) { | ||||||
|             data.data = acb->ioh->dxferp; |             data.data = acb->ioh->dxferp; | ||||||
| @@ -1044,6 +1074,7 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | |||||||
|                                  iscsi_aio_ioctl_cb, |                                  iscsi_aio_ioctl_cb, | ||||||
|                                  (data.size > 0) ? &data : NULL, |                                  (data.size > 0) ? &data : NULL, | ||||||
|                                  acb) != 0) { |                                  acb) != 0) { | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         scsi_free_scsi_task(acb->task); |         scsi_free_scsi_task(acb->task); | ||||||
|         qemu_aio_unref(acb); |         qemu_aio_unref(acb); | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -1063,6 +1094,7 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_set_events(iscsilun); |     iscsi_set_events(iscsilun); | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |  | ||||||
|     return &acb->common; |     return &acb->common; | ||||||
| } | } | ||||||
| @@ -1087,6 +1119,7 @@ coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) | |||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
|     struct IscsiTask iTask; |     struct IscsiTask iTask; | ||||||
|     struct unmap_list list; |     struct unmap_list list; | ||||||
|  |     int r = 0; | ||||||
|  |  | ||||||
|     if (!is_byte_request_lun_aligned(offset, count, iscsilun)) { |     if (!is_byte_request_lun_aligned(offset, count, iscsilun)) { | ||||||
|         return -ENOTSUP; |         return -ENOTSUP; | ||||||
| @@ -1101,15 +1134,19 @@ coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) | |||||||
|     list.num = count / iscsilun->block_size; |     list.num = count / iscsilun->block_size; | ||||||
|  |  | ||||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); |     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
| retry: | retry: | ||||||
|     if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1, |     if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1, | ||||||
|                          iscsi_co_generic_cb, &iTask) == NULL) { |                          iscsi_co_generic_cb, &iTask) == NULL) { | ||||||
|         return -ENOMEM; |         r = -ENOMEM; | ||||||
|  |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (!iTask.complete) { |     while (!iTask.complete) { | ||||||
|         iscsi_set_events(iscsilun); |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
| @@ -1126,17 +1163,20 @@ retry: | |||||||
|         /* the target might fail with a check condition if it |         /* the target might fail with a check condition if it | ||||||
|            is not happy with the alignment of the UNMAP request |            is not happy with the alignment of the UNMAP request | ||||||
|            we silently fail in this case */ |            we silently fail in this case */ | ||||||
|         return 0; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.status != SCSI_STATUS_GOOD) { |     if (iTask.status != SCSI_STATUS_GOOD) { | ||||||
|         return iTask.err_code; |         r = iTask.err_code; | ||||||
|  |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, |     iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, | ||||||
|                                count >> BDRV_SECTOR_BITS); |                                count >> BDRV_SECTOR_BITS); | ||||||
|  |  | ||||||
|     return 0; | out_unlock: | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |     return r; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int | static int | ||||||
| @@ -1148,6 +1188,7 @@ coroutine_fn iscsi_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, | |||||||
|     uint64_t lba; |     uint64_t lba; | ||||||
|     uint32_t nb_blocks; |     uint32_t nb_blocks; | ||||||
|     bool use_16_for_ws = iscsilun->use_16_for_rw; |     bool use_16_for_ws = iscsilun->use_16_for_rw; | ||||||
|  |     int r = 0; | ||||||
|  |  | ||||||
|     if (!is_byte_request_lun_aligned(offset, count, iscsilun)) { |     if (!is_byte_request_lun_aligned(offset, count, iscsilun)) { | ||||||
|         return -ENOTSUP; |         return -ENOTSUP; | ||||||
| @@ -1181,6 +1222,7 @@ coroutine_fn iscsi_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); |     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||||
| retry: | retry: | ||||||
|     if (use_16_for_ws) { |     if (use_16_for_ws) { | ||||||
| @@ -1200,7 +1242,9 @@ retry: | |||||||
|  |  | ||||||
|     while (!iTask.complete) { |     while (!iTask.complete) { | ||||||
|         iscsi_set_events(iscsilun); |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && |     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && | ||||||
| @@ -1210,7 +1254,8 @@ retry: | |||||||
|         /* WRITE SAME is not supported by the target */ |         /* WRITE SAME is not supported by the target */ | ||||||
|         iscsilun->has_write_same = false; |         iscsilun->has_write_same = false; | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
|         return -ENOTSUP; |         r = -ENOTSUP; | ||||||
|  |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
| @@ -1226,7 +1271,8 @@ retry: | |||||||
|     if (iTask.status != SCSI_STATUS_GOOD) { |     if (iTask.status != SCSI_STATUS_GOOD) { | ||||||
|         iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, |         iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, | ||||||
|                                    count >> BDRV_SECTOR_BITS); |                                    count >> BDRV_SECTOR_BITS); | ||||||
|         return iTask.err_code; |         r = iTask.err_code; | ||||||
|  |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (flags & BDRV_REQ_MAY_UNMAP) { |     if (flags & BDRV_REQ_MAY_UNMAP) { | ||||||
| @@ -1237,32 +1283,19 @@ retry: | |||||||
|                                      count >> BDRV_SECTOR_BITS); |                                      count >> BDRV_SECTOR_BITS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; | out_unlock: | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |     return r; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_chap(struct iscsi_context *iscsi, const char *target, | static void apply_chap(struct iscsi_context *iscsi, QemuOpts *opts, | ||||||
|                        Error **errp) |                        Error **errp) | ||||||
| { | { | ||||||
|     QemuOptsList *list; |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     const char *user = NULL; |     const char *user = NULL; | ||||||
|     const char *password = NULL; |     const char *password = NULL; | ||||||
|     const char *secretid; |     const char *secretid; | ||||||
|     char *secret = NULL; |     char *secret = NULL; | ||||||
|  |  | ||||||
|     list = qemu_find_opts("iscsi"); |  | ||||||
|     if (!list) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     opts = qemu_opts_find(list, target); |  | ||||||
|     if (opts == NULL) { |  | ||||||
|         opts = QTAILQ_FIRST(&list->head); |  | ||||||
|         if (!opts) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     user = qemu_opt_get(opts, "user"); |     user = qemu_opt_get(opts, "user"); | ||||||
|     if (!user) { |     if (!user) { | ||||||
|         return; |         return; | ||||||
| @@ -1293,65 +1326,37 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target, | |||||||
|     g_free(secret); |     g_free(secret); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_header_digest(struct iscsi_context *iscsi, const char *target, | static void apply_header_digest(struct iscsi_context *iscsi, QemuOpts *opts, | ||||||
|                                 Error **errp) |                                 Error **errp) | ||||||
| { | { | ||||||
|     QemuOptsList *list; |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     const char *digest = NULL; |     const char *digest = NULL; | ||||||
|  |  | ||||||
|     list = qemu_find_opts("iscsi"); |  | ||||||
|     if (!list) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     opts = qemu_opts_find(list, target); |  | ||||||
|     if (opts == NULL) { |  | ||||||
|         opts = QTAILQ_FIRST(&list->head); |  | ||||||
|         if (!opts) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     digest = qemu_opt_get(opts, "header-digest"); |     digest = qemu_opt_get(opts, "header-digest"); | ||||||
|     if (!digest) { |     if (!digest) { | ||||||
|         return; |         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); | ||||||
|     } |     } else if (!strcmp(digest, "crc32c")) { | ||||||
|  |  | ||||||
|     if (!strcmp(digest, "CRC32C")) { |  | ||||||
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C); |         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C); | ||||||
|     } else if (!strcmp(digest, "NONE")) { |     } else if (!strcmp(digest, "none")) { | ||||||
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE); |         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE); | ||||||
|     } else if (!strcmp(digest, "CRC32C-NONE")) { |     } else if (!strcmp(digest, "crc32c-none")) { | ||||||
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE); |         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE); | ||||||
|     } else if (!strcmp(digest, "NONE-CRC32C")) { |     } else if (!strcmp(digest, "none-crc32c")) { | ||||||
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); |         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); | ||||||
|     } else { |     } else { | ||||||
|         error_setg(errp, "Invalid header-digest setting : %s", digest); |         error_setg(errp, "Invalid header-digest setting : %s", digest); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static char *parse_initiator_name(const char *target) | static char *get_initiator_name(QemuOpts *opts) | ||||||
| { | { | ||||||
|     QemuOptsList *list; |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     const char *name; |     const char *name; | ||||||
|     char *iscsi_name; |     char *iscsi_name; | ||||||
|     UuidInfo *uuid_info; |     UuidInfo *uuid_info; | ||||||
|  |  | ||||||
|     list = qemu_find_opts("iscsi"); |  | ||||||
|     if (list) { |  | ||||||
|         opts = qemu_opts_find(list, target); |  | ||||||
|         if (!opts) { |  | ||||||
|             opts = QTAILQ_FIRST(&list->head); |  | ||||||
|         } |  | ||||||
|         if (opts) { |  | ||||||
|     name = qemu_opt_get(opts, "initiator-name"); |     name = qemu_opt_get(opts, "initiator-name"); | ||||||
|     if (name) { |     if (name) { | ||||||
|         return g_strdup(name); |         return g_strdup(name); | ||||||
|     } |     } | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uuid_info = qmp_query_uuid(NULL); |     uuid_info = qmp_query_uuid(NULL); | ||||||
|     if (strcmp(uuid_info->UUID, UUID_NONE) == 0) { |     if (strcmp(uuid_info->UUID, UUID_NONE) == 0) { | ||||||
| @@ -1365,43 +1370,24 @@ static char *parse_initiator_name(const char *target) | |||||||
|     return iscsi_name; |     return iscsi_name; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parse_timeout(const char *target) |  | ||||||
| { |  | ||||||
|     QemuOptsList *list; |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     const char *timeout; |  | ||||||
|  |  | ||||||
|     list = qemu_find_opts("iscsi"); |  | ||||||
|     if (list) { |  | ||||||
|         opts = qemu_opts_find(list, target); |  | ||||||
|         if (!opts) { |  | ||||||
|             opts = QTAILQ_FIRST(&list->head); |  | ||||||
|         } |  | ||||||
|         if (opts) { |  | ||||||
|             timeout = qemu_opt_get(opts, "timeout"); |  | ||||||
|             if (timeout) { |  | ||||||
|                 return atoi(timeout); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void iscsi_nop_timed_event(void *opaque) | static void iscsi_nop_timed_event(void *opaque) | ||||||
| { | { | ||||||
|     IscsiLun *iscsilun = opaque; |     IscsiLun *iscsilun = opaque; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&iscsilun->mutex); | ||||||
|     if (iscsi_get_nops_in_flight(iscsilun->iscsi) >= MAX_NOP_FAILURES) { |     if (iscsi_get_nops_in_flight(iscsilun->iscsi) >= MAX_NOP_FAILURES) { | ||||||
|         error_report("iSCSI: NOP timeout. Reconnecting..."); |         error_report("iSCSI: NOP timeout. Reconnecting..."); | ||||||
|         iscsilun->request_timed_out = true; |         iscsilun->request_timed_out = true; | ||||||
|     } else if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { |     } else if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { | ||||||
|         error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); |         error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); | ||||||
|         return; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); |     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); | ||||||
|     iscsi_set_events(iscsilun); |     iscsi_set_events(iscsilun); | ||||||
|  |  | ||||||
|  | out: | ||||||
|  |     qemu_mutex_unlock(&iscsilun->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) | static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) | ||||||
| @@ -1474,20 +1460,6 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* TODO Convert to fine grained options */ |  | ||||||
| static QemuOptsList runtime_opts = { |  | ||||||
|     .name = "iscsi", |  | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |  | ||||||
|     .desc = { |  | ||||||
|         { |  | ||||||
|             .name = "filename", |  | ||||||
|             .type = QEMU_OPT_STRING, |  | ||||||
|             .help = "URL to the iscsi image", |  | ||||||
|         }, |  | ||||||
|         { /* end of list */ } |  | ||||||
|     }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun, | static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun, | ||||||
|                                           int evpd, int pc, void **inq, Error **errp) |                                           int evpd, int pc, void **inq, Error **errp) | ||||||
| { | { | ||||||
| @@ -1605,24 +1577,178 @@ out: | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void iscsi_parse_iscsi_option(const char *target, QDict *options) | ||||||
|  | { | ||||||
|  |     QemuOptsList *list; | ||||||
|  |     QemuOpts *opts; | ||||||
|  |     const char *user, *password, *password_secret, *initiator_name, | ||||||
|  |                *header_digest, *timeout; | ||||||
|  |  | ||||||
|  |     list = qemu_find_opts("iscsi"); | ||||||
|  |     if (!list) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     opts = qemu_opts_find(list, target); | ||||||
|  |     if (opts == NULL) { | ||||||
|  |         opts = QTAILQ_FIRST(&list->head); | ||||||
|  |         if (!opts) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     user = qemu_opt_get(opts, "user"); | ||||||
|  |     if (user) { | ||||||
|  |         qdict_set_default_str(options, "user", user); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     password = qemu_opt_get(opts, "password"); | ||||||
|  |     if (password) { | ||||||
|  |         qdict_set_default_str(options, "password", password); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     password_secret = qemu_opt_get(opts, "password-secret"); | ||||||
|  |     if (password_secret) { | ||||||
|  |         qdict_set_default_str(options, "password-secret", password_secret); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     initiator_name = qemu_opt_get(opts, "initiator-name"); | ||||||
|  |     if (initiator_name) { | ||||||
|  |         qdict_set_default_str(options, "initiator-name", initiator_name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     header_digest = qemu_opt_get(opts, "header-digest"); | ||||||
|  |     if (header_digest) { | ||||||
|  |         /* -iscsi takes upper case values, but QAPI only supports lower case | ||||||
|  |          * enum constant names, so we have to convert here. */ | ||||||
|  |         char *qapi_value = g_ascii_strdown(header_digest, -1); | ||||||
|  |         qdict_set_default_str(options, "header-digest", qapi_value); | ||||||
|  |         g_free(qapi_value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     timeout = qemu_opt_get(opts, "timeout"); | ||||||
|  |     if (timeout) { | ||||||
|  |         qdict_set_default_str(options, "timeout", timeout); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * We support iscsi url's on the form |  * We support iscsi url's on the form | ||||||
|  * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> |  * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> | ||||||
|  */ |  */ | ||||||
|  | static void iscsi_parse_filename(const char *filename, QDict *options, | ||||||
|  |                                  Error **errp) | ||||||
|  | { | ||||||
|  |     struct iscsi_url *iscsi_url; | ||||||
|  |     const char *transport_name; | ||||||
|  |     char *lun_str; | ||||||
|  |  | ||||||
|  |     iscsi_url = iscsi_parse_full_url(NULL, filename); | ||||||
|  |     if (iscsi_url == NULL) { | ||||||
|  |         error_setg(errp, "Failed to parse URL : %s", filename); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
|  |     switch (iscsi_url->transport) { | ||||||
|  |     case TCP_TRANSPORT: | ||||||
|  |         transport_name = "tcp"; | ||||||
|  |         break; | ||||||
|  |     case ISER_TRANSPORT: | ||||||
|  |         transport_name = "iser"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         error_setg(errp, "Unknown transport type (%d)", | ||||||
|  |                    iscsi_url->transport); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     transport_name = "tcp"; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     qdict_set_default_str(options, "transport", transport_name); | ||||||
|  |     qdict_set_default_str(options, "portal", iscsi_url->portal); | ||||||
|  |     qdict_set_default_str(options, "target", iscsi_url->target); | ||||||
|  |  | ||||||
|  |     lun_str = g_strdup_printf("%d", iscsi_url->lun); | ||||||
|  |     qdict_set_default_str(options, "lun", lun_str); | ||||||
|  |     g_free(lun_str); | ||||||
|  |  | ||||||
|  |     /* User/password from -iscsi take precedence over those from the URL */ | ||||||
|  |     iscsi_parse_iscsi_option(iscsi_url->target, options); | ||||||
|  |  | ||||||
|  |     if (iscsi_url->user[0] != '\0') { | ||||||
|  |         qdict_set_default_str(options, "user", iscsi_url->user); | ||||||
|  |         qdict_set_default_str(options, "password", iscsi_url->passwd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     iscsi_destroy_url(iscsi_url); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static QemuOptsList runtime_opts = { | ||||||
|  |     .name = "iscsi", | ||||||
|  |     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), | ||||||
|  |     .desc = { | ||||||
|  |         { | ||||||
|  |             .name = "transport", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "portal", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "target", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "user", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "password", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "password-secret", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "lun", | ||||||
|  |             .type = QEMU_OPT_NUMBER, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "initiator-name", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "header-digest", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             .name = "timeout", | ||||||
|  |             .type = QEMU_OPT_NUMBER, | ||||||
|  |         }, | ||||||
|  |         { /* end of list */ } | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  |  | ||||||
| static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                       Error **errp) |                       Error **errp) | ||||||
| { | { | ||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
|     struct iscsi_context *iscsi = NULL; |     struct iscsi_context *iscsi = NULL; | ||||||
|     struct iscsi_url *iscsi_url = NULL; |  | ||||||
|     struct scsi_task *task = NULL; |     struct scsi_task *task = NULL; | ||||||
|     struct scsi_inquiry_standard *inq = NULL; |     struct scsi_inquiry_standard *inq = NULL; | ||||||
|     struct scsi_inquiry_supported_pages *inq_vpd; |     struct scsi_inquiry_supported_pages *inq_vpd; | ||||||
|     char *initiator_name = NULL; |     char *initiator_name = NULL; | ||||||
|     QemuOpts *opts; |     QemuOpts *opts; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     const char *filename; |     const char *transport_name, *portal, *target; | ||||||
|     int i, ret = 0, timeout = 0; | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
|  |     enum iscsi_transport_type transport; | ||||||
|  | #endif | ||||||
|  |     int i, ret = 0, timeout = 0, lun; | ||||||
|  |  | ||||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); |     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||||
| @@ -1632,18 +1758,34 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     filename = qemu_opt_get(opts, "filename"); |     transport_name = qemu_opt_get(opts, "transport"); | ||||||
|  |     portal = qemu_opt_get(opts, "portal"); | ||||||
|  |     target = qemu_opt_get(opts, "target"); | ||||||
|  |     lun = qemu_opt_get_number(opts, "lun", 0); | ||||||
|  |  | ||||||
|     iscsi_url = iscsi_parse_full_url(iscsi, filename); |     if (!transport_name || !portal || !target) { | ||||||
|     if (iscsi_url == NULL) { |         error_setg(errp, "Need all of transport, portal and target options"); | ||||||
|         error_setg(errp, "Failed to parse URL : %s", filename); |         ret = -EINVAL; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!strcmp(transport_name, "tcp")) { | ||||||
|  | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
|  |         transport = TCP_TRANSPORT; | ||||||
|  |     } else if (!strcmp(transport_name, "iser")) { | ||||||
|  |         transport = ISER_TRANSPORT; | ||||||
|  | #else | ||||||
|  |         /* TCP is what older libiscsi versions always use */ | ||||||
|  | #endif | ||||||
|  |     } else { | ||||||
|  |         error_setg(errp, "Unknown transport: %s", transport_name); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(iscsilun, 0, sizeof(IscsiLun)); |     memset(iscsilun, 0, sizeof(IscsiLun)); | ||||||
|  |  | ||||||
|     initiator_name = parse_initiator_name(iscsi_url->target); |     initiator_name = get_initiator_name(opts); | ||||||
|  |  | ||||||
|     iscsi = iscsi_create_context(initiator_name); |     iscsi = iscsi_create_context(initiator_name); | ||||||
|     if (iscsi == NULL) { |     if (iscsi == NULL) { | ||||||
| @@ -1652,30 +1794,20 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
| #if LIBISCSI_API_VERSION >= (20160603) | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
|     if (iscsi_init_transport(iscsi, iscsi_url->transport)) { |     if (iscsi_init_transport(iscsi, transport)) { | ||||||
|         error_setg(errp, ("Error initializing transport.")); |         error_setg(errp, ("Error initializing transport.")); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|     if (iscsi_set_targetname(iscsi, iscsi_url->target)) { |     if (iscsi_set_targetname(iscsi, target)) { | ||||||
|         error_setg(errp, "iSCSI: Failed to set target name."); |         error_setg(errp, "iSCSI: Failed to set target name."); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (iscsi_url->user[0] != '\0') { |  | ||||||
|         ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, |  | ||||||
|                                               iscsi_url->passwd); |  | ||||||
|         if (ret != 0) { |  | ||||||
|             error_setg(errp, "Failed to set initiator username and password"); |  | ||||||
|             ret = -EINVAL; |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* check if we got CHAP username/password via the options */ |     /* check if we got CHAP username/password via the options */ | ||||||
|     parse_chap(iscsi, iscsi_url->target, &local_err); |     apply_chap(iscsi, opts, &local_err); | ||||||
|     if (local_err != NULL) { |     if (local_err != NULL) { | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
| @@ -1688,10 +1820,8 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); |  | ||||||
|  |  | ||||||
|     /* check if we got HEADER_DIGEST via the options */ |     /* check if we got HEADER_DIGEST via the options */ | ||||||
|     parse_header_digest(iscsi, iscsi_url->target, &local_err); |     apply_header_digest(iscsi, opts, &local_err); | ||||||
|     if (local_err != NULL) { |     if (local_err != NULL) { | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
| @@ -1699,7 +1829,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* timeout handling is broken in libiscsi before 1.15.0 */ |     /* timeout handling is broken in libiscsi before 1.15.0 */ | ||||||
|     timeout = parse_timeout(iscsi_url->target); |     timeout = qemu_opt_get_number(opts, "timeout", 0); | ||||||
| #if LIBISCSI_API_VERSION >= 20150621 | #if LIBISCSI_API_VERSION >= 20150621 | ||||||
|     iscsi_set_timeout(iscsi, timeout); |     iscsi_set_timeout(iscsi, timeout); | ||||||
| #else | #else | ||||||
| @@ -1708,7 +1838,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { |     if (iscsi_full_connect_sync(iscsi, portal, lun) != 0) { | ||||||
|         error_setg(errp, "iSCSI: Failed to connect to LUN : %s", |         error_setg(errp, "iSCSI: Failed to connect to LUN : %s", | ||||||
|             iscsi_get_error(iscsi)); |             iscsi_get_error(iscsi)); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
| @@ -1717,7 +1847,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|  |  | ||||||
|     iscsilun->iscsi = iscsi; |     iscsilun->iscsi = iscsi; | ||||||
|     iscsilun->aio_context = bdrv_get_aio_context(bs); |     iscsilun->aio_context = bdrv_get_aio_context(bs); | ||||||
|     iscsilun->lun   = iscsi_url->lun; |     iscsilun->lun = lun; | ||||||
|     iscsilun->has_write_same = true; |     iscsilun->has_write_same = true; | ||||||
|  |  | ||||||
|     task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 0, 0, |     task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 0, 0, | ||||||
| @@ -1803,6 +1933,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     scsi_free_scsi_task(task); |     scsi_free_scsi_task(task); | ||||||
|     task = NULL; |     task = NULL; | ||||||
|  |  | ||||||
|  |     qemu_mutex_init(&iscsilun->mutex); | ||||||
|     iscsi_attach_aio_context(bs, iscsilun->aio_context); |     iscsi_attach_aio_context(bs, iscsilun->aio_context); | ||||||
|  |  | ||||||
|     /* Guess the internal cluster (page) size of the iscsi target by the means |     /* Guess the internal cluster (page) size of the iscsi target by the means | ||||||
| @@ -1820,9 +1951,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
| out: | out: | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     g_free(initiator_name); |     g_free(initiator_name); | ||||||
|     if (iscsi_url != NULL) { |  | ||||||
|         iscsi_destroy_url(iscsi_url); |  | ||||||
|     } |  | ||||||
|     if (task != NULL) { |     if (task != NULL) { | ||||||
|         scsi_free_scsi_task(task); |         scsi_free_scsi_task(task); | ||||||
|     } |     } | ||||||
| @@ -1851,6 +1979,7 @@ static void iscsi_close(BlockDriverState *bs) | |||||||
|     iscsi_destroy_context(iscsi); |     iscsi_destroy_context(iscsi); | ||||||
|     g_free(iscsilun->zeroblock); |     g_free(iscsilun->zeroblock); | ||||||
|     iscsi_allocmap_free(iscsilun); |     iscsi_allocmap_free(iscsilun); | ||||||
|  |     qemu_mutex_destroy(&iscsilun->mutex); | ||||||
|     memset(iscsilun, 0, sizeof(IscsiLun)); |     memset(iscsilun, 0, sizeof(IscsiLun)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2032,7 +2161,7 @@ static BlockDriver bdrv_iscsi = { | |||||||
|     .protocol_name   = "iscsi", |     .protocol_name   = "iscsi", | ||||||
|  |  | ||||||
|     .instance_size          = sizeof(IscsiLun), |     .instance_size          = sizeof(IscsiLun), | ||||||
|     .bdrv_needs_filename = true, |     .bdrv_parse_filename    = iscsi_parse_filename, | ||||||
|     .bdrv_file_open         = iscsi_open, |     .bdrv_file_open         = iscsi_open, | ||||||
|     .bdrv_close             = iscsi_close, |     .bdrv_close             = iscsi_close, | ||||||
|     .bdrv_create            = iscsi_create, |     .bdrv_create            = iscsi_create, | ||||||
| @@ -2067,7 +2196,7 @@ static BlockDriver bdrv_iser = { | |||||||
|     .protocol_name   = "iser", |     .protocol_name   = "iser", | ||||||
|  |  | ||||||
|     .instance_size          = sizeof(IscsiLun), |     .instance_size          = sizeof(IscsiLun), | ||||||
|     .bdrv_needs_filename = true, |     .bdrv_parse_filename    = iscsi_parse_filename, | ||||||
|     .bdrv_file_open         = iscsi_open, |     .bdrv_file_open         = iscsi_open, | ||||||
|     .bdrv_close             = iscsi_close, |     .bdrv_close             = iscsi_close, | ||||||
|     .bdrv_create            = iscsi_create, |     .bdrv_create            = iscsi_create, | ||||||
|   | |||||||
| @@ -54,10 +54,10 @@ struct LinuxAioState { | |||||||
|     io_context_t ctx; |     io_context_t ctx; | ||||||
|     EventNotifier e; |     EventNotifier e; | ||||||
|  |  | ||||||
|     /* io queue for submit at batch */ |     /* io queue for submit at batch.  Protected by AioContext lock. */ | ||||||
|     LaioQueue io_q; |     LaioQueue io_q; | ||||||
|  |  | ||||||
|     /* I/O completion processing */ |     /* I/O completion processing.  Only runs in I/O thread.  */ | ||||||
|     QEMUBH *completion_bh; |     QEMUBH *completion_bh; | ||||||
|     int event_idx; |     int event_idx; | ||||||
|     int event_max; |     int event_max; | ||||||
| @@ -100,7 +100,7 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) | |||||||
|          * that! |          * that! | ||||||
|          */ |          */ | ||||||
|         if (!qemu_coroutine_entered(laiocb->co)) { |         if (!qemu_coroutine_entered(laiocb->co)) { | ||||||
|             qemu_coroutine_enter(laiocb->co); |             aio_co_wake(laiocb->co); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         laiocb->common.cb(laiocb->common.opaque, ret); |         laiocb->common.cb(laiocb->common.opaque, ret); | ||||||
| @@ -234,9 +234,12 @@ static void qemu_laio_process_completions(LinuxAioState *s) | |||||||
| static void qemu_laio_process_completions_and_submit(LinuxAioState *s) | static void qemu_laio_process_completions_and_submit(LinuxAioState *s) | ||||||
| { | { | ||||||
|     qemu_laio_process_completions(s); |     qemu_laio_process_completions(s); | ||||||
|  |  | ||||||
|  |     aio_context_acquire(s->aio_context); | ||||||
|     if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { |     if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { | ||||||
|         ioq_submit(s); |         ioq_submit(s); | ||||||
|     } |     } | ||||||
|  |     aio_context_release(s->aio_context); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qemu_laio_completion_bh(void *opaque) | static void qemu_laio_completion_bh(void *opaque) | ||||||
| @@ -455,6 +458,7 @@ void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context) | |||||||
| { | { | ||||||
|     aio_set_event_notifier(old_context, &s->e, false, NULL, NULL); |     aio_set_event_notifier(old_context, &s->e, false, NULL, NULL); | ||||||
|     qemu_bh_delete(s->completion_bh); |     qemu_bh_delete(s->completion_bh); | ||||||
|  |     s->aio_context = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) | void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) | ||||||
|   | |||||||
| @@ -69,6 +69,7 @@ typedef struct MirrorBlockJob { | |||||||
|     bool waiting_for_io; |     bool waiting_for_io; | ||||||
|     int target_cluster_sectors; |     int target_cluster_sectors; | ||||||
|     int max_iov; |     int max_iov; | ||||||
|  |     bool initial_zeroing_ongoing; | ||||||
| } MirrorBlockJob; | } MirrorBlockJob; | ||||||
|  |  | ||||||
| typedef struct MirrorOp { | typedef struct MirrorOp { | ||||||
| @@ -117,9 +118,10 @@ static void mirror_iteration_done(MirrorOp *op, int ret) | |||||||
|         if (s->cow_bitmap) { |         if (s->cow_bitmap) { | ||||||
|             bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); |             bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); | ||||||
|         } |         } | ||||||
|  |         if (!s->initial_zeroing_ongoing) { | ||||||
|             s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE; |             s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|     qemu_iovec_destroy(&op->qiov); |     qemu_iovec_destroy(&op->qiov); | ||||||
|     g_free(op); |     g_free(op); | ||||||
|  |  | ||||||
| @@ -132,6 +134,8 @@ static void mirror_write_complete(void *opaque, int ret) | |||||||
| { | { | ||||||
|     MirrorOp *op = opaque; |     MirrorOp *op = opaque; | ||||||
|     MirrorBlockJob *s = op->s; |     MirrorBlockJob *s = op->s; | ||||||
|  |  | ||||||
|  |     aio_context_acquire(blk_get_aio_context(s->common.blk)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         BlockErrorAction action; |         BlockErrorAction action; | ||||||
|  |  | ||||||
| @@ -142,12 +146,15 @@ static void mirror_write_complete(void *opaque, int ret) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     mirror_iteration_done(op, ret); |     mirror_iteration_done(op, ret); | ||||||
|  |     aio_context_release(blk_get_aio_context(s->common.blk)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void mirror_read_complete(void *opaque, int ret) | static void mirror_read_complete(void *opaque, int ret) | ||||||
| { | { | ||||||
|     MirrorOp *op = opaque; |     MirrorOp *op = opaque; | ||||||
|     MirrorBlockJob *s = op->s; |     MirrorBlockJob *s = op->s; | ||||||
|  |  | ||||||
|  |     aio_context_acquire(blk_get_aio_context(s->common.blk)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         BlockErrorAction action; |         BlockErrorAction action; | ||||||
|  |  | ||||||
| @@ -158,11 +165,12 @@ static void mirror_read_complete(void *opaque, int ret) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         mirror_iteration_done(op, ret); |         mirror_iteration_done(op, ret); | ||||||
|         return; |     } else { | ||||||
|     } |  | ||||||
|         blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov, |         blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov, | ||||||
|                         0, mirror_write_complete, op); |                         0, mirror_write_complete, op); | ||||||
|     } |     } | ||||||
|  |     aio_context_release(blk_get_aio_context(s->common.blk)); | ||||||
|  | } | ||||||
|  |  | ||||||
| static inline void mirror_clip_sectors(MirrorBlockJob *s, | static inline void mirror_clip_sectors(MirrorBlockJob *s, | ||||||
|                                        int64_t sector_num, |                                        int64_t sector_num, | ||||||
| @@ -378,7 +386,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | |||||||
|                             nb_chunks * sectors_per_chunk); |                             nb_chunks * sectors_per_chunk); | ||||||
|     bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks); |     bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks); | ||||||
|     while (nb_chunks > 0 && sector_num < end) { |     while (nb_chunks > 0 && sector_num < end) { | ||||||
|         int ret; |         int64_t ret; | ||||||
|         int io_sectors, io_sectors_acct; |         int io_sectors, io_sectors_acct; | ||||||
|         BlockDriverState *file; |         BlockDriverState *file; | ||||||
|         enum MirrorMethod { |         enum MirrorMethod { | ||||||
| @@ -566,6 +574,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) | |||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         s->initial_zeroing_ongoing = true; | ||||||
|         for (sector_num = 0; sector_num < end; ) { |         for (sector_num = 0; sector_num < end; ) { | ||||||
|             int nb_sectors = MIN(end - sector_num, |             int nb_sectors = MIN(end - sector_num, | ||||||
|                 QEMU_ALIGN_DOWN(INT_MAX, s->granularity) >> BDRV_SECTOR_BITS); |                 QEMU_ALIGN_DOWN(INT_MAX, s->granularity) >> BDRV_SECTOR_BITS); | ||||||
| @@ -573,6 +582,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) | |||||||
|             mirror_throttle(s); |             mirror_throttle(s); | ||||||
|  |  | ||||||
|             if (block_job_is_cancelled(&s->common)) { |             if (block_job_is_cancelled(&s->common)) { | ||||||
|  |                 s->initial_zeroing_ongoing = false; | ||||||
|                 return 0; |                 return 0; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -587,6 +597,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         mirror_wait_for_all_io(s); |         mirror_wait_for_all_io(s); | ||||||
|  |         s->initial_zeroing_ongoing = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* First part, loop on the sectors and initialize the dirty bitmap.  */ |     /* First part, loop on the sectors and initialize the dirty bitmap.  */ | ||||||
| @@ -651,7 +662,28 @@ static void coroutine_fn mirror_run(void *opaque) | |||||||
|     if (s->bdev_length < 0) { |     if (s->bdev_length < 0) { | ||||||
|         ret = s->bdev_length; |         ret = s->bdev_length; | ||||||
|         goto immediate_exit; |         goto immediate_exit; | ||||||
|     } else if (s->bdev_length == 0) { |     } | ||||||
|  |  | ||||||
|  |     /* Active commit must resize the base image if its size differs from the | ||||||
|  |      * active layer. */ | ||||||
|  |     if (s->base == blk_bs(s->target)) { | ||||||
|  |         int64_t base_length; | ||||||
|  |  | ||||||
|  |         base_length = blk_getlength(s->target); | ||||||
|  |         if (base_length < 0) { | ||||||
|  |             ret = base_length; | ||||||
|  |             goto immediate_exit; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (s->bdev_length > base_length) { | ||||||
|  |             ret = blk_truncate(s->target, s->bdev_length); | ||||||
|  |             if (ret < 0) { | ||||||
|  |                 goto immediate_exit; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (s->bdev_length == 0) { | ||||||
|         /* Report BLOCK_JOB_READY and wait for complete. */ |         /* Report BLOCK_JOB_READY and wait for complete. */ | ||||||
|         block_job_event_ready(&s->common); |         block_job_event_ready(&s->common); | ||||||
|         s->synced = true; |         s->synced = true; | ||||||
| @@ -1052,9 +1084,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, | |||||||
|                          BlockCompletionFunc *cb, void *opaque, Error **errp, |                          BlockCompletionFunc *cb, void *opaque, Error **errp, | ||||||
|                          bool auto_complete) |                          bool auto_complete) | ||||||
| { | { | ||||||
|     int64_t length, base_length; |  | ||||||
|     int orig_base_flags; |     int orig_base_flags; | ||||||
|     int ret; |  | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     orig_base_flags = bdrv_get_flags(base); |     orig_base_flags = bdrv_get_flags(base); | ||||||
| @@ -1063,31 +1093,6 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     length = bdrv_getlength(bs); |  | ||||||
|     if (length < 0) { |  | ||||||
|         error_setg_errno(errp, -length, |  | ||||||
|                          "Unable to determine length of %s", bs->filename); |  | ||||||
|         goto error_restore_flags; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     base_length = bdrv_getlength(base); |  | ||||||
|     if (base_length < 0) { |  | ||||||
|         error_setg_errno(errp, -base_length, |  | ||||||
|                          "Unable to determine length of %s", base->filename); |  | ||||||
|         goto error_restore_flags; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (length > base_length) { |  | ||||||
|         ret = bdrv_truncate(base, length); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             error_setg_errno(errp, -ret, |  | ||||||
|                             "Top image %s is larger than base image %s, and " |  | ||||||
|                              "resize of base image failed", |  | ||||||
|                              bs->filename, base->filename); |  | ||||||
|             goto error_restore_flags; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0, |     mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0, | ||||||
|                      MIRROR_LEAVE_BACKING_CHAIN, |                      MIRROR_LEAVE_BACKING_CHAIN, | ||||||
|                      on_error, on_error, true, cb, opaque, &local_err, |                      on_error, on_error, true, cb, opaque, &local_err, | ||||||
|   | |||||||
| @@ -33,8 +33,9 @@ | |||||||
| #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) | #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) | ||||||
| #define INDEX_TO_HANDLE(bs, index)  ((index)  ^ ((uint64_t)(intptr_t)bs)) | #define INDEX_TO_HANDLE(bs, index)  ((index)  ^ ((uint64_t)(intptr_t)bs)) | ||||||
|  |  | ||||||
| static void nbd_recv_coroutines_enter_all(NBDClientSession *s) | static void nbd_recv_coroutines_enter_all(BlockDriverState *bs) | ||||||
| { | { | ||||||
|  |     NBDClientSession *s = nbd_get_client_session(bs); | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_NBD_REQUESTS; i++) { |     for (i = 0; i < MAX_NBD_REQUESTS; i++) { | ||||||
| @@ -42,6 +43,7 @@ static void nbd_recv_coroutines_enter_all(NBDClientSession *s) | |||||||
|             qemu_coroutine_enter(s->recv_coroutine[i]); |             qemu_coroutine_enter(s->recv_coroutine[i]); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     BDRV_POLL_WHILE(bs, s->read_reply_co); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nbd_teardown_connection(BlockDriverState *bs) | static void nbd_teardown_connection(BlockDriverState *bs) | ||||||
| @@ -56,7 +58,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) | |||||||
|     qio_channel_shutdown(client->ioc, |     qio_channel_shutdown(client->ioc, | ||||||
|                          QIO_CHANNEL_SHUTDOWN_BOTH, |                          QIO_CHANNEL_SHUTDOWN_BOTH, | ||||||
|                          NULL); |                          NULL); | ||||||
|     nbd_recv_coroutines_enter_all(client); |     nbd_recv_coroutines_enter_all(bs); | ||||||
|  |  | ||||||
|     nbd_client_detach_aio_context(bs); |     nbd_client_detach_aio_context(bs); | ||||||
|     object_unref(OBJECT(client->sioc)); |     object_unref(OBJECT(client->sioc)); | ||||||
| @@ -65,54 +67,43 @@ static void nbd_teardown_connection(BlockDriverState *bs) | |||||||
|     client->ioc = NULL; |     client->ioc = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nbd_reply_ready(void *opaque) | static coroutine_fn void nbd_read_reply_entry(void *opaque) | ||||||
| { | { | ||||||
|     BlockDriverState *bs = opaque; |     NBDClientSession *s = opaque; | ||||||
|     NBDClientSession *s = nbd_get_client_session(bs); |  | ||||||
|     uint64_t i; |     uint64_t i; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     if (!s->ioc) { /* Already closed */ |     for (;;) { | ||||||
|         return; |         assert(s->reply.handle == 0); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (s->reply.handle == 0) { |  | ||||||
|         /* No reply already in flight.  Fetch a header.  It is possible |  | ||||||
|          * that another thread has done the same thing in parallel, so |  | ||||||
|          * the socket is not readable anymore. |  | ||||||
|          */ |  | ||||||
|         ret = nbd_receive_reply(s->ioc, &s->reply); |         ret = nbd_receive_reply(s->ioc, &s->reply); | ||||||
|         if (ret == -EAGAIN) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             s->reply.handle = 0; |             break; | ||||||
|             goto fail; |  | ||||||
|         } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* There's no need for a mutex on the receive side, because the |         /* There's no need for a mutex on the receive side, because the | ||||||
|          * handler acts as a synchronization point and ensures that only |          * handler acts as a synchronization point and ensures that only | ||||||
|      * one coroutine is called until the reply finishes.  */ |          * one coroutine is called until the reply finishes. | ||||||
|  |          */ | ||||||
|         i = HANDLE_TO_INDEX(s, s->reply.handle); |         i = HANDLE_TO_INDEX(s, s->reply.handle); | ||||||
|     if (i >= MAX_NBD_REQUESTS) { |         if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) { | ||||||
|         goto fail; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     if (s->recv_coroutine[i]) { |         /* We're woken up by the recv_coroutine itself.  Note that there | ||||||
|         qemu_coroutine_enter(s->recv_coroutine[i]); |          * is no race between yielding and reentering read_reply_co.  This | ||||||
|         return; |          * is because: | ||||||
|  |          * | ||||||
|  |          * - if recv_coroutine[i] runs on the same AioContext, it is only | ||||||
|  |          *   entered after we yield | ||||||
|  |          * | ||||||
|  |          * - if recv_coroutine[i] runs on a different AioContext, reentering | ||||||
|  |          *   read_reply_co happens through a bottom half, which can only | ||||||
|  |          *   run after we yield. | ||||||
|  |          */ | ||||||
|  |         aio_co_wake(s->recv_coroutine[i]); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|     } |     } | ||||||
|  |     s->read_reply_co = NULL; | ||||||
| fail: |  | ||||||
|     nbd_teardown_connection(bs); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void nbd_restart_write(void *opaque) |  | ||||||
| { |  | ||||||
|     BlockDriverState *bs = opaque; |  | ||||||
|  |  | ||||||
|     qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int nbd_co_send_request(BlockDriverState *bs, | static int nbd_co_send_request(BlockDriverState *bs, | ||||||
| @@ -120,7 +111,6 @@ static int nbd_co_send_request(BlockDriverState *bs, | |||||||
|                                QEMUIOVector *qiov) |                                QEMUIOVector *qiov) | ||||||
| { | { | ||||||
|     NBDClientSession *s = nbd_get_client_session(bs); |     NBDClientSession *s = nbd_get_client_session(bs); | ||||||
|     AioContext *aio_context; |  | ||||||
|     int rc, ret, i; |     int rc, ret, i; | ||||||
|  |  | ||||||
|     qemu_co_mutex_lock(&s->send_mutex); |     qemu_co_mutex_lock(&s->send_mutex); | ||||||
| @@ -141,11 +131,6 @@ static int nbd_co_send_request(BlockDriverState *bs, | |||||||
|         return -EPIPE; |         return -EPIPE; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->send_coroutine = qemu_coroutine_self(); |  | ||||||
|     aio_context = bdrv_get_aio_context(bs); |  | ||||||
|  |  | ||||||
|     aio_set_fd_handler(aio_context, s->sioc->fd, false, |  | ||||||
|                        nbd_reply_ready, nbd_restart_write, NULL, bs); |  | ||||||
|     if (qiov) { |     if (qiov) { | ||||||
|         qio_channel_set_cork(s->ioc, true); |         qio_channel_set_cork(s->ioc, true); | ||||||
|         rc = nbd_send_request(s->ioc, request); |         rc = nbd_send_request(s->ioc, request); | ||||||
| @@ -160,9 +145,6 @@ static int nbd_co_send_request(BlockDriverState *bs, | |||||||
|     } else { |     } else { | ||||||
|         rc = nbd_send_request(s->ioc, request); |         rc = nbd_send_request(s->ioc, request); | ||||||
|     } |     } | ||||||
|     aio_set_fd_handler(aio_context, s->sioc->fd, false, |  | ||||||
|                        nbd_reply_ready, NULL, NULL, bs); |  | ||||||
|     s->send_coroutine = NULL; |  | ||||||
|     qemu_co_mutex_unlock(&s->send_mutex); |     qemu_co_mutex_unlock(&s->send_mutex); | ||||||
|     return rc; |     return rc; | ||||||
| } | } | ||||||
| @@ -174,8 +156,7 @@ static void nbd_co_receive_reply(NBDClientSession *s, | |||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     /* Wait until we're woken up by the read handler.  TODO: perhaps |     /* Wait until we're woken up by nbd_read_reply_entry.  */ | ||||||
|      * peek at the next reply and avoid yielding if it's ours?  */ |  | ||||||
|     qemu_coroutine_yield(); |     qemu_coroutine_yield(); | ||||||
|     *reply = s->reply; |     *reply = s->reply; | ||||||
|     if (reply->handle != request->handle || |     if (reply->handle != request->handle || | ||||||
| @@ -201,7 +182,7 @@ static void nbd_coroutine_start(NBDClientSession *s, | |||||||
|     /* Poor man semaphore.  The free_sema is locked when no other request |     /* Poor man semaphore.  The free_sema is locked when no other request | ||||||
|      * can be accepted, and unlocked after receiving one reply.  */ |      * can be accepted, and unlocked after receiving one reply.  */ | ||||||
|     if (s->in_flight == MAX_NBD_REQUESTS) { |     if (s->in_flight == MAX_NBD_REQUESTS) { | ||||||
|         qemu_co_queue_wait(&s->free_sema); |         qemu_co_queue_wait(&s->free_sema, NULL); | ||||||
|         assert(s->in_flight < MAX_NBD_REQUESTS); |         assert(s->in_flight < MAX_NBD_REQUESTS); | ||||||
|     } |     } | ||||||
|     s->in_flight++; |     s->in_flight++; | ||||||
| @@ -209,13 +190,19 @@ static void nbd_coroutine_start(NBDClientSession *s, | |||||||
|     /* s->recv_coroutine[i] is set as soon as we get the send_lock.  */ |     /* s->recv_coroutine[i] is set as soon as we get the send_lock.  */ | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nbd_coroutine_end(NBDClientSession *s, | static void nbd_coroutine_end(BlockDriverState *bs, | ||||||
|                               NBDRequest *request) |                               NBDRequest *request) | ||||||
| { | { | ||||||
|  |     NBDClientSession *s = nbd_get_client_session(bs); | ||||||
|     int i = HANDLE_TO_INDEX(s, request->handle); |     int i = HANDLE_TO_INDEX(s, request->handle); | ||||||
|  |  | ||||||
|     s->recv_coroutine[i] = NULL; |     s->recv_coroutine[i] = NULL; | ||||||
|     if (s->in_flight-- == MAX_NBD_REQUESTS) { |     s->in_flight--; | ||||||
|     qemu_co_queue_next(&s->free_sema); |     qemu_co_queue_next(&s->free_sema); | ||||||
|  |  | ||||||
|  |     /* Kick the read_reply_co to get the next reply.  */ | ||||||
|  |     if (s->read_reply_co) { | ||||||
|  |         aio_co_wake(s->read_reply_co); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -241,7 +228,7 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|     } else { |     } else { | ||||||
|         nbd_co_receive_reply(client, &request, &reply, qiov); |         nbd_co_receive_reply(client, &request, &reply, qiov); | ||||||
|     } |     } | ||||||
|     nbd_coroutine_end(client, &request); |     nbd_coroutine_end(bs, &request); | ||||||
|     return -reply.error; |     return -reply.error; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -271,7 +258,7 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, | |||||||
|     } else { |     } else { | ||||||
|         nbd_co_receive_reply(client, &request, &reply, NULL); |         nbd_co_receive_reply(client, &request, &reply, NULL); | ||||||
|     } |     } | ||||||
|     nbd_coroutine_end(client, &request); |     nbd_coroutine_end(bs, &request); | ||||||
|     return -reply.error; |     return -reply.error; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -306,7 +293,7 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, | |||||||
|     } else { |     } else { | ||||||
|         nbd_co_receive_reply(client, &request, &reply, NULL); |         nbd_co_receive_reply(client, &request, &reply, NULL); | ||||||
|     } |     } | ||||||
|     nbd_coroutine_end(client, &request); |     nbd_coroutine_end(bs, &request); | ||||||
|     return -reply.error; |     return -reply.error; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -331,7 +318,7 @@ int nbd_client_co_flush(BlockDriverState *bs) | |||||||
|     } else { |     } else { | ||||||
|         nbd_co_receive_reply(client, &request, &reply, NULL); |         nbd_co_receive_reply(client, &request, &reply, NULL); | ||||||
|     } |     } | ||||||
|     nbd_coroutine_end(client, &request); |     nbd_coroutine_end(bs, &request); | ||||||
|     return -reply.error; |     return -reply.error; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -357,23 +344,23 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) | |||||||
|     } else { |     } else { | ||||||
|         nbd_co_receive_reply(client, &request, &reply, NULL); |         nbd_co_receive_reply(client, &request, &reply, NULL); | ||||||
|     } |     } | ||||||
|     nbd_coroutine_end(client, &request); |     nbd_coroutine_end(bs, &request); | ||||||
|     return -reply.error; |     return -reply.error; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void nbd_client_detach_aio_context(BlockDriverState *bs) | void nbd_client_detach_aio_context(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|                        nbd_get_client_session(bs)->sioc->fd, |     qio_channel_detach_aio_context(QIO_CHANNEL(client->sioc)); | ||||||
|                        false, NULL, NULL, NULL, NULL); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void nbd_client_attach_aio_context(BlockDriverState *bs, | void nbd_client_attach_aio_context(BlockDriverState *bs, | ||||||
|                                    AioContext *new_context) |                                    AioContext *new_context) | ||||||
| { | { | ||||||
|     aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd, |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|                        false, nbd_reply_ready, NULL, NULL, bs); |     qio_channel_attach_aio_context(QIO_CHANNEL(client->sioc), new_context); | ||||||
|  |     aio_co_schedule(new_context, client->read_reply_co); | ||||||
| } | } | ||||||
|  |  | ||||||
| void nbd_client_close(BlockDriverState *bs) | void nbd_client_close(BlockDriverState *bs) | ||||||
| @@ -434,7 +421,7 @@ int nbd_client_init(BlockDriverState *bs, | |||||||
|     /* Now that we're connected, set the socket to be non-blocking and |     /* Now that we're connected, set the socket to be non-blocking and | ||||||
|      * kick the reply mechanism.  */ |      * kick the reply mechanism.  */ | ||||||
|     qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); |     qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); | ||||||
|  |     client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); | ||||||
|     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); |     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); | ||||||
|  |  | ||||||
|     logout("Established connection with NBD server\n"); |     logout("Established connection with NBD server\n"); | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ typedef struct NBDClientSession { | |||||||
|  |  | ||||||
|     CoMutex send_mutex; |     CoMutex send_mutex; | ||||||
|     CoQueue free_sema; |     CoQueue free_sema; | ||||||
|     Coroutine *send_coroutine; |     Coroutine *read_reply_co; | ||||||
|     int in_flight; |     int in_flight; | ||||||
|  |  | ||||||
|     Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; |     Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; | ||||||
|   | |||||||
| @@ -537,8 +537,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) | |||||||
|     visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); |     visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); | ||||||
|     visit_complete(ov, &saddr_qdict); |     visit_complete(ov, &saddr_qdict); | ||||||
|     visit_free(ov); |     visit_free(ov); | ||||||
|     assert(qobject_type(saddr_qdict) == QTYPE_QDICT); |  | ||||||
|  |  | ||||||
|     qdict_put_obj(opts, "server", saddr_qdict); |     qdict_put_obj(opts, "server", saddr_qdict); | ||||||
|  |  | ||||||
|     if (s->export) { |     if (s->export) { | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								block/nfs.c
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								block/nfs.c
									
									
									
									
									
								
							| @@ -54,6 +54,7 @@ typedef struct NFSClient { | |||||||
|     int events; |     int events; | ||||||
|     bool has_zero_init; |     bool has_zero_init; | ||||||
|     AioContext *aio_context; |     AioContext *aio_context; | ||||||
|  |     QemuMutex mutex; | ||||||
|     blkcnt_t st_blocks; |     blkcnt_t st_blocks; | ||||||
|     bool cache_used; |     bool cache_used; | ||||||
|     NFSServer *server; |     NFSServer *server; | ||||||
| @@ -191,6 +192,7 @@ static void nfs_parse_filename(const char *filename, QDict *options, | |||||||
| static void nfs_process_read(void *arg); | static void nfs_process_read(void *arg); | ||||||
| static void nfs_process_write(void *arg); | static void nfs_process_write(void *arg); | ||||||
|  |  | ||||||
|  | /* Called with QemuMutex held.  */ | ||||||
| static void nfs_set_events(NFSClient *client) | static void nfs_set_events(NFSClient *client) | ||||||
| { | { | ||||||
|     int ev = nfs_which_events(client->context); |     int ev = nfs_which_events(client->context); | ||||||
| @@ -208,15 +210,21 @@ static void nfs_set_events(NFSClient *client) | |||||||
| static void nfs_process_read(void *arg) | static void nfs_process_read(void *arg) | ||||||
| { | { | ||||||
|     NFSClient *client = arg; |     NFSClient *client = arg; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&client->mutex); | ||||||
|     nfs_service(client->context, POLLIN); |     nfs_service(client->context, POLLIN); | ||||||
|     nfs_set_events(client); |     nfs_set_events(client); | ||||||
|  |     qemu_mutex_unlock(&client->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nfs_process_write(void *arg) | static void nfs_process_write(void *arg) | ||||||
| { | { | ||||||
|     NFSClient *client = arg; |     NFSClient *client = arg; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&client->mutex); | ||||||
|     nfs_service(client->context, POLLOUT); |     nfs_service(client->context, POLLOUT); | ||||||
|     nfs_set_events(client); |     nfs_set_events(client); | ||||||
|  |     qemu_mutex_unlock(&client->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) | static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) | ||||||
| @@ -231,10 +239,12 @@ static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) | |||||||
| static void nfs_co_generic_bh_cb(void *opaque) | static void nfs_co_generic_bh_cb(void *opaque) | ||||||
| { | { | ||||||
|     NFSRPC *task = opaque; |     NFSRPC *task = opaque; | ||||||
|  |  | ||||||
|     task->complete = 1; |     task->complete = 1; | ||||||
|     qemu_coroutine_enter(task->co); |     aio_co_wake(task->co); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called (via nfs_service) with QemuMutex held.  */ | ||||||
| static void | static void | ||||||
| nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, | nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, | ||||||
|                   void *private_data) |                   void *private_data) | ||||||
| @@ -256,9 +266,9 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, | |||||||
|                             nfs_co_generic_bh_cb, task); |                             nfs_co_generic_bh_cb, task); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn nfs_co_readv(BlockDriverState *bs, | static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||||
|                                      int64_t sector_num, int nb_sectors, |                                       uint64_t bytes, QEMUIOVector *iov, | ||||||
|                                      QEMUIOVector *iov) |                                       int flags) | ||||||
| { | { | ||||||
|     NFSClient *client = bs->opaque; |     NFSClient *client = bs->opaque; | ||||||
|     NFSRPC task; |     NFSRPC task; | ||||||
| @@ -266,14 +276,15 @@ static int coroutine_fn nfs_co_readv(BlockDriverState *bs, | |||||||
|     nfs_co_init_task(bs, &task); |     nfs_co_init_task(bs, &task); | ||||||
|     task.iov = iov; |     task.iov = iov; | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&client->mutex); | ||||||
|     if (nfs_pread_async(client->context, client->fh, |     if (nfs_pread_async(client->context, client->fh, | ||||||
|                         sector_num * BDRV_SECTOR_SIZE, |                         offset, bytes, nfs_co_generic_cb, &task) != 0) { | ||||||
|                         nb_sectors * BDRV_SECTOR_SIZE, |         qemu_mutex_unlock(&client->mutex); | ||||||
|                         nfs_co_generic_cb, &task) != 0) { |  | ||||||
|         return -ENOMEM; |         return -ENOMEM; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     nfs_set_events(client); |     nfs_set_events(client); | ||||||
|  |     qemu_mutex_unlock(&client->mutex); | ||||||
|     while (!task.complete) { |     while (!task.complete) { | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|     } |     } | ||||||
| @@ -290,39 +301,50 @@ static int coroutine_fn nfs_co_readv(BlockDriverState *bs, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn nfs_co_writev(BlockDriverState *bs, | static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||||
|                                         int64_t sector_num, int nb_sectors, |                                        uint64_t bytes, QEMUIOVector *iov, | ||||||
|                                         QEMUIOVector *iov) |                                        int flags) | ||||||
| { | { | ||||||
|     NFSClient *client = bs->opaque; |     NFSClient *client = bs->opaque; | ||||||
|     NFSRPC task; |     NFSRPC task; | ||||||
|     char *buf = NULL; |     char *buf = NULL; | ||||||
|  |     bool my_buffer = false; | ||||||
|  |  | ||||||
|     nfs_co_init_task(bs, &task); |     nfs_co_init_task(bs, &task); | ||||||
|  |  | ||||||
|     buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE); |     if (iov->niov != 1) { | ||||||
|     if (nb_sectors && buf == NULL) { |         buf = g_try_malloc(bytes); | ||||||
|  |         if (bytes && buf == NULL) { | ||||||
|             return -ENOMEM; |             return -ENOMEM; | ||||||
|         } |         } | ||||||
|  |         qemu_iovec_to_buf(iov, 0, buf, bytes); | ||||||
|  |         my_buffer = true; | ||||||
|  |     } else { | ||||||
|  |         buf = iov->iov[0].iov_base; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); |     qemu_mutex_lock(&client->mutex); | ||||||
|  |  | ||||||
|     if (nfs_pwrite_async(client->context, client->fh, |     if (nfs_pwrite_async(client->context, client->fh, | ||||||
|                          sector_num * BDRV_SECTOR_SIZE, |                          offset, bytes, buf, | ||||||
|                          nb_sectors * BDRV_SECTOR_SIZE, |                          nfs_co_generic_cb, &task) != 0) { | ||||||
|                          buf, nfs_co_generic_cb, &task) != 0) { |         qemu_mutex_unlock(&client->mutex); | ||||||
|  |         if (my_buffer) { | ||||||
|             g_free(buf); |             g_free(buf); | ||||||
|  |         } | ||||||
|         return -ENOMEM; |         return -ENOMEM; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     nfs_set_events(client); |     nfs_set_events(client); | ||||||
|  |     qemu_mutex_unlock(&client->mutex); | ||||||
|     while (!task.complete) { |     while (!task.complete) { | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (my_buffer) { | ||||||
|         g_free(buf); |         g_free(buf); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { |     if (task.ret != bytes) { | ||||||
|         return task.ret < 0 ? task.ret : -EIO; |         return task.ret < 0 ? task.ret : -EIO; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -336,12 +358,15 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs) | |||||||
|  |  | ||||||
|     nfs_co_init_task(bs, &task); |     nfs_co_init_task(bs, &task); | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock(&client->mutex); | ||||||
|     if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, |     if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, | ||||||
|                         &task) != 0) { |                         &task) != 0) { | ||||||
|  |         qemu_mutex_unlock(&client->mutex); | ||||||
|         return -ENOMEM; |         return -ENOMEM; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     nfs_set_events(client); |     nfs_set_events(client); | ||||||
|  |     qemu_mutex_unlock(&client->mutex); | ||||||
|     while (!task.complete) { |     while (!task.complete) { | ||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|     } |     } | ||||||
| @@ -427,6 +452,7 @@ static void nfs_file_close(BlockDriverState *bs) | |||||||
| { | { | ||||||
|     NFSClient *client = bs->opaque; |     NFSClient *client = bs->opaque; | ||||||
|     nfs_client_close(client); |     nfs_client_close(client); | ||||||
|  |     qemu_mutex_destroy(&client->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static NFSServer *nfs_config(QDict *options, Error **errp) | static NFSServer *nfs_config(QDict *options, Error **errp) | ||||||
| @@ -634,6 +660,7 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |     qemu_mutex_init(&client->mutex); | ||||||
|     bs->total_sectors = ret; |     bs->total_sectors = ret; | ||||||
|     ret = 0; |     ret = 0; | ||||||
|     return ret; |     return ret; | ||||||
| @@ -689,6 +716,7 @@ static int nfs_has_zero_init(BlockDriverState *bs) | |||||||
|     return client->has_zero_init; |     return client->has_zero_init; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called (via nfs_service) with QemuMutex held.  */ | ||||||
| static void | static void | ||||||
| nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, | nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, | ||||||
|                                void *private_data) |                                void *private_data) | ||||||
| @@ -798,8 +826,6 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options) | |||||||
|     ov = qobject_output_visitor_new(&server_qdict); |     ov = qobject_output_visitor_new(&server_qdict); | ||||||
|     visit_type_NFSServer(ov, NULL, &client->server, &error_abort); |     visit_type_NFSServer(ov, NULL, &client->server, &error_abort); | ||||||
|     visit_complete(ov, &server_qdict); |     visit_complete(ov, &server_qdict); | ||||||
|     assert(qobject_type(server_qdict) == QTYPE_QDICT); |  | ||||||
|  |  | ||||||
|     qdict_put_obj(opts, "server", server_qdict); |     qdict_put_obj(opts, "server", server_qdict); | ||||||
|     qdict_put(opts, "path", qstring_from_str(client->path)); |     qdict_put(opts, "path", qstring_from_str(client->path)); | ||||||
|  |  | ||||||
| @@ -856,8 +882,8 @@ static BlockDriver bdrv_nfs = { | |||||||
|     .bdrv_create                    = nfs_file_create, |     .bdrv_create                    = nfs_file_create, | ||||||
|     .bdrv_reopen_prepare            = nfs_reopen_prepare, |     .bdrv_reopen_prepare            = nfs_reopen_prepare, | ||||||
|  |  | ||||||
|     .bdrv_co_readv                  = nfs_co_readv, |     .bdrv_co_preadv                 = nfs_co_preadv, | ||||||
|     .bdrv_co_writev                 = nfs_co_writev, |     .bdrv_co_pwritev                = nfs_co_pwritev, | ||||||
|     .bdrv_co_flush_to_disk          = nfs_co_flush, |     .bdrv_co_flush_to_disk          = nfs_co_flush, | ||||||
|  |  | ||||||
|     .bdrv_detach_aio_context        = nfs_detach_aio_context, |     .bdrv_detach_aio_context        = nfs_detach_aio_context, | ||||||
|   | |||||||
| @@ -215,7 +215,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, | |||||||
|                                      s->data_end << BDRV_SECTOR_BITS, |                                      s->data_end << BDRV_SECTOR_BITS, | ||||||
|                                      space << BDRV_SECTOR_BITS, 0); |                                      space << BDRV_SECTOR_BITS, 0); | ||||||
|         } else { |         } else { | ||||||
|             ret = bdrv_truncate(bs->file->bs, |             ret = bdrv_truncate(bs->file, | ||||||
|                                 (s->data_end + space) << BDRV_SECTOR_BITS); |                                 (s->data_end + space) << BDRV_SECTOR_BITS); | ||||||
|         } |         } | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
| @@ -449,7 +449,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|                 size - res->image_end_offset); |                 size - res->image_end_offset); | ||||||
|         res->leaks += count; |         res->leaks += count; | ||||||
|         if (fix & BDRV_FIX_LEAKS) { |         if (fix & BDRV_FIX_LEAKS) { | ||||||
|             ret = bdrv_truncate(bs->file->bs, res->image_end_offset); |             ret = bdrv_truncate(bs->file, res->image_end_offset); | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 res->check_errors++; |                 res->check_errors++; | ||||||
|                 return ret; |                 return ret; | ||||||
| @@ -581,6 +581,12 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     char *buf; |     char *buf; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); |     ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
| @@ -681,7 +687,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto fail_options; |         goto fail_options; | ||||||
|     } |     } | ||||||
|     if (!bdrv_has_zero_init(bs->file->bs) || |     if (!bdrv_has_zero_init(bs->file->bs) || | ||||||
|             bdrv_truncate(bs->file->bs, bdrv_getlength(bs->file->bs)) != 0) { |             bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs)) != 0) { | ||||||
|         s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; |         s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -724,7 +730,7 @@ static void parallels_close(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (bs->open_flags & BDRV_O_RDWR) { |     if (bs->open_flags & BDRV_O_RDWR) { | ||||||
|         bdrv_truncate(bs->file->bs, s->data_end << BDRV_SECTOR_BITS); |         bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     g_free(s->bat_dirty_bmap); |     g_free(s->bat_dirty_bmap); | ||||||
|   | |||||||
| @@ -682,7 +682,6 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, | |||||||
|  |  | ||||||
|     visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); |     visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); | ||||||
|     visit_complete(v, &obj); |     visit_complete(v, &obj); | ||||||
|     assert(qobject_type(obj) == QTYPE_QDICT); |  | ||||||
|     data = qdict_get(qobject_to_qdict(obj), "data"); |     data = qdict_get(qobject_to_qdict(obj), "data"); | ||||||
|     dump_qobject(func_fprintf, f, 1, data); |     dump_qobject(func_fprintf, f, 1, data); | ||||||
|     qobject_decref(obj); |     qobject_decref(obj); | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -106,6 +106,12 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     QCowHeader header; |     QCowHeader header; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); |     ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
| @@ -467,7 +473,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | |||||||
|                 /* round to cluster size */ |                 /* round to cluster size */ | ||||||
|                 cluster_offset = (cluster_offset + s->cluster_size - 1) & |                 cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||||||
|                     ~(s->cluster_size - 1); |                     ~(s->cluster_size - 1); | ||||||
|                 bdrv_truncate(bs->file->bs, cluster_offset + s->cluster_size); |                 bdrv_truncate(bs->file, cluster_offset + s->cluster_size); | ||||||
|                 /* if encrypted, we must initialize the cluster |                 /* if encrypted, we must initialize the cluster | ||||||
|                    content which won't be written */ |                    content which won't be written */ | ||||||
|                 if (bs->encrypted && |                 if (bs->encrypted && | ||||||
| @@ -909,7 +915,7 @@ static int qcow_make_empty(BlockDriverState *bs) | |||||||
|     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, |     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, | ||||||
|             l1_length) < 0) |             l1_length) < 0) | ||||||
|         return -1; |         return -1; | ||||||
|     ret = bdrv_truncate(bs->file->bs, s->l1_table_offset + l1_length); |     ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -932,9 +932,7 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, | |||||||
|             if (bytes == 0) { |             if (bytes == 0) { | ||||||
|                 /* Wait for the dependency to complete. We need to recheck |                 /* Wait for the dependency to complete. We need to recheck | ||||||
|                  * the free/allocated clusters when we continue. */ |                  * the free/allocated clusters when we continue. */ | ||||||
|                 qemu_co_mutex_unlock(&s->lock); |                 qemu_co_queue_wait(&old_alloc->dependent_requests, &s->lock); | ||||||
|                 qemu_co_queue_wait(&old_alloc->dependent_requests); |  | ||||||
|                 qemu_co_mutex_lock(&s->lock); |  | ||||||
|                 return -EAGAIN; |                 return -EAGAIN; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1734,7 +1734,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|                     goto resize_fail; |                     goto resize_fail; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 ret = bdrv_truncate(bs->file->bs, offset + s->cluster_size); |                 ret = bdrv_truncate(bs->file, offset + s->cluster_size); | ||||||
|                 if (ret < 0) { |                 if (ret < 0) { | ||||||
|                     goto resize_fail; |                     goto resize_fail; | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -814,7 +814,7 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options, | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, | static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                          Error **errp) |                          Error **errp) | ||||||
| { | { | ||||||
|     BDRVQcow2State *s = bs->opaque; |     BDRVQcow2State *s = bs->opaque; | ||||||
| @@ -1205,6 +1205,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|  |                       Error **errp) | ||||||
|  | { | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return qcow2_do_open(bs, options, flags, errp); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) | static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) | ||||||
| { | { | ||||||
|     BDRVQcow2State *s = bs->opaque; |     BDRVQcow2State *s = bs->opaque; | ||||||
| @@ -1785,7 +1797,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) | |||||||
|     options = qdict_clone_shallow(bs->options); |     options = qdict_clone_shallow(bs->options); | ||||||
|  |  | ||||||
|     flags &= ~BDRV_O_INACTIVE; |     flags &= ~BDRV_O_INACTIVE; | ||||||
|     ret = qcow2_open(bs, options, flags, &local_err); |     ret = qcow2_do_open(bs, options, flags, &local_err); | ||||||
|     QDECREF(options); |     QDECREF(options); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
| @@ -2570,7 +2582,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, | |||||||
|         /* align end of file to a sector boundary to ease reading with |         /* align end of file to a sector boundary to ease reading with | ||||||
|            sector based I/Os */ |            sector based I/Os */ | ||||||
|         cluster_offset = bdrv_getlength(bs->file->bs); |         cluster_offset = bdrv_getlength(bs->file->bs); | ||||||
|         return bdrv_truncate(bs->file->bs, cluster_offset); |         return bdrv_truncate(bs->file, cluster_offset); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     buf = qemu_blockalign(bs, s->cluster_size); |     buf = qemu_blockalign(bs, s->cluster_size); | ||||||
| @@ -2784,7 +2796,7 @@ static int make_completely_empty(BlockDriverState *bs) | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = bdrv_truncate(bs->file->bs, (3 + l1_clusters) * s->cluster_size); |     ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
| @@ -3250,7 +3262,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (new_size) { |     if (new_size) { | ||||||
|         ret = bdrv_truncate(bs, new_size); |         BlockBackend *blk = blk_new(); | ||||||
|  |         blk_insert_bs(blk, bs); | ||||||
|  |         ret = blk_truncate(blk, new_size); | ||||||
|  |         blk_unref(blk); | ||||||
|  |  | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -83,6 +83,7 @@ static void qed_find_cluster_cb(void *opaque, int ret) | |||||||
|     unsigned int index; |     unsigned int index; | ||||||
|     unsigned int n; |     unsigned int n; | ||||||
|  |  | ||||||
|  |     qed_acquire(s); | ||||||
|     if (ret) { |     if (ret) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
| @@ -109,6 +110,7 @@ static void qed_find_cluster_cb(void *opaque, int ret) | |||||||
|  |  | ||||||
| out: | out: | ||||||
|     find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); |     find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); | ||||||
|  |     qed_release(s); | ||||||
|     g_free(find_cluster_cb); |     g_free(find_cluster_cb); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ static void qed_read_table_cb(void *opaque, int ret) | |||||||
| { | { | ||||||
|     QEDReadTableCB *read_table_cb = opaque; |     QEDReadTableCB *read_table_cb = opaque; | ||||||
|     QEDTable *table = read_table_cb->table; |     QEDTable *table = read_table_cb->table; | ||||||
|  |     BDRVQEDState *s = read_table_cb->s; | ||||||
|     int noffsets = read_table_cb->qiov.size / sizeof(uint64_t); |     int noffsets = read_table_cb->qiov.size / sizeof(uint64_t); | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -40,13 +41,15 @@ static void qed_read_table_cb(void *opaque, int ret) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Byteswap offsets */ |     /* Byteswap offsets */ | ||||||
|  |     qed_acquire(s); | ||||||
|     for (i = 0; i < noffsets; i++) { |     for (i = 0; i < noffsets; i++) { | ||||||
|         table->offsets[i] = le64_to_cpu(table->offsets[i]); |         table->offsets[i] = le64_to_cpu(table->offsets[i]); | ||||||
|     } |     } | ||||||
|  |     qed_release(s); | ||||||
|  |  | ||||||
| out: | out: | ||||||
|     /* Completion */ |     /* Completion */ | ||||||
|     trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret); |     trace_qed_read_table_cb(s, read_table_cb->table, ret); | ||||||
|     gencb_complete(&read_table_cb->gencb, ret); |     gencb_complete(&read_table_cb->gencb, ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -84,8 +87,9 @@ typedef struct { | |||||||
| static void qed_write_table_cb(void *opaque, int ret) | static void qed_write_table_cb(void *opaque, int ret) | ||||||
| { | { | ||||||
|     QEDWriteTableCB *write_table_cb = opaque; |     QEDWriteTableCB *write_table_cb = opaque; | ||||||
|  |     BDRVQEDState *s = write_table_cb->s; | ||||||
|  |  | ||||||
|     trace_qed_write_table_cb(write_table_cb->s, |     trace_qed_write_table_cb(s, | ||||||
|                              write_table_cb->orig_table, |                              write_table_cb->orig_table, | ||||||
|                              write_table_cb->flush, |                              write_table_cb->flush, | ||||||
|                              ret); |                              ret); | ||||||
| @@ -97,8 +101,10 @@ static void qed_write_table_cb(void *opaque, int ret) | |||||||
|     if (write_table_cb->flush) { |     if (write_table_cb->flush) { | ||||||
|         /* We still need to flush first */ |         /* We still need to flush first */ | ||||||
|         write_table_cb->flush = false; |         write_table_cb->flush = false; | ||||||
|  |         qed_acquire(s); | ||||||
|         bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb, |         bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb, | ||||||
|                        write_table_cb); |                        write_table_cb); | ||||||
|  |         qed_release(s); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -213,6 +219,7 @@ static void qed_read_l2_table_cb(void *opaque, int ret) | |||||||
|     CachedL2Table *l2_table = request->l2_table; |     CachedL2Table *l2_table = request->l2_table; | ||||||
|     uint64_t l2_offset = read_l2_table_cb->l2_offset; |     uint64_t l2_offset = read_l2_table_cb->l2_offset; | ||||||
|  |  | ||||||
|  |     qed_acquire(s); | ||||||
|     if (ret) { |     if (ret) { | ||||||
|         /* can't trust loaded L2 table anymore */ |         /* can't trust loaded L2 table anymore */ | ||||||
|         qed_unref_l2_cache_entry(l2_table); |         qed_unref_l2_cache_entry(l2_table); | ||||||
| @@ -228,6 +235,7 @@ static void qed_read_l2_table_cb(void *opaque, int ret) | |||||||
|         request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); |         request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); | ||||||
|         assert(request->l2_table != NULL); |         assert(request->l2_table != NULL); | ||||||
|     } |     } | ||||||
|  |     qed_release(s); | ||||||
|  |  | ||||||
|     gencb_complete(&read_l2_table_cb->gencb, ret); |     gencb_complete(&read_l2_table_cb->gencb, ret); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								block/qed.c
									
									
									
									
									
								
							| @@ -273,7 +273,19 @@ static CachedL2Table *qed_new_l2_table(BDRVQEDState *s) | |||||||
|     return l2_table; |     return l2_table; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qed_aio_next_io(void *opaque, int ret); | static void qed_aio_next_io(QEDAIOCB *acb, int ret); | ||||||
|  |  | ||||||
|  | static void qed_aio_start_io(QEDAIOCB *acb) | ||||||
|  | { | ||||||
|  |     qed_aio_next_io(acb, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void qed_aio_next_io_cb(void *opaque, int ret) | ||||||
|  | { | ||||||
|  |     QEDAIOCB *acb = opaque; | ||||||
|  |  | ||||||
|  |     qed_aio_next_io(acb, ret); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void qed_plug_allocating_write_reqs(BDRVQEDState *s) | static void qed_plug_allocating_write_reqs(BDRVQEDState *s) | ||||||
| { | { | ||||||
| @@ -292,7 +304,7 @@ static void qed_unplug_allocating_write_reqs(BDRVQEDState *s) | |||||||
|  |  | ||||||
|     acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); |     acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); | ||||||
|     if (acb) { |     if (acb) { | ||||||
|         qed_aio_next_io(acb, 0); |         qed_aio_start_io(acb); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -333,10 +345,22 @@ static void qed_need_check_timer_cb(void *opaque) | |||||||
|  |  | ||||||
|     trace_qed_need_check_timer_cb(s); |     trace_qed_need_check_timer_cb(s); | ||||||
|  |  | ||||||
|  |     qed_acquire(s); | ||||||
|     qed_plug_allocating_write_reqs(s); |     qed_plug_allocating_write_reqs(s); | ||||||
|  |  | ||||||
|     /* Ensure writes are on disk before clearing flag */ |     /* Ensure writes are on disk before clearing flag */ | ||||||
|     bdrv_aio_flush(s->bs->file->bs, qed_clear_need_check, s); |     bdrv_aio_flush(s->bs->file->bs, qed_clear_need_check, s); | ||||||
|  |     qed_release(s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void qed_acquire(BDRVQEDState *s) | ||||||
|  | { | ||||||
|  |     aio_context_acquire(bdrv_get_aio_context(s->bs)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void qed_release(BDRVQEDState *s) | ||||||
|  | { | ||||||
|  |     aio_context_release(bdrv_get_aio_context(s->bs)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qed_start_need_check_timer(BDRVQEDState *s) | static void qed_start_need_check_timer(BDRVQEDState *s) | ||||||
| @@ -391,7 +415,7 @@ static void bdrv_qed_drain(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                             Error **errp) |                             Error **errp) | ||||||
| { | { | ||||||
|     BDRVQEDState *s = bs->opaque; |     BDRVQEDState *s = bs->opaque; | ||||||
| @@ -526,6 +550,18 @@ out: | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|  |                          Error **errp) | ||||||
|  | { | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return bdrv_qed_do_open(bs, options, flags, errp); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp) | static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp) | ||||||
| { | { | ||||||
|     BDRVQEDState *s = bs->opaque; |     BDRVQEDState *s = bs->opaque; | ||||||
| @@ -721,7 +757,7 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (cb->co) { |     if (cb->co) { | ||||||
|         qemu_coroutine_enter(cb->co); |         aio_co_wake(cb->co); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -918,6 +954,7 @@ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index, | |||||||
| static void qed_aio_complete_bh(void *opaque) | static void qed_aio_complete_bh(void *opaque) | ||||||
| { | { | ||||||
|     QEDAIOCB *acb = opaque; |     QEDAIOCB *acb = opaque; | ||||||
|  |     BDRVQEDState *s = acb_to_s(acb); | ||||||
|     BlockCompletionFunc *cb = acb->common.cb; |     BlockCompletionFunc *cb = acb->common.cb; | ||||||
|     void *user_opaque = acb->common.opaque; |     void *user_opaque = acb->common.opaque; | ||||||
|     int ret = acb->bh_ret; |     int ret = acb->bh_ret; | ||||||
| @@ -925,7 +962,9 @@ static void qed_aio_complete_bh(void *opaque) | |||||||
|     qemu_aio_unref(acb); |     qemu_aio_unref(acb); | ||||||
|  |  | ||||||
|     /* Invoke callback */ |     /* Invoke callback */ | ||||||
|  |     qed_acquire(s); | ||||||
|     cb(user_opaque, ret); |     cb(user_opaque, ret); | ||||||
|  |     qed_release(s); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qed_aio_complete(QEDAIOCB *acb, int ret) | static void qed_aio_complete(QEDAIOCB *acb, int ret) | ||||||
| @@ -959,7 +998,7 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret) | |||||||
|         QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next); |         QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next); | ||||||
|         acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); |         acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); | ||||||
|         if (acb) { |         if (acb) { | ||||||
|             qed_aio_next_io(acb, 0); |             qed_aio_start_io(acb); | ||||||
|         } else if (s->header.features & QED_F_NEED_CHECK) { |         } else if (s->header.features & QED_F_NEED_CHECK) { | ||||||
|             qed_start_need_check_timer(s); |             qed_start_need_check_timer(s); | ||||||
|         } |         } | ||||||
| @@ -984,7 +1023,7 @@ static void qed_commit_l2_update(void *opaque, int ret) | |||||||
|     acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); |     acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); | ||||||
|     assert(acb->request.l2_table != NULL); |     assert(acb->request.l2_table != NULL); | ||||||
|  |  | ||||||
|     qed_aio_next_io(opaque, ret); |     qed_aio_next_io(acb, ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -1036,7 +1075,7 @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset) | |||||||
|     } else { |     } else { | ||||||
|         /* Write out only the updated part of the L2 table */ |         /* Write out only the updated part of the L2 table */ | ||||||
|         qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false, |         qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false, | ||||||
|                             qed_aio_next_io, acb); |                            qed_aio_next_io_cb, acb); | ||||||
|     } |     } | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
| @@ -1088,7 +1127,7 @@ static void qed_aio_write_main(void *opaque, int ret) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (acb->find_cluster_ret == QED_CLUSTER_FOUND) { |     if (acb->find_cluster_ret == QED_CLUSTER_FOUND) { | ||||||
|         next_fn = qed_aio_next_io; |         next_fn = qed_aio_next_io_cb; | ||||||
|     } else { |     } else { | ||||||
|         if (s->bs->backing) { |         if (s->bs->backing) { | ||||||
|             next_fn = qed_aio_write_flush_before_l2_update; |             next_fn = qed_aio_write_flush_before_l2_update; | ||||||
| @@ -1201,7 +1240,7 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) | |||||||
|     if (acb->flags & QED_AIOCB_ZERO) { |     if (acb->flags & QED_AIOCB_ZERO) { | ||||||
|         /* Skip ahead if the clusters are already zero */ |         /* Skip ahead if the clusters are already zero */ | ||||||
|         if (acb->find_cluster_ret == QED_CLUSTER_ZERO) { |         if (acb->find_cluster_ret == QED_CLUSTER_ZERO) { | ||||||
|             qed_aio_next_io(acb, 0); |             qed_aio_start_io(acb); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -1321,18 +1360,18 @@ static void qed_aio_read_data(void *opaque, int ret, | |||||||
|     /* Handle zero cluster and backing file reads */ |     /* Handle zero cluster and backing file reads */ | ||||||
|     if (ret == QED_CLUSTER_ZERO) { |     if (ret == QED_CLUSTER_ZERO) { | ||||||
|         qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size); |         qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size); | ||||||
|         qed_aio_next_io(acb, 0); |         qed_aio_start_io(acb); | ||||||
|         return; |         return; | ||||||
|     } else if (ret != QED_CLUSTER_FOUND) { |     } else if (ret != QED_CLUSTER_FOUND) { | ||||||
|         qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov, |         qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov, | ||||||
|                               &acb->backing_qiov, qed_aio_next_io, acb); |                               &acb->backing_qiov, qed_aio_next_io_cb, acb); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); |     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||||
|     bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, |     bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, | ||||||
|                    &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, |                    &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, | ||||||
|                    qed_aio_next_io, acb); |                    qed_aio_next_io_cb, acb); | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
| err: | err: | ||||||
| @@ -1342,9 +1381,8 @@ err: | |||||||
| /** | /** | ||||||
|  * Begin next I/O or complete the request |  * Begin next I/O or complete the request | ||||||
|  */ |  */ | ||||||
| static void qed_aio_next_io(void *opaque, int ret) | static void qed_aio_next_io(QEDAIOCB *acb, int ret) | ||||||
| { | { | ||||||
|     QEDAIOCB *acb = opaque; |  | ||||||
|     BDRVQEDState *s = acb_to_s(acb); |     BDRVQEDState *s = acb_to_s(acb); | ||||||
|     QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ? |     QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ? | ||||||
|                                 qed_aio_write_data : qed_aio_read_data; |                                 qed_aio_write_data : qed_aio_read_data; | ||||||
| @@ -1400,7 +1438,7 @@ static BlockAIOCB *qed_aio_setup(BlockDriverState *bs, | |||||||
|     qemu_iovec_init(&acb->cur_qiov, qiov->niov); |     qemu_iovec_init(&acb->cur_qiov, qiov->niov); | ||||||
|  |  | ||||||
|     /* Start request */ |     /* Start request */ | ||||||
|     qed_aio_next_io(acb, 0); |     qed_aio_start_io(acb); | ||||||
|     return &acb->common; |     return &acb->common; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1436,7 +1474,7 @@ static void coroutine_fn qed_co_pwrite_zeroes_cb(void *opaque, int ret) | |||||||
|     cb->done = true; |     cb->done = true; | ||||||
|     cb->ret = ret; |     cb->ret = ret; | ||||||
|     if (cb->co) { |     if (cb->co) { | ||||||
|         qemu_coroutine_enter(cb->co); |         aio_co_wake(cb->co); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1603,7 +1641,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) | |||||||
|     bdrv_qed_close(bs); |     bdrv_qed_close(bs); | ||||||
|  |  | ||||||
|     memset(s, 0, sizeof(BDRVQEDState)); |     memset(s, 0, sizeof(BDRVQEDState)); | ||||||
|     ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err); |     ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
|         error_prepend(errp, "Could not reopen qed layer: "); |         error_prepend(errp, "Could not reopen qed layer: "); | ||||||
|   | |||||||
| @@ -198,6 +198,9 @@ enum { | |||||||
|  */ |  */ | ||||||
| typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len); | typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len); | ||||||
|  |  | ||||||
|  | void qed_acquire(BDRVQEDState *s); | ||||||
|  | void qed_release(BDRVQEDState *s); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Generic callback for chaining async callbacks |  * Generic callback for chaining async callbacks | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -341,7 +341,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset) | |||||||
|  |  | ||||||
|     s->size = offset; |     s->size = offset; | ||||||
|     offset += s->offset; |     offset += s->offset; | ||||||
|     return bdrv_truncate(bs->file->bs, offset); |     return bdrv_truncate(bs->file, offset); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int raw_media_changed(BlockDriverState *bs) | static int raw_media_changed(BlockDriverState *bs) | ||||||
| @@ -384,6 +384,12 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bs->sg = bs->file->bs->sg; |     bs->sg = bs->file->bs->sg; | ||||||
|     bs->supported_write_flags = BDRV_REQ_FUA & |     bs->supported_write_flags = BDRV_REQ_FUA & | ||||||
|         bs->file->bs->supported_write_flags; |         bs->file->bs->supported_write_flags; | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								block/rbd.c
									
									
									
									
									
								
							| @@ -62,6 +62,13 @@ | |||||||
| #define RBD_MAX_SNAP_NAME_SIZE 128 | #define RBD_MAX_SNAP_NAME_SIZE 128 | ||||||
| #define RBD_MAX_SNAPS 100 | #define RBD_MAX_SNAPS 100 | ||||||
|  |  | ||||||
|  | /* The LIBRBD_SUPPORTS_IOVEC is defined in librbd.h */ | ||||||
|  | #ifdef LIBRBD_SUPPORTS_IOVEC | ||||||
|  | #define LIBRBD_USE_IOVEC 1 | ||||||
|  | #else | ||||||
|  | #define LIBRBD_USE_IOVEC 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     RBD_AIO_READ, |     RBD_AIO_READ, | ||||||
|     RBD_AIO_WRITE, |     RBD_AIO_WRITE, | ||||||
| @@ -310,6 +317,17 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf, | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) | ||||||
|  | { | ||||||
|  |     if (LIBRBD_USE_IOVEC) { | ||||||
|  |         RBDAIOCB *acb = rcb->acb; | ||||||
|  |         iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0, | ||||||
|  |                    acb->qiov->size - offs); | ||||||
|  |     } else { | ||||||
|  |         memset(rcb->buf + offs, 0, rcb->size - offs); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) | static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) | ||||||
| { | { | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
| @@ -426,11 +444,11 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb) | |||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         if (r < 0) { |         if (r < 0) { | ||||||
|             memset(rcb->buf, 0, rcb->size); |             qemu_rbd_memset(rcb, 0); | ||||||
|             acb->ret = r; |             acb->ret = r; | ||||||
|             acb->error = 1; |             acb->error = 1; | ||||||
|         } else if (r < rcb->size) { |         } else if (r < rcb->size) { | ||||||
|             memset(rcb->buf + r, 0, rcb->size - r); |             qemu_rbd_memset(rcb, r); | ||||||
|             if (!acb->error) { |             if (!acb->error) { | ||||||
|                 acb->ret = rcb->size; |                 acb->ret = rcb->size; | ||||||
|             } |             } | ||||||
| @@ -441,10 +459,13 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb) | |||||||
|  |  | ||||||
|     g_free(rcb); |     g_free(rcb); | ||||||
|  |  | ||||||
|  |     if (!LIBRBD_USE_IOVEC) { | ||||||
|         if (acb->cmd == RBD_AIO_READ) { |         if (acb->cmd == RBD_AIO_READ) { | ||||||
|             qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size); |             qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size); | ||||||
|         } |         } | ||||||
|         qemu_vfree(acb->bounce); |         qemu_vfree(acb->bounce); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); |     acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); | ||||||
|  |  | ||||||
|     qemu_aio_unref(acb); |     qemu_aio_unref(acb); | ||||||
| @@ -655,7 +676,6 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | |||||||
|     RBDAIOCB *acb; |     RBDAIOCB *acb; | ||||||
|     RADOSCB *rcb = NULL; |     RADOSCB *rcb = NULL; | ||||||
|     rbd_completion_t c; |     rbd_completion_t c; | ||||||
|     char *buf; |  | ||||||
|     int r; |     int r; | ||||||
|  |  | ||||||
|     BDRVRBDState *s = bs->opaque; |     BDRVRBDState *s = bs->opaque; | ||||||
| @@ -664,6 +684,10 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | |||||||
|     acb->cmd = cmd; |     acb->cmd = cmd; | ||||||
|     acb->qiov = qiov; |     acb->qiov = qiov; | ||||||
|     assert(!qiov || qiov->size == size); |     assert(!qiov || qiov->size == size); | ||||||
|  |  | ||||||
|  |     rcb = g_new(RADOSCB, 1); | ||||||
|  |  | ||||||
|  |     if (!LIBRBD_USE_IOVEC) { | ||||||
|         if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { |         if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { | ||||||
|             acb->bounce = NULL; |             acb->bounce = NULL; | ||||||
|         } else { |         } else { | ||||||
| @@ -672,19 +696,17 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | |||||||
|                 goto failed; |                 goto failed; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         if (cmd == RBD_AIO_WRITE) { | ||||||
|  |             qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); | ||||||
|  |         } | ||||||
|  |         rcb->buf = acb->bounce; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     acb->ret = 0; |     acb->ret = 0; | ||||||
|     acb->error = 0; |     acb->error = 0; | ||||||
|     acb->s = s; |     acb->s = s; | ||||||
|  |  | ||||||
|     if (cmd == RBD_AIO_WRITE) { |  | ||||||
|         qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     buf = acb->bounce; |  | ||||||
|  |  | ||||||
|     rcb = g_new(RADOSCB, 1); |  | ||||||
|     rcb->acb = acb; |     rcb->acb = acb; | ||||||
|     rcb->buf = buf; |  | ||||||
|     rcb->s = acb->s; |     rcb->s = acb->s; | ||||||
|     rcb->size = size; |     rcb->size = size; | ||||||
|     r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); |     r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); | ||||||
| @@ -694,10 +716,18 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     switch (cmd) { |     switch (cmd) { | ||||||
|     case RBD_AIO_WRITE: |     case RBD_AIO_WRITE: | ||||||
|         r = rbd_aio_write(s->image, off, size, buf, c); | #ifdef LIBRBD_SUPPORTS_IOVEC | ||||||
|  |             r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c); | ||||||
|  | #else | ||||||
|  |             r = rbd_aio_write(s->image, off, size, rcb->buf, c); | ||||||
|  | #endif | ||||||
|         break; |         break; | ||||||
|     case RBD_AIO_READ: |     case RBD_AIO_READ: | ||||||
|         r = rbd_aio_read(s->image, off, size, buf, c); | #ifdef LIBRBD_SUPPORTS_IOVEC | ||||||
|  |             r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c); | ||||||
|  | #else | ||||||
|  |             r = rbd_aio_read(s->image, off, size, rcb->buf, c); | ||||||
|  | #endif | ||||||
|         break; |         break; | ||||||
|     case RBD_AIO_DISCARD: |     case RBD_AIO_DISCARD: | ||||||
|         r = rbd_aio_discard_wrapper(s->image, off, size, c); |         r = rbd_aio_discard_wrapper(s->image, off, size, c); | ||||||
| @@ -712,14 +742,16 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | |||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|         goto failed_completion; |         goto failed_completion; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return &acb->common; |     return &acb->common; | ||||||
|  |  | ||||||
| failed_completion: | failed_completion: | ||||||
|     rbd_aio_release(c); |     rbd_aio_release(c); | ||||||
| failed: | failed: | ||||||
|     g_free(rcb); |     g_free(rcb); | ||||||
|  |     if (!LIBRBD_USE_IOVEC) { | ||||||
|         qemu_vfree(acb->bounce); |         qemu_vfree(acb->bounce); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     qemu_aio_unref(acb); |     qemu_aio_unref(acb); | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -86,6 +86,12 @@ static int replication_open(BlockDriverState *bs, QDict *options, | |||||||
|     const char *mode; |     const char *mode; | ||||||
|     const char *top_id; |     const char *top_id; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ret = -EINVAL; |     ret = -EINVAL; | ||||||
|     opts = qemu_opts_create(&replication_runtime_opts, NULL, 0, &error_abort); |     opts = qemu_opts_create(&replication_runtime_opts, NULL, 0, &error_abort); | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||||
|   | |||||||
| @@ -486,7 +486,7 @@ static void wait_for_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *acb) | |||||||
| retry: | retry: | ||||||
|     QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) { |     QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) { | ||||||
|         if (AIOCBOverlapping(acb, cb)) { |         if (AIOCBOverlapping(acb, cb)) { | ||||||
|             qemu_co_queue_wait(&s->overlapping_queue); |             qemu_co_queue_wait(&s->overlapping_queue, NULL); | ||||||
|             goto retry; |             goto retry; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -575,13 +575,6 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void restart_co_req(void *opaque) |  | ||||||
| { |  | ||||||
|     Coroutine *co = opaque; |  | ||||||
|  |  | ||||||
|     qemu_coroutine_enter(co); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef struct SheepdogReqCo { | typedef struct SheepdogReqCo { | ||||||
|     int sockfd; |     int sockfd; | ||||||
|     BlockDriverState *bs; |     BlockDriverState *bs; | ||||||
| @@ -592,12 +585,19 @@ typedef struct SheepdogReqCo { | |||||||
|     unsigned int *rlen; |     unsigned int *rlen; | ||||||
|     int ret; |     int ret; | ||||||
|     bool finished; |     bool finished; | ||||||
|  |     Coroutine *co; | ||||||
| } SheepdogReqCo; | } SheepdogReqCo; | ||||||
|  |  | ||||||
|  | static void restart_co_req(void *opaque) | ||||||
|  | { | ||||||
|  |     SheepdogReqCo *srco = opaque; | ||||||
|  |  | ||||||
|  |     aio_co_wake(srco->co); | ||||||
|  | } | ||||||
|  |  | ||||||
| static coroutine_fn void do_co_req(void *opaque) | static coroutine_fn void do_co_req(void *opaque) | ||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
|     Coroutine *co; |  | ||||||
|     SheepdogReqCo *srco = opaque; |     SheepdogReqCo *srco = opaque; | ||||||
|     int sockfd = srco->sockfd; |     int sockfd = srco->sockfd; | ||||||
|     SheepdogReq *hdr = srco->hdr; |     SheepdogReq *hdr = srco->hdr; | ||||||
| @@ -605,9 +605,9 @@ static coroutine_fn void do_co_req(void *opaque) | |||||||
|     unsigned int *wlen = srco->wlen; |     unsigned int *wlen = srco->wlen; | ||||||
|     unsigned int *rlen = srco->rlen; |     unsigned int *rlen = srco->rlen; | ||||||
|  |  | ||||||
|     co = qemu_coroutine_self(); |     srco->co = qemu_coroutine_self(); | ||||||
|     aio_set_fd_handler(srco->aio_context, sockfd, false, |     aio_set_fd_handler(srco->aio_context, sockfd, false, | ||||||
|                        NULL, restart_co_req, NULL, co); |                        NULL, restart_co_req, NULL, srco); | ||||||
|  |  | ||||||
|     ret = send_co_req(sockfd, hdr, data, wlen); |     ret = send_co_req(sockfd, hdr, data, wlen); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
| @@ -615,7 +615,7 @@ static coroutine_fn void do_co_req(void *opaque) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     aio_set_fd_handler(srco->aio_context, sockfd, false, |     aio_set_fd_handler(srco->aio_context, sockfd, false, | ||||||
|                        restart_co_req, NULL, NULL, co); |                        restart_co_req, NULL, NULL, srco); | ||||||
|  |  | ||||||
|     ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); |     ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); | ||||||
|     if (ret != sizeof(*hdr)) { |     if (ret != sizeof(*hdr)) { | ||||||
| @@ -643,6 +643,7 @@ out: | |||||||
|     aio_set_fd_handler(srco->aio_context, sockfd, false, |     aio_set_fd_handler(srco->aio_context, sockfd, false, | ||||||
|                        NULL, NULL, NULL, NULL); |                        NULL, NULL, NULL, NULL); | ||||||
|  |  | ||||||
|  |     srco->co = NULL; | ||||||
|     srco->ret = ret; |     srco->ret = ret; | ||||||
|     srco->finished = true; |     srco->finished = true; | ||||||
|     if (srco->bs) { |     if (srco->bs) { | ||||||
| @@ -866,7 +867,7 @@ static void coroutine_fn aio_read_response(void *opaque) | |||||||
|          * We've finished all requests which belong to the AIOCB, so |          * We've finished all requests which belong to the AIOCB, so | ||||||
|          * we can switch back to sd_co_readv/writev now. |          * we can switch back to sd_co_readv/writev now. | ||||||
|          */ |          */ | ||||||
|         qemu_coroutine_enter(acb->coroutine); |         aio_co_wake(acb->coroutine); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
| @@ -883,14 +884,14 @@ static void co_read_response(void *opaque) | |||||||
|         s->co_recv = qemu_coroutine_create(aio_read_response, opaque); |         s->co_recv = qemu_coroutine_create(aio_read_response, opaque); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qemu_coroutine_enter(s->co_recv); |     aio_co_wake(s->co_recv); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void co_write_request(void *opaque) | static void co_write_request(void *opaque) | ||||||
| { | { | ||||||
|     BDRVSheepdogState *s = opaque; |     BDRVSheepdogState *s = opaque; | ||||||
|  |  | ||||||
|     qemu_coroutine_enter(s->co_send); |     aio_co_wake(s->co_send); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								block/ssh.c
									
									
									
									
									
								
							| @@ -889,10 +889,14 @@ static void restart_coroutine(void *opaque) | |||||||
|  |  | ||||||
|     DPRINTF("co=%p", co); |     DPRINTF("co=%p", co); | ||||||
|  |  | ||||||
|     qemu_coroutine_enter(co); |     aio_co_wake(co); | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs) | /* A non-blocking call returned EAGAIN, so yield, ensuring the | ||||||
|  |  * handlers are set up so that we'll be rescheduled when there is an | ||||||
|  |  * interesting event on the socket. | ||||||
|  |  */ | ||||||
|  | static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) | ||||||
| { | { | ||||||
|     int r; |     int r; | ||||||
|     IOHandler *rd_handler = NULL, *wr_handler = NULL; |     IOHandler *rd_handler = NULL, *wr_handler = NULL; | ||||||
| @@ -912,25 +916,10 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs) | |||||||
|  |  | ||||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, |     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, | ||||||
|                        false, rd_handler, wr_handler, NULL, co); |                        false, rd_handler, wr_handler, NULL, co); | ||||||
| } |  | ||||||
|  |  | ||||||
| static coroutine_fn void clear_fd_handler(BDRVSSHState *s, |  | ||||||
|                                           BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     DPRINTF("s->sock=%d", s->sock); |  | ||||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, |  | ||||||
|                        false, NULL, NULL, NULL, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* A non-blocking call returned EAGAIN, so yield, ensuring the |  | ||||||
|  * handlers are set up so that we'll be rescheduled when there is an |  | ||||||
|  * interesting event on the socket. |  | ||||||
|  */ |  | ||||||
| static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     set_fd_handler(s, bs); |  | ||||||
|     qemu_coroutine_yield(); |     qemu_coroutine_yield(); | ||||||
|     clear_fd_handler(s, bs); |     DPRINTF("s->sock=%d - back", s->sock); | ||||||
|  |     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, false, | ||||||
|  |                        NULL, NULL, NULL, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* SFTP has a function `libssh2_sftp_seek64' which seeks to a position | /* SFTP has a function `libssh2_sftp_seek64' which seeks to a position | ||||||
|   | |||||||
| @@ -326,7 +326,7 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk, | |||||||
|     if (must_wait || blkp->pending_reqs[is_write]) { |     if (must_wait || blkp->pending_reqs[is_write]) { | ||||||
|         blkp->pending_reqs[is_write]++; |         blkp->pending_reqs[is_write]++; | ||||||
|         qemu_mutex_unlock(&tg->lock); |         qemu_mutex_unlock(&tg->lock); | ||||||
|         qemu_co_queue_wait(&blkp->throttled_reqs[is_write]); |         qemu_co_queue_wait(&blkp->throttled_reqs[is_write], NULL); | ||||||
|         qemu_mutex_lock(&tg->lock); |         qemu_mutex_lock(&tg->lock); | ||||||
|         blkp->pending_reqs[is_write]--; |         blkp->pending_reqs[is_write]--; | ||||||
|     } |     } | ||||||
| @@ -416,7 +416,9 @@ static void timer_cb(BlockBackend *blk, bool is_write) | |||||||
|     qemu_mutex_unlock(&tg->lock); |     qemu_mutex_unlock(&tg->lock); | ||||||
|  |  | ||||||
|     /* Run the request that was waiting for this timer */ |     /* 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]); |     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 |     /* If the request queue was empty then we have to take care of | ||||||
|      * scheduling the next one */ |      * scheduling the next one */ | ||||||
|   | |||||||
| @@ -363,6 +363,12 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     int ret; |     int ret; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     logout("\n"); |     logout("\n"); | ||||||
|  |  | ||||||
|     ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); |     ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); | ||||||
|   | |||||||
| @@ -548,7 +548,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, | |||||||
|             if (new_file_size % (1024*1024)) { |             if (new_file_size % (1024*1024)) { | ||||||
|                 /* round up to nearest 1MB boundary */ |                 /* round up to nearest 1MB boundary */ | ||||||
|                 new_file_size = ((new_file_size >> 20) + 1) << 20; |                 new_file_size = ((new_file_size >> 20) + 1) << 20; | ||||||
|                 bdrv_truncate(bs->file->bs, new_file_size); |                 bdrv_truncate(bs->file, new_file_size); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         qemu_vfree(desc_entries); |         qemu_vfree(desc_entries); | ||||||
|   | |||||||
| @@ -898,6 +898,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     uint64_t signature; |     uint64_t signature; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     s->bat = NULL; |     s->bat = NULL; | ||||||
|     s->first_visible_write = true; |     s->first_visible_write = true; | ||||||
|  |  | ||||||
| @@ -1165,7 +1171,7 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, | |||||||
|     /* per the spec, the address for a block is in units of 1MB */ |     /* per the spec, the address for a block is in units of 1MB */ | ||||||
|     *new_offset = ROUND_UP(*new_offset, 1024 * 1024); |     *new_offset = ROUND_UP(*new_offset, 1024 * 1024); | ||||||
|  |  | ||||||
|     return bdrv_truncate(bs->file->bs, *new_offset + s->block_size); |     return bdrv_truncate(bs->file, *new_offset + s->block_size); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
| @@ -943,6 +943,12 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     uint32_t magic; |     uint32_t magic; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     buf = vmdk_read_desc(bs->file, 0, errp); |     buf = vmdk_read_desc(bs->file, 0, errp); | ||||||
|     if (!buf) { |     if (!buf) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|   | |||||||
| @@ -220,6 +220,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     int disk_type = VHD_DYNAMIC; |     int disk_type = VHD_DYNAMIC; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|  |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|  |                                false, errp); | ||||||
|  |     if (!bs->file) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); |     opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|   | |||||||
| @@ -2968,6 +2968,7 @@ static void write_target_close(BlockDriverState *bs) { | |||||||
|  |  | ||||||
| static BlockDriver vvfat_write_target = { | static BlockDriver vvfat_write_target = { | ||||||
|     .format_name        = "vvfat_write_target", |     .format_name        = "vvfat_write_target", | ||||||
|  |     .instance_size      = sizeof(void*), | ||||||
|     .bdrv_co_pwritev    = write_target_commit, |     .bdrv_co_pwritev    = write_target_commit, | ||||||
|     .bdrv_close         = write_target_close, |     .bdrv_close         = write_target_close, | ||||||
| }; | }; | ||||||
| @@ -3036,14 +3037,13 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) | |||||||
|     unlink(s->qcow_filename); |     unlink(s->qcow_filename); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     backing = bdrv_new(); |     backing = bdrv_new_open_driver(&vvfat_write_target, NULL, BDRV_O_ALLOW_RDWR, | ||||||
|  |                                    &error_abort); | ||||||
|  |     *(void**) backing->opaque = s; | ||||||
|  |  | ||||||
|     bdrv_set_backing_hd(s->bs, backing); |     bdrv_set_backing_hd(s->bs, backing); | ||||||
|     bdrv_unref(backing); |     bdrv_unref(backing); | ||||||
|  |  | ||||||
|     s->bs->backing->bs->drv = &vvfat_write_target; |  | ||||||
|     s->bs->backing->bs->opaque = g_new(void *, 1); |  | ||||||
|     *(void**)s->bs->backing->bs->opaque = s; |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| err: | err: | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ struct QEMUWin32AIOState { | |||||||
|     HANDLE hIOCP; |     HANDLE hIOCP; | ||||||
|     EventNotifier e; |     EventNotifier e; | ||||||
|     int count; |     int count; | ||||||
|     bool is_aio_context_attached; |     AioContext *aio_ctx; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct QEMUWin32AIOCB { | typedef struct QEMUWin32AIOCB { | ||||||
| @@ -87,7 +87,6 @@ static void win32_aio_process_completion(QEMUWin32AIOState *s, | |||||||
|         qemu_vfree(waiocb->buf); |         qemu_vfree(waiocb->buf); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     waiocb->common.cb(waiocb->common.opaque, ret); |     waiocb->common.cb(waiocb->common.opaque, ret); | ||||||
|     qemu_aio_unref(waiocb); |     qemu_aio_unref(waiocb); | ||||||
| } | } | ||||||
| @@ -176,13 +175,13 @@ void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, | |||||||
|                                   AioContext *old_context) |                                   AioContext *old_context) | ||||||
| { | { | ||||||
|     aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL); |     aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL); | ||||||
|     aio->is_aio_context_attached = false; |     aio->aio_ctx = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, | void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, | ||||||
|                                   AioContext *new_context) |                                   AioContext *new_context) | ||||||
| { | { | ||||||
|     aio->is_aio_context_attached = true; |     aio->aio_ctx = new_context; | ||||||
|     aio_set_event_notifier(new_context, &aio->e, false, |     aio_set_event_notifier(new_context, &aio->e, false, | ||||||
|                            win32_aio_completion_cb, NULL); |                            win32_aio_completion_cb, NULL); | ||||||
| } | } | ||||||
| @@ -212,7 +211,7 @@ out_free_state: | |||||||
|  |  | ||||||
| void win32_aio_cleanup(QEMUWin32AIOState *aio) | void win32_aio_cleanup(QEMUWin32AIOState *aio) | ||||||
| { | { | ||||||
|     assert(!aio->is_aio_context_attached); |     assert(!aio->aio_ctx); | ||||||
|     CloseHandle(aio->hIOCP); |     CloseHandle(aio->hIOCP); | ||||||
|     event_notifier_cleanup(&aio->e); |     event_notifier_cleanup(&aio->e); | ||||||
|     g_free(aio); |     g_free(aio); | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								blockdev.c
									
									
									
									
									
								
							| @@ -52,6 +52,7 @@ | |||||||
| #include "sysemu/arch_init.h" | #include "sysemu/arch_init.h" | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
| #include "qemu/help_option.h" | #include "qemu/help_option.h" | ||||||
|  | #include "qemu/throttle-options.h" | ||||||
|  |  | ||||||
| static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = | static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = | ||||||
|     QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); |     QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); | ||||||
| @@ -227,27 +228,30 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool drive_check_orphaned(void) | void drive_check_orphaned(void) | ||||||
| { | { | ||||||
|     BlockBackend *blk; |     BlockBackend *blk; | ||||||
|     DriveInfo *dinfo; |     DriveInfo *dinfo; | ||||||
|     bool rs = false; |     Location loc; | ||||||
|  |     bool orphans = false; | ||||||
|  |  | ||||||
|     for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { |     for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { | ||||||
|         dinfo = blk_legacy_dinfo(blk); |         dinfo = blk_legacy_dinfo(blk); | ||||||
|         /* If dinfo->bdrv->dev is NULL, it has no device attached. */ |  | ||||||
|         /* Unless this is a default drive, this may be an oversight. */ |  | ||||||
|         if (!blk_get_attached_dev(blk) && !dinfo->is_default && |         if (!blk_get_attached_dev(blk) && !dinfo->is_default && | ||||||
|             dinfo->type != IF_NONE) { |             dinfo->type != IF_NONE) { | ||||||
|             fprintf(stderr, "Warning: Orphaned drive without device: " |             loc_push_none(&loc); | ||||||
|                     "id=%s,file=%s,if=%s,bus=%d,unit=%d\n", |             qemu_opts_loc_restore(dinfo->opts); | ||||||
|                     blk_name(blk), blk_bs(blk) ? blk_bs(blk)->filename : "", |             error_report("machine type does not support" | ||||||
|  |                          " if=%s,bus=%d,unit=%d", | ||||||
|                          if_name[dinfo->type], dinfo->bus, dinfo->unit); |                          if_name[dinfo->type], dinfo->bus, dinfo->unit); | ||||||
|             rs = true; |             loc_pop(&loc); | ||||||
|  |             orphans = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return rs; |     if (orphans) { | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) | DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) | ||||||
| @@ -2855,6 +2859,7 @@ void qmp_block_resize(bool has_device, const char *device, | |||||||
|                       int64_t size, Error **errp) |                       int64_t size, Error **errp) | ||||||
| { | { | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |     BlockBackend *blk = NULL; | ||||||
|     BlockDriverState *bs; |     BlockDriverState *bs; | ||||||
|     AioContext *aio_context; |     AioContext *aio_context; | ||||||
|     int ret; |     int ret; | ||||||
| @@ -2885,10 +2890,13 @@ void qmp_block_resize(bool has_device, const char *device, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     blk = blk_new(); | ||||||
|  |     blk_insert_bs(blk, bs); | ||||||
|  |  | ||||||
|     /* complete all in-flight operations before resizing the device */ |     /* complete all in-flight operations before resizing the device */ | ||||||
|     bdrv_drain_all(); |     bdrv_drain_all(); | ||||||
|  |  | ||||||
|     ret = bdrv_truncate(bs, size); |     ret = blk_truncate(blk, size); | ||||||
|     switch (ret) { |     switch (ret) { | ||||||
|     case 0: |     case 0: | ||||||
|         break; |         break; | ||||||
| @@ -2910,6 +2918,7 @@ void qmp_block_resize(bool has_device, const char *device, | |||||||
|     } |     } | ||||||
|  |  | ||||||
| out: | out: | ||||||
|  |     blk_unref(blk); | ||||||
|     aio_context_release(aio_context); |     aio_context_release(aio_context); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -3999,83 +4008,11 @@ QemuOptsList qemu_common_drive_opts = { | |||||||
|             .name = BDRV_OPT_READ_ONLY, |             .name = BDRV_OPT_READ_ONLY, | ||||||
|             .type = QEMU_OPT_BOOL, |             .type = QEMU_OPT_BOOL, | ||||||
|             .help = "open drive file as read-only", |             .help = "open drive file as read-only", | ||||||
|         },{ |         }, | ||||||
|             .name = "throttling.iops-total", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |         THROTTLE_OPTS, | ||||||
|             .help = "limit total I/O operations per second", |  | ||||||
|         },{ |         { | ||||||
|             .name = "throttling.iops-read", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "limit read operations per second", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-write", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "limit write operations per second", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-total", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "limit total bytes per second", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-read", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "limit read bytes per second", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-write", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "limit write bytes per second", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-total-max", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "I/O operations burst", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-read-max", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "I/O operations read burst", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-write-max", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "I/O operations write burst", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-total-max", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "total bytes burst", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-read-max", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "total bytes read burst", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-write-max", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "total bytes write burst", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-total-max-length", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "length of the iops-total-max burst period, in seconds", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-read-max-length", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "length of the iops-read-max burst period, in seconds", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-write-max-length", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "length of the iops-write-max burst period, in seconds", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-total-max-length", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "length of the bps-total-max burst period, in seconds", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-read-max-length", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "length of the bps-read-max burst period, in seconds", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.bps-write-max-length", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "length of the bps-write-max burst period, in seconds", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.iops-size", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "when limiting by iops max size of an I/O in bytes", |  | ||||||
|         },{ |  | ||||||
|             .name = "throttling.group", |             .name = "throttling.group", | ||||||
|             .type = QEMU_OPT_STRING, |             .type = QEMU_OPT_STRING, | ||||||
|             .help = "name of the block throttling group", |             .help = "name of the block throttling group", | ||||||
|   | |||||||
| @@ -652,6 +652,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) | |||||||
|     if (strcmp(filename, "null")    == 0 || |     if (strcmp(filename, "null")    == 0 || | ||||||
|         strcmp(filename, "pty")     == 0 || |         strcmp(filename, "pty")     == 0 || | ||||||
|         strcmp(filename, "msmouse") == 0 || |         strcmp(filename, "msmouse") == 0 || | ||||||
|  |         strcmp(filename, "wctablet") == 0 || | ||||||
|         strcmp(filename, "braille") == 0 || |         strcmp(filename, "braille") == 0 || | ||||||
|         strcmp(filename, "testdev") == 0 || |         strcmp(filename, "testdev") == 0 || | ||||||
|         strcmp(filename, "stdio")   == 0) { |         strcmp(filename, "stdio")   == 0) { | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								configure
									
									
									
									
										vendored
									
									
								
							| @@ -3378,7 +3378,7 @@ fi | |||||||
| fdt_required=no | fdt_required=no | ||||||
| for target in $target_list; do | for target in $target_list; do | ||||||
|   case $target in |   case $target in | ||||||
|     aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu) |     aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) | ||||||
|       fdt_required=yes |       fdt_required=yes | ||||||
|     ;; |     ;; | ||||||
|   esac |   esac | ||||||
| @@ -3396,11 +3396,11 @@ fi | |||||||
| if test "$fdt" != "no" ; then | if test "$fdt" != "no" ; then | ||||||
|   fdt_libs="-lfdt" |   fdt_libs="-lfdt" | ||||||
|   # explicitly check for libfdt_env.h as it is missing in some stable installs |   # explicitly check for libfdt_env.h as it is missing in some stable installs | ||||||
|   # and test for required functions to make sure we are on a version >= 1.4.0 |   # and test for required functions to make sure we are on a version >= 1.4.2 | ||||||
|   cat > $TMPC << EOF |   cat > $TMPC << EOF | ||||||
| #include <libfdt.h> | #include <libfdt.h> | ||||||
| #include <libfdt_env.h> | #include <libfdt_env.h> | ||||||
| int main(void) { fdt_get_property_by_offset(0, 0, 0); return 0; } | int main(void) { fdt_first_subnode(0, 0); return 0; } | ||||||
| EOF | EOF | ||||||
|   if compile_prog "" "$fdt_libs" ; then |   if compile_prog "" "$fdt_libs" ; then | ||||||
|     # system DTC is good - use it |     # system DTC is good - use it | ||||||
| @@ -3418,7 +3418,7 @@ EOF | |||||||
|     fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" |     fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" | ||||||
|   elif test "$fdt" = "yes" ; then |   elif test "$fdt" = "yes" ; then | ||||||
|     # have neither and want - prompt for system/submodule install |     # have neither and want - prompt for system/submodule install | ||||||
|     error_exit "DTC (libfdt) version >= 1.4.0 not present. Your options:" \ |     error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" \ | ||||||
|         "  (1) Preferred: Install the DTC (libfdt) devel package" \ |         "  (1) Preferred: Install the DTC (libfdt) devel package" \ | ||||||
|         "  (2) Fetch the DTC submodule, using:" \ |         "  (2) Fetch the DTC submodule, using:" \ | ||||||
|         "      git submodule update --init dtc" |         "      git submodule update --init dtc" | ||||||
| @@ -5879,6 +5879,7 @@ mkdir -p $target_dir | |||||||
| echo "# Automatically generated by configure - do not modify" > $config_target_mak | echo "# Automatically generated by configure - do not modify" > $config_target_mak | ||||||
|  |  | ||||||
| bflt="no" | bflt="no" | ||||||
|  | mttcg="no" | ||||||
| interp_prefix1=$(echo "$interp_prefix" | sed "s/%M/$target_name/g") | interp_prefix1=$(echo "$interp_prefix" | sed "s/%M/$target_name/g") | ||||||
| gdb_xml_files="" | gdb_xml_files="" | ||||||
|  |  | ||||||
| @@ -5893,15 +5894,18 @@ case "$target_name" in | |||||||
|     TARGET_BASE_ARCH=i386 |     TARGET_BASE_ARCH=i386 | ||||||
|   ;; |   ;; | ||||||
|   alpha) |   alpha) | ||||||
|  |     mttcg="yes" | ||||||
|   ;; |   ;; | ||||||
|   arm|armeb) |   arm|armeb) | ||||||
|     TARGET_ARCH=arm |     TARGET_ARCH=arm | ||||||
|     bflt="yes" |     bflt="yes" | ||||||
|  |     mttcg="yes" | ||||||
|     gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" |     gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" | ||||||
|   ;; |   ;; | ||||||
|   aarch64) |   aarch64) | ||||||
|     TARGET_BASE_ARCH=arm |     TARGET_BASE_ARCH=arm | ||||||
|     bflt="yes" |     bflt="yes" | ||||||
|  |     mttcg="yes" | ||||||
|     gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" |     gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" | ||||||
|   ;; |   ;; | ||||||
|   cris) |   cris) | ||||||
| @@ -6066,6 +6070,9 @@ if test "$target_bigendian" = "yes" ; then | |||||||
| fi | fi | ||||||
| if test "$target_softmmu" = "yes" ; then | if test "$target_softmmu" = "yes" ; then | ||||||
|   echo "CONFIG_SOFTMMU=y" >> $config_target_mak |   echo "CONFIG_SOFTMMU=y" >> $config_target_mak | ||||||
|  |   if test "$mttcg" = "yes" ; then | ||||||
|  |     echo "TARGET_SUPPORTS_MTTCG=y" >> $config_target_mak | ||||||
|  |   fi | ||||||
| fi | fi | ||||||
| if test "$target_user_only" = "yes" ; then | if test "$target_user_only" = "yes" ; then | ||||||
|   echo "CONFIG_USER_ONLY=y" >> $config_target_mak |   echo "CONFIG_USER_ONLY=y" >> $config_target_mak | ||||||
|   | |||||||
| @@ -23,9 +23,6 @@ | |||||||
| #include "exec/exec-all.h" | #include "exec/exec-all.h" | ||||||
| #include "exec/memory-internal.h" | #include "exec/memory-internal.h" | ||||||
|  |  | ||||||
| bool exit_request; |  | ||||||
| CPUState *tcg_current_cpu; |  | ||||||
|  |  | ||||||
| /* exit the current TB, but without causing any exception to be raised */ | /* exit the current TB, but without causing any exception to be raised */ | ||||||
| void cpu_loop_exit_noexc(CPUState *cpu) | void cpu_loop_exit_noexc(CPUState *cpu) | ||||||
| { | { | ||||||
|   | |||||||
							
								
								
									
										73
									
								
								cpu-exec.c
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								cpu-exec.c
									
									
									
									
									
								
							| @@ -29,6 +29,7 @@ | |||||||
| #include "qemu/rcu.h" | #include "qemu/rcu.h" | ||||||
| #include "exec/tb-hash.h" | #include "exec/tb-hash.h" | ||||||
| #include "exec/log.h" | #include "exec/log.h" | ||||||
|  | #include "qemu/main-loop.h" | ||||||
| #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) | #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) | ||||||
| #include "hw/i386/apic.h" | #include "hw/i386/apic.h" | ||||||
| #endif | #endif | ||||||
| @@ -227,20 +228,43 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, | |||||||
|  |  | ||||||
| static void cpu_exec_step(CPUState *cpu) | static void cpu_exec_step(CPUState *cpu) | ||||||
| { | { | ||||||
|  |     CPUClass *cc = CPU_GET_CLASS(cpu); | ||||||
|     CPUArchState *env = (CPUArchState *)cpu->env_ptr; |     CPUArchState *env = (CPUArchState *)cpu->env_ptr; | ||||||
|     TranslationBlock *tb; |     TranslationBlock *tb; | ||||||
|     target_ulong cs_base, pc; |     target_ulong cs_base, pc; | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|  |  | ||||||
|     cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); |     cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); | ||||||
|  |     if (sigsetjmp(cpu->jmp_env, 0) == 0) { | ||||||
|  |         mmap_lock(); | ||||||
|  |         tb_lock(); | ||||||
|         tb = tb_gen_code(cpu, pc, cs_base, flags, |         tb = tb_gen_code(cpu, pc, cs_base, flags, | ||||||
|                          1 | CF_NOCACHE | CF_IGNORE_ICOUNT); |                          1 | CF_NOCACHE | CF_IGNORE_ICOUNT); | ||||||
|         tb->orig_tb = NULL; |         tb->orig_tb = NULL; | ||||||
|  |         tb_unlock(); | ||||||
|  |         mmap_unlock(); | ||||||
|  |  | ||||||
|  |         cc->cpu_exec_enter(cpu); | ||||||
|         /* execute the generated code */ |         /* execute the generated code */ | ||||||
|         trace_exec_tb_nocache(tb, pc); |         trace_exec_tb_nocache(tb, pc); | ||||||
|         cpu_tb_exec(cpu, tb); |         cpu_tb_exec(cpu, tb); | ||||||
|  |         cc->cpu_exec_exit(cpu); | ||||||
|  |  | ||||||
|  |         tb_lock(); | ||||||
|         tb_phys_invalidate(tb, -1); |         tb_phys_invalidate(tb, -1); | ||||||
|         tb_free(tb); |         tb_free(tb); | ||||||
|  |         tb_unlock(); | ||||||
|  |     } else { | ||||||
|  |         /* We may have exited due to another problem here, so we need | ||||||
|  |          * to reset any tb_locks we may have taken but didn't release. | ||||||
|  |          * The mmap_lock is dropped by tb_gen_code if it runs out of | ||||||
|  |          * memory. | ||||||
|  |          */ | ||||||
|  | #ifndef CONFIG_SOFTMMU | ||||||
|  |         tcg_debug_assert(!have_mmap_lock()); | ||||||
|  | #endif | ||||||
|  |         tb_lock_reset(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cpu_exec_step_atomic(CPUState *cpu) | void cpu_exec_step_atomic(CPUState *cpu) | ||||||
| @@ -384,12 +408,13 @@ static inline bool cpu_handle_halt(CPUState *cpu) | |||||||
|         if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) |         if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) | ||||||
|             && replay_interrupt()) { |             && replay_interrupt()) { | ||||||
|             X86CPU *x86_cpu = X86_CPU(cpu); |             X86CPU *x86_cpu = X86_CPU(cpu); | ||||||
|  |             qemu_mutex_lock_iothread(); | ||||||
|             apic_poll_irq(x86_cpu->apic_state); |             apic_poll_irq(x86_cpu->apic_state); | ||||||
|             cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); |             cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); | ||||||
|  |             qemu_mutex_unlock_iothread(); | ||||||
|         } |         } | ||||||
| #endif | #endif | ||||||
|         if (!cpu_has_work(cpu)) { |         if (!cpu_has_work(cpu)) { | ||||||
|             current_cpu = NULL; |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -439,7 +464,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) | |||||||
| #else | #else | ||||||
|             if (replay_exception()) { |             if (replay_exception()) { | ||||||
|                 CPUClass *cc = CPU_GET_CLASS(cpu); |                 CPUClass *cc = CPU_GET_CLASS(cpu); | ||||||
|  |                 qemu_mutex_lock_iothread(); | ||||||
|                 cc->do_interrupt(cpu); |                 cc->do_interrupt(cpu); | ||||||
|  |                 qemu_mutex_unlock_iothread(); | ||||||
|                 cpu->exception_index = -1; |                 cpu->exception_index = -1; | ||||||
|             } else if (!replay_has_interrupt()) { |             } else if (!replay_has_interrupt()) { | ||||||
|                 /* give a chance to iothread in replay mode */ |                 /* give a chance to iothread in replay mode */ | ||||||
| @@ -465,9 +492,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|                                         TranslationBlock **last_tb) |                                         TranslationBlock **last_tb) | ||||||
| { | { | ||||||
|     CPUClass *cc = CPU_GET_CLASS(cpu); |     CPUClass *cc = CPU_GET_CLASS(cpu); | ||||||
|     int interrupt_request = cpu->interrupt_request; |  | ||||||
|  |  | ||||||
|     if (unlikely(interrupt_request)) { |     if (unlikely(atomic_read(&cpu->interrupt_request))) { | ||||||
|  |         int interrupt_request; | ||||||
|  |         qemu_mutex_lock_iothread(); | ||||||
|  |         interrupt_request = cpu->interrupt_request; | ||||||
|         if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { |         if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { | ||||||
|             /* Mask out external interrupts for this step. */ |             /* Mask out external interrupts for this step. */ | ||||||
|             interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; |             interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; | ||||||
| @@ -475,6 +504,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|         if (interrupt_request & CPU_INTERRUPT_DEBUG) { |         if (interrupt_request & CPU_INTERRUPT_DEBUG) { | ||||||
|             cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; |             cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; | ||||||
|             cpu->exception_index = EXCP_DEBUG; |             cpu->exception_index = EXCP_DEBUG; | ||||||
|  |             qemu_mutex_unlock_iothread(); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { |         if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { | ||||||
| @@ -484,6 +514,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|             cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; |             cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; | ||||||
|             cpu->halted = 1; |             cpu->halted = 1; | ||||||
|             cpu->exception_index = EXCP_HLT; |             cpu->exception_index = EXCP_HLT; | ||||||
|  |             qemu_mutex_unlock_iothread(); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
| #if defined(TARGET_I386) | #if defined(TARGET_I386) | ||||||
| @@ -494,12 +525,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|             cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); |             cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); | ||||||
|             do_cpu_init(x86_cpu); |             do_cpu_init(x86_cpu); | ||||||
|             cpu->exception_index = EXCP_HALTED; |             cpu->exception_index = EXCP_HALTED; | ||||||
|  |             qemu_mutex_unlock_iothread(); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
| #else | #else | ||||||
|         else if (interrupt_request & CPU_INTERRUPT_RESET) { |         else if (interrupt_request & CPU_INTERRUPT_RESET) { | ||||||
|             replay_interrupt(); |             replay_interrupt(); | ||||||
|             cpu_reset(cpu); |             cpu_reset(cpu); | ||||||
|  |             qemu_mutex_unlock_iothread(); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
| #endif | #endif | ||||||
| @@ -522,7 +555,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|                the program flow was changed */ |                the program flow was changed */ | ||||||
|             *last_tb = NULL; |             *last_tb = NULL; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ | ||||||
|  |         qemu_mutex_unlock_iothread(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) { |     if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) { | ||||||
|         atomic_set(&cpu->exit_request, 0); |         atomic_set(&cpu->exit_request, 0); | ||||||
|         cpu->exception_index = EXCP_INTERRUPT; |         cpu->exception_index = EXCP_INTERRUPT; | ||||||
| @@ -548,15 +586,13 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, | |||||||
|     *tb_exit = ret & TB_EXIT_MASK; |     *tb_exit = ret & TB_EXIT_MASK; | ||||||
|     switch (*tb_exit) { |     switch (*tb_exit) { | ||||||
|     case TB_EXIT_REQUESTED: |     case TB_EXIT_REQUESTED: | ||||||
|         /* Something asked us to stop executing |         /* Something asked us to stop executing chained TBs; just | ||||||
|          * chained TBs; just continue round the main |          * continue round the main loop. Whatever requested the exit | ||||||
|          * loop. Whatever requested the exit will also |          * will also have set something else (eg interrupt_request) | ||||||
|          * have set something else (eg exit_request or |          * which we will handle next time around the loop.  But we | ||||||
|          * interrupt_request) which we will handle |          * need to ensure the tcg_exit_req read in generated code | ||||||
|          * next time around the loop.  But we need to |          * comes before the next read of cpu->exit_request or | ||||||
|          * ensure the zeroing of tcg_exit_req (see cpu_tb_exec) |          * cpu->interrupt_request. | ||||||
|          * comes before the next read of cpu->exit_request |  | ||||||
|          * or cpu->interrupt_request. |  | ||||||
|          */ |          */ | ||||||
|         smp_mb(); |         smp_mb(); | ||||||
|         *last_tb = NULL; |         *last_tb = NULL; | ||||||
| @@ -608,13 +644,8 @@ int cpu_exec(CPUState *cpu) | |||||||
|         return EXCP_HALTED; |         return EXCP_HALTED; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     atomic_mb_set(&tcg_current_cpu, cpu); |  | ||||||
|     rcu_read_lock(); |     rcu_read_lock(); | ||||||
|  |  | ||||||
|     if (unlikely(atomic_mb_read(&exit_request))) { |  | ||||||
|         cpu->exit_request = 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cc->cpu_exec_enter(cpu); |     cc->cpu_exec_enter(cpu); | ||||||
|  |  | ||||||
|     /* Calculate difference between guest clock and host clock. |     /* Calculate difference between guest clock and host clock. | ||||||
| @@ -640,6 +671,9 @@ int cpu_exec(CPUState *cpu) | |||||||
| #endif /* buggy compiler */ | #endif /* buggy compiler */ | ||||||
|         cpu->can_do_io = 1; |         cpu->can_do_io = 1; | ||||||
|         tb_lock_reset(); |         tb_lock_reset(); | ||||||
|  |         if (qemu_mutex_iothread_locked()) { | ||||||
|  |             qemu_mutex_unlock_iothread(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* if an exception is pending, we execute it here */ |     /* if an exception is pending, we execute it here */ | ||||||
| @@ -659,10 +693,5 @@ int cpu_exec(CPUState *cpu) | |||||||
|     cc->cpu_exec_exit(cpu); |     cc->cpu_exec_exit(cpu); | ||||||
|     rcu_read_unlock(); |     rcu_read_unlock(); | ||||||
|  |  | ||||||
|     /* fail safe : never use current_cpu outside cpu_exec() */ |  | ||||||
|     current_cpu = NULL; |  | ||||||
|  |  | ||||||
|     /* Does not need atomic_mb_set because a spurious wakeup is okay.  */ |  | ||||||
|     atomic_set(&tcg_current_cpu, NULL); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										335
									
								
								cpus.c
									
									
									
									
									
								
							
							
						
						
									
										335
									
								
								cpus.c
									
									
									
									
									
								
							| @@ -25,6 +25,7 @@ | |||||||
| /* Needed early for CONFIG_BSD etc. */ | /* Needed early for CONFIG_BSD etc. */ | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
|  | #include "qemu/config-file.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "monitor/monitor.h" | #include "monitor/monitor.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| @@ -45,6 +46,7 @@ | |||||||
| #include "qemu/main-loop.h" | #include "qemu/main-loop.h" | ||||||
| #include "qemu/bitmap.h" | #include "qemu/bitmap.h" | ||||||
| #include "qemu/seqlock.h" | #include "qemu/seqlock.h" | ||||||
|  | #include "tcg.h" | ||||||
| #include "qapi-event.h" | #include "qapi-event.h" | ||||||
| #include "hw/nmi.h" | #include "hw/nmi.h" | ||||||
| #include "sysemu/replay.h" | #include "sysemu/replay.h" | ||||||
| @@ -150,6 +152,77 @@ typedef struct TimersState { | |||||||
| } TimersState; | } TimersState; | ||||||
|  |  | ||||||
| static TimersState timers_state; | static TimersState timers_state; | ||||||
|  | bool mttcg_enabled; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * We default to false if we know other options have been enabled | ||||||
|  |  * which are currently incompatible with MTTCG. Otherwise when each | ||||||
|  |  * guest (target) has been updated to support: | ||||||
|  |  *   - atomic instructions | ||||||
|  |  *   - memory ordering primitives (barriers) | ||||||
|  |  * they can set the appropriate CONFIG flags in ${target}-softmmu.mak | ||||||
|  |  * | ||||||
|  |  * Once a guest architecture has been converted to the new primitives | ||||||
|  |  * there are two remaining limitations to check. | ||||||
|  |  * | ||||||
|  |  * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) | ||||||
|  |  * - The host must have a stronger memory order than the guest | ||||||
|  |  * | ||||||
|  |  * It may be possible in future to support strong guests on weak hosts | ||||||
|  |  * but that will require tagging all load/stores in a guest with their | ||||||
|  |  * implicit memory order requirements which would likely slow things | ||||||
|  |  * down a lot. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static bool check_tcg_memory_orders_compatible(void) | ||||||
|  | { | ||||||
|  | #if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO) | ||||||
|  |     return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0; | ||||||
|  | #else | ||||||
|  |     return false; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool default_mttcg_enabled(void) | ||||||
|  | { | ||||||
|  |     QemuOpts *icount_opts = qemu_find_opts_singleton("icount"); | ||||||
|  |     const char *rr = qemu_opt_get(icount_opts, "rr"); | ||||||
|  |  | ||||||
|  |     if (rr || TCG_OVERSIZED_GUEST) { | ||||||
|  |         return false; | ||||||
|  |     } else { | ||||||
|  | #ifdef TARGET_SUPPORTS_MTTCG | ||||||
|  |         return check_tcg_memory_orders_compatible(); | ||||||
|  | #else | ||||||
|  |         return false; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void qemu_tcg_configure(QemuOpts *opts, Error **errp) | ||||||
|  | { | ||||||
|  |     const char *t = qemu_opt_get(opts, "thread"); | ||||||
|  |     if (t) { | ||||||
|  |         if (strcmp(t, "multi") == 0) { | ||||||
|  |             if (TCG_OVERSIZED_GUEST) { | ||||||
|  |                 error_setg(errp, "No MTTCG when guest word size > hosts"); | ||||||
|  |             } else { | ||||||
|  |                 if (!check_tcg_memory_orders_compatible()) { | ||||||
|  |                     error_report("Guest expects a stronger memory ordering " | ||||||
|  |                                  "than the host provides"); | ||||||
|  |                     error_printf("This may cause strange/hard to debug errors"); | ||||||
|  |                 } | ||||||
|  |                 mttcg_enabled = true; | ||||||
|  |             } | ||||||
|  |         } else if (strcmp(t, "single") == 0) { | ||||||
|  |             mttcg_enabled = false; | ||||||
|  |         } else { | ||||||
|  |             error_setg(errp, "Invalid 'thread' setting %s", t); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         mttcg_enabled = default_mttcg_enabled(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| int64_t cpu_get_icount_raw(void) | int64_t cpu_get_icount_raw(void) | ||||||
| { | { | ||||||
| @@ -694,6 +767,63 @@ void configure_icount(QemuOpts *opts, Error **errp) | |||||||
|                    NANOSECONDS_PER_SECOND / 10); |                    NANOSECONDS_PER_SECOND / 10); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /***********************************************************/ | ||||||
|  | /* TCG vCPU kick timer | ||||||
|  |  * | ||||||
|  |  * The kick timer is responsible for moving single threaded vCPU | ||||||
|  |  * emulation on to the next vCPU. If more than one vCPU is running a | ||||||
|  |  * timer event with force a cpu->exit so the next vCPU can get | ||||||
|  |  * scheduled. | ||||||
|  |  * | ||||||
|  |  * The timer is removed if all vCPUs are idle and restarted again once | ||||||
|  |  * idleness is complete. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static QEMUTimer *tcg_kick_vcpu_timer; | ||||||
|  | static CPUState *tcg_current_rr_cpu; | ||||||
|  |  | ||||||
|  | #define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) | ||||||
|  |  | ||||||
|  | static inline int64_t qemu_tcg_next_kick(void) | ||||||
|  | { | ||||||
|  |     return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Kick the currently round-robin scheduled vCPU */ | ||||||
|  | static void qemu_cpu_kick_rr_cpu(void) | ||||||
|  | { | ||||||
|  |     CPUState *cpu; | ||||||
|  |     do { | ||||||
|  |         cpu = atomic_mb_read(&tcg_current_rr_cpu); | ||||||
|  |         if (cpu) { | ||||||
|  |             cpu_exit(cpu); | ||||||
|  |         } | ||||||
|  |     } while (cpu != atomic_mb_read(&tcg_current_rr_cpu)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kick_tcg_thread(void *opaque) | ||||||
|  | { | ||||||
|  |     timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick()); | ||||||
|  |     qemu_cpu_kick_rr_cpu(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void start_tcg_kick_timer(void) | ||||||
|  | { | ||||||
|  |     if (!mttcg_enabled && !tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) { | ||||||
|  |         tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, | ||||||
|  |                                            kick_tcg_thread, NULL); | ||||||
|  |         timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void stop_tcg_kick_timer(void) | ||||||
|  | { | ||||||
|  |     if (tcg_kick_vcpu_timer) { | ||||||
|  |         timer_del(tcg_kick_vcpu_timer); | ||||||
|  |         tcg_kick_vcpu_timer = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /***********************************************************/ | /***********************************************************/ | ||||||
| void hw_error(const char *fmt, ...) | void hw_error(const char *fmt, ...) | ||||||
| { | { | ||||||
| @@ -896,8 +1026,6 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu) | |||||||
| #endif /* _WIN32 */ | #endif /* _WIN32 */ | ||||||
|  |  | ||||||
| static QemuMutex qemu_global_mutex; | static QemuMutex qemu_global_mutex; | ||||||
| static QemuCond qemu_io_proceeded_cond; |  | ||||||
| static unsigned iothread_requesting_mutex; |  | ||||||
|  |  | ||||||
| static QemuThread io_thread; | static QemuThread io_thread; | ||||||
|  |  | ||||||
| @@ -911,7 +1039,6 @@ void qemu_init_cpu_loop(void) | |||||||
|     qemu_init_sigbus(); |     qemu_init_sigbus(); | ||||||
|     qemu_cond_init(&qemu_cpu_cond); |     qemu_cond_init(&qemu_cpu_cond); | ||||||
|     qemu_cond_init(&qemu_pause_cond); |     qemu_cond_init(&qemu_pause_cond); | ||||||
|     qemu_cond_init(&qemu_io_proceeded_cond); |  | ||||||
|     qemu_mutex_init(&qemu_global_mutex); |     qemu_mutex_init(&qemu_global_mutex); | ||||||
|  |  | ||||||
|     qemu_thread_get_self(&io_thread); |     qemu_thread_get_self(&io_thread); | ||||||
| @@ -936,29 +1063,35 @@ static void qemu_tcg_destroy_vcpu(CPUState *cpu) | |||||||
|  |  | ||||||
| static void qemu_wait_io_event_common(CPUState *cpu) | static void qemu_wait_io_event_common(CPUState *cpu) | ||||||
| { | { | ||||||
|  |     atomic_mb_set(&cpu->thread_kicked, false); | ||||||
|     if (cpu->stop) { |     if (cpu->stop) { | ||||||
|         cpu->stop = false; |         cpu->stop = false; | ||||||
|         cpu->stopped = true; |         cpu->stopped = true; | ||||||
|         qemu_cond_broadcast(&qemu_pause_cond); |         qemu_cond_broadcast(&qemu_pause_cond); | ||||||
|     } |     } | ||||||
|     process_queued_cpu_work(cpu); |     process_queued_cpu_work(cpu); | ||||||
|     cpu->thread_kicked = false; | } | ||||||
|  |  | ||||||
|  | static bool qemu_tcg_should_sleep(CPUState *cpu) | ||||||
|  | { | ||||||
|  |     if (mttcg_enabled) { | ||||||
|  |         return cpu_thread_is_idle(cpu); | ||||||
|  |     } else { | ||||||
|  |         return all_cpu_threads_idle(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qemu_tcg_wait_io_event(CPUState *cpu) | static void qemu_tcg_wait_io_event(CPUState *cpu) | ||||||
| { | { | ||||||
|     while (all_cpu_threads_idle()) { |     while (qemu_tcg_should_sleep(cpu)) { | ||||||
|  |         stop_tcg_kick_timer(); | ||||||
|         qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); |         qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (iothread_requesting_mutex) { |     start_tcg_kick_timer(); | ||||||
|         qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     CPU_FOREACH(cpu) { |  | ||||||
|     qemu_wait_io_event_common(cpu); |     qemu_wait_io_event_common(cpu); | ||||||
| } | } | ||||||
| } |  | ||||||
|  |  | ||||||
| static void qemu_kvm_wait_io_event(CPUState *cpu) | static void qemu_kvm_wait_io_event(CPUState *cpu) | ||||||
| { | { | ||||||
| @@ -1028,6 +1161,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) | |||||||
|     qemu_thread_get_self(cpu->thread); |     qemu_thread_get_self(cpu->thread); | ||||||
|     cpu->thread_id = qemu_get_thread_id(); |     cpu->thread_id = qemu_get_thread_id(); | ||||||
|     cpu->can_do_io = 1; |     cpu->can_do_io = 1; | ||||||
|  |     current_cpu = cpu; | ||||||
|  |  | ||||||
|     sigemptyset(&waitset); |     sigemptyset(&waitset); | ||||||
|     sigaddset(&waitset, SIG_IPI); |     sigaddset(&waitset, SIG_IPI); | ||||||
| @@ -1036,9 +1170,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) | |||||||
|     cpu->created = true; |     cpu->created = true; | ||||||
|     qemu_cond_signal(&qemu_cpu_cond); |     qemu_cond_signal(&qemu_cpu_cond); | ||||||
|  |  | ||||||
|     current_cpu = cpu; |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         current_cpu = NULL; |  | ||||||
|         qemu_mutex_unlock_iothread(); |         qemu_mutex_unlock_iothread(); | ||||||
|         do { |         do { | ||||||
|             int sig; |             int sig; | ||||||
| @@ -1049,7 +1181,6 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) | |||||||
|             exit(1); |             exit(1); | ||||||
|         } |         } | ||||||
|         qemu_mutex_lock_iothread(); |         qemu_mutex_lock_iothread(); | ||||||
|         current_cpu = cpu; |  | ||||||
|         qemu_wait_io_event_common(cpu); |         qemu_wait_io_event_common(cpu); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1115,9 +1246,11 @@ static int tcg_cpu_exec(CPUState *cpu) | |||||||
|         cpu->icount_decr.u16.low = decr; |         cpu->icount_decr.u16.low = decr; | ||||||
|         cpu->icount_extra = count; |         cpu->icount_extra = count; | ||||||
|     } |     } | ||||||
|  |     qemu_mutex_unlock_iothread(); | ||||||
|     cpu_exec_start(cpu); |     cpu_exec_start(cpu); | ||||||
|     ret = cpu_exec(cpu); |     ret = cpu_exec(cpu); | ||||||
|     cpu_exec_end(cpu); |     cpu_exec_end(cpu); | ||||||
|  |     qemu_mutex_lock_iothread(); | ||||||
| #ifdef CONFIG_PROFILER | #ifdef CONFIG_PROFILER | ||||||
|     tcg_time += profile_getclock() - ti; |     tcg_time += profile_getclock() - ti; | ||||||
| #endif | #endif | ||||||
| @@ -1150,7 +1283,16 @@ static void deal_with_unplugged_cpus(void) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *qemu_tcg_cpu_thread_fn(void *arg) | /* Single-threaded TCG | ||||||
|  |  * | ||||||
|  |  * In the single-threaded case each vCPU is simulated in turn. If | ||||||
|  |  * there is more than a single vCPU we create a simple timer to kick | ||||||
|  |  * the vCPU and ensure we don't get stuck in a tight loop in one vCPU. | ||||||
|  |  * This is done explicitly rather than relying on side-effects | ||||||
|  |  * elsewhere. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static void *qemu_tcg_rr_cpu_thread_fn(void *arg) | ||||||
| { | { | ||||||
|     CPUState *cpu = arg; |     CPUState *cpu = arg; | ||||||
|  |  | ||||||
| @@ -1172,15 +1314,18 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) | |||||||
|  |  | ||||||
|         /* process any pending work */ |         /* process any pending work */ | ||||||
|         CPU_FOREACH(cpu) { |         CPU_FOREACH(cpu) { | ||||||
|  |             current_cpu = cpu; | ||||||
|             qemu_wait_io_event_common(cpu); |             qemu_wait_io_event_common(cpu); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* process any pending work */ |     start_tcg_kick_timer(); | ||||||
|     atomic_mb_set(&exit_request, 1); |  | ||||||
|  |  | ||||||
|     cpu = first_cpu; |     cpu = first_cpu; | ||||||
|  |  | ||||||
|  |     /* process any pending work */ | ||||||
|  |     cpu->exit_request = 1; | ||||||
|  |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         /* Account partial waits to QEMU_CLOCK_VIRTUAL.  */ |         /* Account partial waits to QEMU_CLOCK_VIRTUAL.  */ | ||||||
|         qemu_account_warp_timer(); |         qemu_account_warp_timer(); | ||||||
| @@ -1189,7 +1334,10 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) | |||||||
|             cpu = first_cpu; |             cpu = first_cpu; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         for (; cpu != NULL && !exit_request; cpu = CPU_NEXT(cpu)) { |         while (cpu && !cpu->queued_work_first && !cpu->exit_request) { | ||||||
|  |  | ||||||
|  |             atomic_mb_set(&tcg_current_rr_cpu, cpu); | ||||||
|  |             current_cpu = cpu; | ||||||
|  |  | ||||||
|             qemu_clock_enable(QEMU_CLOCK_VIRTUAL, |             qemu_clock_enable(QEMU_CLOCK_VIRTUAL, | ||||||
|                               (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); |                               (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); | ||||||
| @@ -1200,22 +1348,32 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) | |||||||
|                 if (r == EXCP_DEBUG) { |                 if (r == EXCP_DEBUG) { | ||||||
|                     cpu_handle_guest_debug(cpu); |                     cpu_handle_guest_debug(cpu); | ||||||
|                     break; |                     break; | ||||||
|  |                 } else if (r == EXCP_ATOMIC) { | ||||||
|  |                     qemu_mutex_unlock_iothread(); | ||||||
|  |                     cpu_exec_step_atomic(cpu); | ||||||
|  |                     qemu_mutex_lock_iothread(); | ||||||
|  |                     break; | ||||||
|                 } |                 } | ||||||
|             } else if (cpu->stop || cpu->stopped) { |             } else if (cpu->stop) { | ||||||
|                 if (cpu->unplug) { |                 if (cpu->unplug) { | ||||||
|                     cpu = CPU_NEXT(cpu); |                     cpu = CPU_NEXT(cpu); | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         } /* for cpu.. */ |             cpu = CPU_NEXT(cpu); | ||||||
|  |         } /* while (cpu && !cpu->exit_request).. */ | ||||||
|  |  | ||||||
|         /* Pairs with smp_wmb in qemu_cpu_kick.  */ |         /* Does not need atomic_mb_set because a spurious wakeup is okay.  */ | ||||||
|         atomic_mb_set(&exit_request, 0); |         atomic_set(&tcg_current_rr_cpu, NULL); | ||||||
|  |  | ||||||
|  |         if (cpu && cpu->exit_request) { | ||||||
|  |             atomic_mb_set(&cpu->exit_request, 0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         handle_icount_deadline(); |         handle_icount_deadline(); | ||||||
|  |  | ||||||
|         qemu_tcg_wait_io_event(QTAILQ_FIRST(&cpus)); |         qemu_tcg_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus)); | ||||||
|         deal_with_unplugged_cpus(); |         deal_with_unplugged_cpus(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1262,6 +1420,68 @@ static void CALLBACK dummy_apc_func(ULONG_PTR unused) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | /* Multi-threaded TCG | ||||||
|  |  * | ||||||
|  |  * In the multi-threaded case each vCPU has its own thread. The TLS | ||||||
|  |  * variable current_cpu can be used deep in the code to find the | ||||||
|  |  * current CPUState for a given thread. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static void *qemu_tcg_cpu_thread_fn(void *arg) | ||||||
|  | { | ||||||
|  |     CPUState *cpu = arg; | ||||||
|  |  | ||||||
|  |     rcu_register_thread(); | ||||||
|  |  | ||||||
|  |     qemu_mutex_lock_iothread(); | ||||||
|  |     qemu_thread_get_self(cpu->thread); | ||||||
|  |  | ||||||
|  |     cpu->thread_id = qemu_get_thread_id(); | ||||||
|  |     cpu->created = true; | ||||||
|  |     cpu->can_do_io = 1; | ||||||
|  |     current_cpu = cpu; | ||||||
|  |     qemu_cond_signal(&qemu_cpu_cond); | ||||||
|  |  | ||||||
|  |     /* process any pending work */ | ||||||
|  |     cpu->exit_request = 1; | ||||||
|  |  | ||||||
|  |     while (1) { | ||||||
|  |         if (cpu_can_run(cpu)) { | ||||||
|  |             int r; | ||||||
|  |             r = tcg_cpu_exec(cpu); | ||||||
|  |             switch (r) { | ||||||
|  |             case EXCP_DEBUG: | ||||||
|  |                 cpu_handle_guest_debug(cpu); | ||||||
|  |                 break; | ||||||
|  |             case EXCP_HALTED: | ||||||
|  |                 /* during start-up the vCPU is reset and the thread is | ||||||
|  |                  * kicked several times. If we don't ensure we go back | ||||||
|  |                  * to sleep in the halted state we won't cleanly | ||||||
|  |                  * start-up when the vCPU is enabled. | ||||||
|  |                  * | ||||||
|  |                  * cpu->halted should ensure we sleep in wait_io_event | ||||||
|  |                  */ | ||||||
|  |                 g_assert(cpu->halted); | ||||||
|  |                 break; | ||||||
|  |             case EXCP_ATOMIC: | ||||||
|  |                 qemu_mutex_unlock_iothread(); | ||||||
|  |                 cpu_exec_step_atomic(cpu); | ||||||
|  |                 qemu_mutex_lock_iothread(); | ||||||
|  |             default: | ||||||
|  |                 /* Ignore everything else? */ | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         handle_icount_deadline(); | ||||||
|  |  | ||||||
|  |         atomic_mb_set(&cpu->exit_request, 0); | ||||||
|  |         qemu_tcg_wait_io_event(cpu); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void qemu_cpu_kick_thread(CPUState *cpu) | static void qemu_cpu_kick_thread(CPUState *cpu) | ||||||
| { | { | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
| @@ -1287,24 +1507,13 @@ static void qemu_cpu_kick_thread(CPUState *cpu) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qemu_cpu_kick_no_halt(void) |  | ||||||
| { |  | ||||||
|     CPUState *cpu; |  | ||||||
|     /* Ensure whatever caused the exit has reached the CPU threads before |  | ||||||
|      * writing exit_request. |  | ||||||
|      */ |  | ||||||
|     atomic_mb_set(&exit_request, 1); |  | ||||||
|     cpu = atomic_mb_read(&tcg_current_cpu); |  | ||||||
|     if (cpu) { |  | ||||||
|         cpu_exit(cpu); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qemu_cpu_kick(CPUState *cpu) | void qemu_cpu_kick(CPUState *cpu) | ||||||
| { | { | ||||||
|     qemu_cond_broadcast(cpu->halt_cond); |     qemu_cond_broadcast(cpu->halt_cond); | ||||||
|     if (tcg_enabled()) { |     if (tcg_enabled()) { | ||||||
|         qemu_cpu_kick_no_halt(); |         cpu_exit(cpu); | ||||||
|  |         /* NOP unless doing single-thread RR */ | ||||||
|  |         qemu_cpu_kick_rr_cpu(); | ||||||
|     } else { |     } else { | ||||||
|         if (hax_enabled()) { |         if (hax_enabled()) { | ||||||
|             /* |             /* | ||||||
| @@ -1342,27 +1551,14 @@ bool qemu_mutex_iothread_locked(void) | |||||||
|  |  | ||||||
| void qemu_mutex_lock_iothread(void) | void qemu_mutex_lock_iothread(void) | ||||||
| { | { | ||||||
|     atomic_inc(&iothread_requesting_mutex); |     g_assert(!qemu_mutex_iothread_locked()); | ||||||
|     /* In the simple case there is no need to bump the VCPU thread out of |  | ||||||
|      * TCG code execution. |  | ||||||
|      */ |  | ||||||
|     if (!tcg_enabled() || qemu_in_vcpu_thread() || |  | ||||||
|         !first_cpu || !first_cpu->created) { |  | ||||||
|     qemu_mutex_lock(&qemu_global_mutex); |     qemu_mutex_lock(&qemu_global_mutex); | ||||||
|         atomic_dec(&iothread_requesting_mutex); |  | ||||||
|     } else { |  | ||||||
|         if (qemu_mutex_trylock(&qemu_global_mutex)) { |  | ||||||
|             qemu_cpu_kick_no_halt(); |  | ||||||
|             qemu_mutex_lock(&qemu_global_mutex); |  | ||||||
|         } |  | ||||||
|         atomic_dec(&iothread_requesting_mutex); |  | ||||||
|         qemu_cond_broadcast(&qemu_io_proceeded_cond); |  | ||||||
|     } |  | ||||||
|     iothread_locked = true; |     iothread_locked = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void qemu_mutex_unlock_iothread(void) | void qemu_mutex_unlock_iothread(void) | ||||||
| { | { | ||||||
|  |     g_assert(qemu_mutex_iothread_locked()); | ||||||
|     iothread_locked = false; |     iothread_locked = false; | ||||||
|     qemu_mutex_unlock(&qemu_global_mutex); |     qemu_mutex_unlock(&qemu_global_mutex); | ||||||
| } | } | ||||||
| @@ -1392,13 +1588,6 @@ void pause_all_vcpus(void) | |||||||
|  |  | ||||||
|     if (qemu_in_vcpu_thread()) { |     if (qemu_in_vcpu_thread()) { | ||||||
|         cpu_stop_current(); |         cpu_stop_current(); | ||||||
|         if (!kvm_enabled()) { |  | ||||||
|             CPU_FOREACH(cpu) { |  | ||||||
|                 cpu->stop = false; |  | ||||||
|                 cpu->stopped = true; |  | ||||||
|             } |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (!all_vcpus_paused()) { |     while (!all_vcpus_paused()) { | ||||||
| @@ -1447,29 +1636,43 @@ void cpu_remove_sync(CPUState *cpu) | |||||||
| static void qemu_tcg_init_vcpu(CPUState *cpu) | static void qemu_tcg_init_vcpu(CPUState *cpu) | ||||||
| { | { | ||||||
|     char thread_name[VCPU_THREAD_NAME_SIZE]; |     char thread_name[VCPU_THREAD_NAME_SIZE]; | ||||||
|     static QemuCond *tcg_halt_cond; |     static QemuCond *single_tcg_halt_cond; | ||||||
|     static QemuThread *tcg_cpu_thread; |     static QemuThread *single_tcg_cpu_thread; | ||||||
|  |  | ||||||
|     /* share a single thread for all cpus with TCG */ |     if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) { | ||||||
|     if (!tcg_cpu_thread) { |  | ||||||
|         cpu->thread = g_malloc0(sizeof(QemuThread)); |         cpu->thread = g_malloc0(sizeof(QemuThread)); | ||||||
|         cpu->halt_cond = g_malloc0(sizeof(QemuCond)); |         cpu->halt_cond = g_malloc0(sizeof(QemuCond)); | ||||||
|         qemu_cond_init(cpu->halt_cond); |         qemu_cond_init(cpu->halt_cond); | ||||||
|         tcg_halt_cond = cpu->halt_cond; |  | ||||||
|  |         if (qemu_tcg_mttcg_enabled()) { | ||||||
|  |             /* create a thread per vCPU with TCG (MTTCG) */ | ||||||
|  |             parallel_cpus = true; | ||||||
|             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", |             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", | ||||||
|                  cpu->cpu_index); |                  cpu->cpu_index); | ||||||
|  |  | ||||||
|             qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn, |             qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn, | ||||||
|                                cpu, QEMU_THREAD_JOINABLE); |                                cpu, QEMU_THREAD_JOINABLE); | ||||||
|  |  | ||||||
|  |         } else { | ||||||
|  |             /* share a single thread for all cpus with TCG */ | ||||||
|  |             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG"); | ||||||
|  |             qemu_thread_create(cpu->thread, thread_name, | ||||||
|  |                                qemu_tcg_rr_cpu_thread_fn, | ||||||
|  |                                cpu, QEMU_THREAD_JOINABLE); | ||||||
|  |  | ||||||
|  |             single_tcg_halt_cond = cpu->halt_cond; | ||||||
|  |             single_tcg_cpu_thread = cpu->thread; | ||||||
|  |         } | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|         cpu->hThread = qemu_thread_get_handle(cpu->thread); |         cpu->hThread = qemu_thread_get_handle(cpu->thread); | ||||||
| #endif | #endif | ||||||
|         while (!cpu->created) { |         while (!cpu->created) { | ||||||
|             qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); |             qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); | ||||||
|         } |         } | ||||||
|         tcg_cpu_thread = cpu->thread; |  | ||||||
|     } else { |     } else { | ||||||
|         cpu->thread = tcg_cpu_thread; |         /* For non-MTTCG cases we share the thread */ | ||||||
|         cpu->halt_cond = tcg_halt_cond; |         cpu->thread = single_tcg_cpu_thread; | ||||||
|  |         cpu->halt_cond = single_tcg_halt_cond; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										478
									
								
								cputlb.c
									
									
									
									
									
								
							
							
						
						
									
										478
									
								
								cputlb.c
									
									
									
									
									
								
							| @@ -18,6 +18,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu/main-loop.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "exec/exec-all.h" | #include "exec/exec-all.h" | ||||||
| #include "exec/memory.h" | #include "exec/memory.h" | ||||||
| @@ -57,6 +58,40 @@ | |||||||
|     } \ |     } \ | ||||||
| } while (0) | } while (0) | ||||||
|  |  | ||||||
|  | #define assert_cpu_is_self(this_cpu) do {                         \ | ||||||
|  |         if (DEBUG_TLB_GATE) {                                     \ | ||||||
|  |             g_assert(!cpu->created || qemu_cpu_is_self(cpu));     \ | ||||||
|  |         }                                                         \ | ||||||
|  |     } while (0) | ||||||
|  |  | ||||||
|  | /* run_on_cpu_data.target_ptr should always be big enough for a | ||||||
|  |  * target_ulong even on 32 bit builds */ | ||||||
|  | QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data)); | ||||||
|  |  | ||||||
|  | /* We currently can't handle more than 16 bits in the MMUIDX bitmask. | ||||||
|  |  */ | ||||||
|  | QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16); | ||||||
|  | #define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1) | ||||||
|  |  | ||||||
|  | /* flush_all_helper: run fn across all cpus | ||||||
|  |  * | ||||||
|  |  * If the wait flag is set then the src cpu's helper will be queued as | ||||||
|  |  * "safe" work and the loop exited creating a synchronisation point | ||||||
|  |  * where all queued work will be finished before execution starts | ||||||
|  |  * again. | ||||||
|  |  */ | ||||||
|  | static void flush_all_helper(CPUState *src, run_on_cpu_func fn, | ||||||
|  |                              run_on_cpu_data d) | ||||||
|  | { | ||||||
|  |     CPUState *cpu; | ||||||
|  |  | ||||||
|  |     CPU_FOREACH(cpu) { | ||||||
|  |         if (cpu != src) { | ||||||
|  |             async_run_on_cpu(cpu, fn, d); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* statistics */ | /* statistics */ | ||||||
| int tlb_flush_count; | int tlb_flush_count; | ||||||
|  |  | ||||||
| @@ -65,10 +100,22 @@ int tlb_flush_count; | |||||||
|  * flushing more entries than required is only an efficiency issue, |  * flushing more entries than required is only an efficiency issue, | ||||||
|  * not a correctness issue. |  * not a correctness issue. | ||||||
|  */ |  */ | ||||||
| void tlb_flush(CPUState *cpu) | static void tlb_flush_nocheck(CPUState *cpu) | ||||||
| { | { | ||||||
|     CPUArchState *env = cpu->env_ptr; |     CPUArchState *env = cpu->env_ptr; | ||||||
|  |  | ||||||
|  |     /* The QOM tests will trigger tlb_flushes without setting up TCG | ||||||
|  |      * so we bug out here in that case. | ||||||
|  |      */ | ||||||
|  |     if (!tcg_enabled()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     assert_cpu_is_self(cpu); | ||||||
|  |     tlb_debug("(count: %d)\n", tlb_flush_count++); | ||||||
|  |  | ||||||
|  |     tb_lock(); | ||||||
|  |  | ||||||
|     memset(env->tlb_table, -1, sizeof(env->tlb_table)); |     memset(env->tlb_table, -1, sizeof(env->tlb_table)); | ||||||
|     memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); |     memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); | ||||||
|     memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); |     memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); | ||||||
| @@ -76,38 +123,116 @@ void tlb_flush(CPUState *cpu) | |||||||
|     env->vtlb_index = 0; |     env->vtlb_index = 0; | ||||||
|     env->tlb_flush_addr = -1; |     env->tlb_flush_addr = -1; | ||||||
|     env->tlb_flush_mask = 0; |     env->tlb_flush_mask = 0; | ||||||
|     tlb_flush_count++; |  | ||||||
|  |     tb_unlock(); | ||||||
|  |  | ||||||
|  |     atomic_mb_set(&cpu->pending_tlb_flush, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, va_list argp) | static void tlb_flush_global_async_work(CPUState *cpu, run_on_cpu_data data) | ||||||
|  | { | ||||||
|  |     tlb_flush_nocheck(cpu); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush(CPUState *cpu) | ||||||
|  | { | ||||||
|  |     if (cpu->created && !qemu_cpu_is_self(cpu)) { | ||||||
|  |         if (atomic_mb_read(&cpu->pending_tlb_flush) != ALL_MMUIDX_BITS) { | ||||||
|  |             atomic_mb_set(&cpu->pending_tlb_flush, ALL_MMUIDX_BITS); | ||||||
|  |             async_run_on_cpu(cpu, tlb_flush_global_async_work, | ||||||
|  |                              RUN_ON_CPU_NULL); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         tlb_flush_nocheck(cpu); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_all_cpus(CPUState *src_cpu) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_flush_global_async_work; | ||||||
|  |     flush_all_helper(src_cpu, fn, RUN_ON_CPU_NULL); | ||||||
|  |     fn(src_cpu, RUN_ON_CPU_NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_all_cpus_synced(CPUState *src_cpu) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_flush_global_async_work; | ||||||
|  |     flush_all_helper(src_cpu, fn, RUN_ON_CPU_NULL); | ||||||
|  |     async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) | ||||||
| { | { | ||||||
|     CPUArchState *env = cpu->env_ptr; |     CPUArchState *env = cpu->env_ptr; | ||||||
|  |     unsigned long mmu_idx_bitmask = data.host_int; | ||||||
|  |     int mmu_idx; | ||||||
|  |  | ||||||
|     tlb_debug("start\n"); |     assert_cpu_is_self(cpu); | ||||||
|  |  | ||||||
|     for (;;) { |     tb_lock(); | ||||||
|         int mmu_idx = va_arg(argp, int); |  | ||||||
|  |  | ||||||
|         if (mmu_idx < 0) { |     tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask); | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||||
|  |  | ||||||
|  |         if (test_bit(mmu_idx, &mmu_idx_bitmask)) { | ||||||
|             tlb_debug("%d\n", mmu_idx); |             tlb_debug("%d\n", mmu_idx); | ||||||
|  |  | ||||||
|             memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0])); |             memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0])); | ||||||
|             memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0])); |             memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0])); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); |     memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); | ||||||
|  |  | ||||||
|  |     tlb_debug("done\n"); | ||||||
|  |  | ||||||
|  |     tb_unlock(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void tlb_flush_by_mmuidx(CPUState *cpu, ...) | void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) | ||||||
| { | { | ||||||
|     va_list argp; |     tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); | ||||||
|     va_start(argp, cpu); |  | ||||||
|     v_tlb_flush_by_mmuidx(cpu, argp); |     if (!qemu_cpu_is_self(cpu)) { | ||||||
|     va_end(argp); |         uint16_t pending_flushes = idxmap; | ||||||
|  |         pending_flushes &= ~atomic_mb_read(&cpu->pending_tlb_flush); | ||||||
|  |  | ||||||
|  |         if (pending_flushes) { | ||||||
|  |             tlb_debug("reduced mmu_idx: 0x%" PRIx16 "\n", pending_flushes); | ||||||
|  |  | ||||||
|  |             atomic_or(&cpu->pending_tlb_flush, pending_flushes); | ||||||
|  |             async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work, | ||||||
|  |                              RUN_ON_CPU_HOST_INT(pending_flushes)); | ||||||
|         } |         } | ||||||
|  |     } else { | ||||||
|  |         tlb_flush_by_mmuidx_async_work(cpu, | ||||||
|  |                                        RUN_ON_CPU_HOST_INT(idxmap)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; | ||||||
|  |  | ||||||
|  |     tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap); | ||||||
|  |  | ||||||
|  |     flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); | ||||||
|  |     fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, | ||||||
|  |                                                        uint16_t idxmap) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; | ||||||
|  |  | ||||||
|  |     tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap); | ||||||
|  |  | ||||||
|  |     flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); | ||||||
|  |     async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) | static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) | ||||||
| { | { | ||||||
| @@ -121,12 +246,15 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void tlb_flush_page(CPUState *cpu, target_ulong addr) | static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) | ||||||
| { | { | ||||||
|     CPUArchState *env = cpu->env_ptr; |     CPUArchState *env = cpu->env_ptr; | ||||||
|  |     target_ulong addr = (target_ulong) data.target_ptr; | ||||||
|     int i; |     int i; | ||||||
|     int mmu_idx; |     int mmu_idx; | ||||||
|  |  | ||||||
|  |     assert_cpu_is_self(cpu); | ||||||
|  |  | ||||||
|     tlb_debug("page :" TARGET_FMT_lx "\n", addr); |     tlb_debug("page :" TARGET_FMT_lx "\n", addr); | ||||||
|  |  | ||||||
|     /* Check if we need to flush due to large pages.  */ |     /* Check if we need to flush due to large pages.  */ | ||||||
| @@ -156,15 +284,62 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) | |||||||
|     tb_flush_jmp_cache(cpu, addr); |     tb_flush_jmp_cache(cpu, addr); | ||||||
| } | } | ||||||
|  |  | ||||||
| void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...) | void tlb_flush_page(CPUState *cpu, target_ulong addr) | ||||||
|  | { | ||||||
|  |     tlb_debug("page :" TARGET_FMT_lx "\n", addr); | ||||||
|  |  | ||||||
|  |     if (!qemu_cpu_is_self(cpu)) { | ||||||
|  |         async_run_on_cpu(cpu, tlb_flush_page_async_work, | ||||||
|  |                          RUN_ON_CPU_TARGET_PTR(addr)); | ||||||
|  |     } else { | ||||||
|  |         tlb_flush_page_async_work(cpu, RUN_ON_CPU_TARGET_PTR(addr)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* As we are going to hijack the bottom bits of the page address for a | ||||||
|  |  * mmuidx bit mask we need to fail to build if we can't do that | ||||||
|  |  */ | ||||||
|  | QEMU_BUILD_BUG_ON(NB_MMU_MODES > TARGET_PAGE_BITS_MIN); | ||||||
|  |  | ||||||
|  | static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, | ||||||
|  |                                                 run_on_cpu_data data) | ||||||
| { | { | ||||||
|     CPUArchState *env = cpu->env_ptr; |     CPUArchState *env = cpu->env_ptr; | ||||||
|     int i, k; |     target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr; | ||||||
|     va_list argp; |     target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK; | ||||||
|  |     unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; | ||||||
|  |     int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |     int mmu_idx; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|     va_start(argp, addr); |     assert_cpu_is_self(cpu); | ||||||
|  |  | ||||||
|     tlb_debug("addr "TARGET_FMT_lx"\n", addr); |     tlb_debug("page:%d addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n", | ||||||
|  |               page, addr, mmu_idx_bitmap); | ||||||
|  |  | ||||||
|  |     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||||
|  |         if (test_bit(mmu_idx, &mmu_idx_bitmap)) { | ||||||
|  |             tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr); | ||||||
|  |  | ||||||
|  |             /* check whether there are vltb entries that need to be flushed */ | ||||||
|  |             for (i = 0; i < CPU_VTLB_SIZE; i++) { | ||||||
|  |                 tlb_flush_entry(&env->tlb_v_table[mmu_idx][i], addr); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     tb_flush_jmp_cache(cpu, addr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void tlb_check_page_and_flush_by_mmuidx_async_work(CPUState *cpu, | ||||||
|  |                                                           run_on_cpu_data data) | ||||||
|  | { | ||||||
|  |     CPUArchState *env = cpu->env_ptr; | ||||||
|  |     target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr; | ||||||
|  |     target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK; | ||||||
|  |     unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; | ||||||
|  |  | ||||||
|  |     tlb_debug("addr:"TARGET_FMT_lx" mmu_idx: %04lx\n", addr, mmu_idx_bitmap); | ||||||
|  |  | ||||||
|     /* Check if we need to flush due to large pages.  */ |     /* Check if we need to flush due to large pages.  */ | ||||||
|     if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { |     if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { | ||||||
| @@ -172,33 +347,80 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...) | |||||||
|                   TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", |                   TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", | ||||||
|                   env->tlb_flush_addr, env->tlb_flush_mask); |                   env->tlb_flush_addr, env->tlb_flush_mask); | ||||||
|  |  | ||||||
|         v_tlb_flush_by_mmuidx(cpu, argp); |         tlb_flush_by_mmuidx_async_work(cpu, | ||||||
|         va_end(argp); |                                        RUN_ON_CPU_HOST_INT(mmu_idx_bitmap)); | ||||||
|         return; |     } else { | ||||||
|     } |         tlb_flush_page_by_mmuidx_async_work(cpu, data); | ||||||
|  |  | ||||||
|     addr &= TARGET_PAGE_MASK; |  | ||||||
|     i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |  | ||||||
|  |  | ||||||
|     for (;;) { |  | ||||||
|         int mmu_idx = va_arg(argp, int); |  | ||||||
|  |  | ||||||
|         if (mmu_idx < 0) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         tlb_debug("idx %d\n", mmu_idx); |  | ||||||
|  |  | ||||||
|         tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); |  | ||||||
|  |  | ||||||
|         /* check whether there are vltb entries that need to be flushed */ |  | ||||||
|         for (k = 0; k < CPU_VTLB_SIZE; k++) { |  | ||||||
|             tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|     va_end(argp); |  | ||||||
|  |  | ||||||
|     tb_flush_jmp_cache(cpu, addr); | void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) | ||||||
|  | { | ||||||
|  |     target_ulong addr_and_mmu_idx; | ||||||
|  |  | ||||||
|  |     tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%" PRIx16 "\n", addr, idxmap); | ||||||
|  |  | ||||||
|  |     /* This should already be page aligned */ | ||||||
|  |     addr_and_mmu_idx = addr & TARGET_PAGE_MASK; | ||||||
|  |     addr_and_mmu_idx |= idxmap; | ||||||
|  |  | ||||||
|  |     if (!qemu_cpu_is_self(cpu)) { | ||||||
|  |         async_run_on_cpu(cpu, tlb_check_page_and_flush_by_mmuidx_async_work, | ||||||
|  |                          RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); | ||||||
|  |     } else { | ||||||
|  |         tlb_check_page_and_flush_by_mmuidx_async_work( | ||||||
|  |             cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, | ||||||
|  |                                        uint16_t idxmap) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_check_page_and_flush_by_mmuidx_async_work; | ||||||
|  |     target_ulong addr_and_mmu_idx; | ||||||
|  |  | ||||||
|  |     tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); | ||||||
|  |  | ||||||
|  |     /* This should already be page aligned */ | ||||||
|  |     addr_and_mmu_idx = addr & TARGET_PAGE_MASK; | ||||||
|  |     addr_and_mmu_idx |= idxmap; | ||||||
|  |  | ||||||
|  |     flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); | ||||||
|  |     fn(src_cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, | ||||||
|  |                                                             target_ulong addr, | ||||||
|  |                                                             uint16_t idxmap) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_check_page_and_flush_by_mmuidx_async_work; | ||||||
|  |     target_ulong addr_and_mmu_idx; | ||||||
|  |  | ||||||
|  |     tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); | ||||||
|  |  | ||||||
|  |     /* This should already be page aligned */ | ||||||
|  |     addr_and_mmu_idx = addr & TARGET_PAGE_MASK; | ||||||
|  |     addr_and_mmu_idx |= idxmap; | ||||||
|  |  | ||||||
|  |     flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); | ||||||
|  |     async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_flush_page_async_work; | ||||||
|  |  | ||||||
|  |     flush_all_helper(src, fn, RUN_ON_CPU_TARGET_PTR(addr)); | ||||||
|  |     fn(src, RUN_ON_CPU_TARGET_PTR(addr)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tlb_flush_page_all_cpus_synced(CPUState *src, | ||||||
|  |                                                   target_ulong addr) | ||||||
|  | { | ||||||
|  |     const run_on_cpu_func fn = tlb_flush_page_async_work; | ||||||
|  |  | ||||||
|  |     flush_all_helper(src, fn, RUN_ON_CPU_TARGET_PTR(addr)); | ||||||
|  |     async_safe_run_on_cpu(src, fn, RUN_ON_CPU_TARGET_PTR(addr)); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* update the TLBs so that writes to code in the virtual page 'addr' | /* update the TLBs so that writes to code in the virtual page 'addr' | ||||||
| @@ -216,36 +438,84 @@ void tlb_unprotect_code(ram_addr_t ram_addr) | |||||||
|     cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); |     cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) |  | ||||||
| { |  | ||||||
|     return (tlbe->addr_write & (TLB_INVALID_MASK|TLB_MMIO|TLB_NOTDIRTY)) == 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, | /* | ||||||
|  |  * Dirty write flag handling | ||||||
|  |  * | ||||||
|  |  * When the TCG code writes to a location it looks up the address in | ||||||
|  |  * the TLB and uses that data to compute the final address. If any of | ||||||
|  |  * the lower bits of the address are set then the slow path is forced. | ||||||
|  |  * There are a number of reasons to do this but for normal RAM the | ||||||
|  |  * most usual is detecting writes to code regions which may invalidate | ||||||
|  |  * generated code. | ||||||
|  |  * | ||||||
|  |  * Because we want other vCPUs to respond to changes straight away we | ||||||
|  |  * update the te->addr_write field atomically. If the TLB entry has | ||||||
|  |  * been changed by the vCPU in the mean time we skip the update. | ||||||
|  |  * | ||||||
|  |  * As this function uses atomic accesses we also need to ensure | ||||||
|  |  * updates to tlb_entries follow the same access rules. We don't need | ||||||
|  |  * to worry about this for oversized guests as MTTCG is disabled for | ||||||
|  |  * them. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, | ||||||
|                            uintptr_t length) |                            uintptr_t length) | ||||||
| { | { | ||||||
|     uintptr_t addr; | #if TCG_OVERSIZED_GUEST | ||||||
|  |     uintptr_t addr = tlb_entry->addr_write; | ||||||
|  |  | ||||||
|     if (tlb_is_dirty_ram(tlb_entry)) { |     if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) { | ||||||
|         addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; |         addr &= TARGET_PAGE_MASK; | ||||||
|  |         addr += tlb_entry->addend; | ||||||
|         if ((addr - start) < length) { |         if ((addr - start) < length) { | ||||||
|             tlb_entry->addr_write |= TLB_NOTDIRTY; |             tlb_entry->addr_write |= TLB_NOTDIRTY; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | #else | ||||||
|  |     /* paired with atomic_mb_set in tlb_set_page_with_attrs */ | ||||||
|  |     uintptr_t orig_addr = atomic_mb_read(&tlb_entry->addr_write); | ||||||
|  |     uintptr_t addr = orig_addr; | ||||||
|  |  | ||||||
|  |     if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) { | ||||||
|  |         addr &= TARGET_PAGE_MASK; | ||||||
|  |         addr += atomic_read(&tlb_entry->addend); | ||||||
|  |         if ((addr - start) < length) { | ||||||
|  |             uintptr_t notdirty_addr = orig_addr | TLB_NOTDIRTY; | ||||||
|  |             atomic_cmpxchg(&tlb_entry->addr_write, orig_addr, notdirty_addr); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) | /* For atomic correctness when running MTTCG we need to use the right | ||||||
|  |  * primitives when copying entries */ | ||||||
|  | static inline void copy_tlb_helper(CPUTLBEntry *d, CPUTLBEntry *s, | ||||||
|  |                                    bool atomic_set) | ||||||
| { | { | ||||||
|     ram_addr_t ram_addr; | #if TCG_OVERSIZED_GUEST | ||||||
|  |     *d = *s; | ||||||
|     ram_addr = qemu_ram_addr_from_host(ptr); | #else | ||||||
|     if (ram_addr == RAM_ADDR_INVALID) { |     if (atomic_set) { | ||||||
|         fprintf(stderr, "Bad ram pointer %p\n", ptr); |         d->addr_read = s->addr_read; | ||||||
|         abort(); |         d->addr_code = s->addr_code; | ||||||
|  |         atomic_set(&d->addend, atomic_read(&s->addend)); | ||||||
|  |         /* Pairs with flag setting in tlb_reset_dirty_range */ | ||||||
|  |         atomic_mb_set(&d->addr_write, atomic_read(&s->addr_write)); | ||||||
|  |     } else { | ||||||
|  |         d->addr_read = s->addr_read; | ||||||
|  |         d->addr_write = atomic_read(&s->addr_write); | ||||||
|  |         d->addr_code = s->addr_code; | ||||||
|  |         d->addend = atomic_read(&s->addend); | ||||||
|     } |     } | ||||||
|     return ram_addr; | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* This is a cross vCPU call (i.e. another vCPU resetting the flags of | ||||||
|  |  * the target vCPU). As such care needs to be taken that we don't | ||||||
|  |  * dangerously race with another vCPU update. The only thing actually | ||||||
|  |  * updated is the target TLB entry ->addr_write flags. | ||||||
|  |  */ | ||||||
| void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) | void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) | ||||||
| { | { | ||||||
|     CPUArchState *env; |     CPUArchState *env; | ||||||
| @@ -283,6 +553,8 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | |||||||
|     int i; |     int i; | ||||||
|     int mmu_idx; |     int mmu_idx; | ||||||
|  |  | ||||||
|  |     assert_cpu_is_self(cpu); | ||||||
|  |  | ||||||
|     vaddr &= TARGET_PAGE_MASK; |     vaddr &= TARGET_PAGE_MASK; | ||||||
|     i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |     i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { |     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||||
| @@ -337,11 +609,12 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, | |||||||
|     target_ulong address; |     target_ulong address; | ||||||
|     target_ulong code_address; |     target_ulong code_address; | ||||||
|     uintptr_t addend; |     uintptr_t addend; | ||||||
|     CPUTLBEntry *te; |     CPUTLBEntry *te, *tv, tn; | ||||||
|     hwaddr iotlb, xlat, sz; |     hwaddr iotlb, xlat, sz; | ||||||
|     unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; |     unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; | ||||||
|     int asidx = cpu_asidx_from_attrs(cpu, attrs); |     int asidx = cpu_asidx_from_attrs(cpu, attrs); | ||||||
|  |  | ||||||
|  |     assert_cpu_is_self(cpu); | ||||||
|     assert(size >= TARGET_PAGE_SIZE); |     assert(size >= TARGET_PAGE_SIZE); | ||||||
|     if (size != TARGET_PAGE_SIZE) { |     if (size != TARGET_PAGE_SIZE) { | ||||||
|         tlb_add_large_page(env, vaddr, size); |         tlb_add_large_page(env, vaddr, size); | ||||||
| @@ -371,41 +644,50 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, | |||||||
|  |  | ||||||
|     index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |     index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     te = &env->tlb_table[mmu_idx][index]; |     te = &env->tlb_table[mmu_idx][index]; | ||||||
|  |  | ||||||
|     /* do not discard the translation in te, evict it into a victim tlb */ |     /* do not discard the translation in te, evict it into a victim tlb */ | ||||||
|     env->tlb_v_table[mmu_idx][vidx] = *te; |     tv = &env->tlb_v_table[mmu_idx][vidx]; | ||||||
|  |  | ||||||
|  |     /* addr_write can race with tlb_reset_dirty_range */ | ||||||
|  |     copy_tlb_helper(tv, te, true); | ||||||
|  |  | ||||||
|     env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; |     env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; | ||||||
|  |  | ||||||
|     /* refill the tlb */ |     /* refill the tlb */ | ||||||
|     env->iotlb[mmu_idx][index].addr = iotlb - vaddr; |     env->iotlb[mmu_idx][index].addr = iotlb - vaddr; | ||||||
|     env->iotlb[mmu_idx][index].attrs = attrs; |     env->iotlb[mmu_idx][index].attrs = attrs; | ||||||
|     te->addend = addend - vaddr; |  | ||||||
|  |     /* Now calculate the new entry */ | ||||||
|  |     tn.addend = addend - vaddr; | ||||||
|     if (prot & PAGE_READ) { |     if (prot & PAGE_READ) { | ||||||
|         te->addr_read = address; |         tn.addr_read = address; | ||||||
|     } else { |     } else { | ||||||
|         te->addr_read = -1; |         tn.addr_read = -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (prot & PAGE_EXEC) { |     if (prot & PAGE_EXEC) { | ||||||
|         te->addr_code = code_address; |         tn.addr_code = code_address; | ||||||
|     } else { |     } else { | ||||||
|         te->addr_code = -1; |         tn.addr_code = -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     tn.addr_write = -1; | ||||||
|     if (prot & PAGE_WRITE) { |     if (prot & PAGE_WRITE) { | ||||||
|         if ((memory_region_is_ram(section->mr) && section->readonly) |         if ((memory_region_is_ram(section->mr) && section->readonly) | ||||||
|             || memory_region_is_romd(section->mr)) { |             || memory_region_is_romd(section->mr)) { | ||||||
|             /* Write access calls the I/O callback.  */ |             /* Write access calls the I/O callback.  */ | ||||||
|             te->addr_write = address | TLB_MMIO; |             tn.addr_write = address | TLB_MMIO; | ||||||
|         } else if (memory_region_is_ram(section->mr) |         } else if (memory_region_is_ram(section->mr) | ||||||
|                    && cpu_physical_memory_is_clean( |                    && cpu_physical_memory_is_clean( | ||||||
|                         memory_region_get_ram_addr(section->mr) + xlat)) { |                         memory_region_get_ram_addr(section->mr) + xlat)) { | ||||||
|             te->addr_write = address | TLB_NOTDIRTY; |             tn.addr_write = address | TLB_NOTDIRTY; | ||||||
|         } else { |         } else { | ||||||
|             te->addr_write = address; |             tn.addr_write = address; | ||||||
|         } |         } | ||||||
|     } else { |  | ||||||
|         te->addr_write = -1; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /* Pairs with flag setting in tlb_reset_dirty_range */ | ||||||
|  |     copy_tlb_helper(te, &tn, true); | ||||||
|  |     /* atomic_mb_set(&te->addr_write, write_address); */ | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Add a new TLB entry, but without specifying the memory | /* Add a new TLB entry, but without specifying the memory | ||||||
| @@ -452,6 +734,18 @@ static void report_bad_exec(CPUState *cpu, target_ulong addr) | |||||||
|     log_cpu_state_mask(LOG_GUEST_ERROR, cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); |     log_cpu_state_mask(LOG_GUEST_ERROR, cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) | ||||||
|  | { | ||||||
|  |     ram_addr_t ram_addr; | ||||||
|  |  | ||||||
|  |     ram_addr = qemu_ram_addr_from_host(ptr); | ||||||
|  |     if (ram_addr == RAM_ADDR_INVALID) { | ||||||
|  |         error_report("Bad ram pointer %p", ptr); | ||||||
|  |         abort(); | ||||||
|  |     } | ||||||
|  |     return ram_addr; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* NOTE: this function can trigger an exception */ | /* NOTE: this function can trigger an exception */ | ||||||
| /* NOTE2: the returned address is not exactly the physical address: it | /* NOTE2: the returned address is not exactly the physical address: it | ||||||
|  * is actually a ram_addr_t (in system mode; the user mode emulation |  * is actually a ram_addr_t (in system mode; the user mode emulation | ||||||
| @@ -475,15 +769,14 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) | |||||||
|     pd = iotlbentry->addr & ~TARGET_PAGE_MASK; |     pd = iotlbentry->addr & ~TARGET_PAGE_MASK; | ||||||
|     mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); |     mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); | ||||||
|     if (memory_region_is_unassigned(mr)) { |     if (memory_region_is_unassigned(mr)) { | ||||||
|         CPUClass *cc = CPU_GET_CLASS(cpu); |         cpu_unassigned_access(cpu, addr, false, true, 0, 4); | ||||||
|  |         /* The CPU's unassigned access hook might have longjumped out | ||||||
|         if (cc->do_unassigned_access) { |          * with an exception. If it didn't (or there was no hook) then | ||||||
|             cc->do_unassigned_access(cpu, addr, false, true, 0, 4); |          * we can't proceed further. | ||||||
|         } else { |          */ | ||||||
|         report_bad_exec(cpu, addr); |         report_bad_exec(cpu, addr); | ||||||
|         exit(1); |         exit(1); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|     p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend); |     p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend); | ||||||
|     return qemu_ram_addr_from_host_nofail(p); |     return qemu_ram_addr_from_host_nofail(p); | ||||||
| } | } | ||||||
| @@ -495,6 +788,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | |||||||
|     hwaddr physaddr = iotlbentry->addr; |     hwaddr physaddr = iotlbentry->addr; | ||||||
|     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); |     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); | ||||||
|     uint64_t val; |     uint64_t val; | ||||||
|  |     bool locked = false; | ||||||
|  |  | ||||||
|     physaddr = (physaddr & TARGET_PAGE_MASK) + addr; |     physaddr = (physaddr & TARGET_PAGE_MASK) + addr; | ||||||
|     cpu->mem_io_pc = retaddr; |     cpu->mem_io_pc = retaddr; | ||||||
| @@ -503,7 +797,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     cpu->mem_io_vaddr = addr; |     cpu->mem_io_vaddr = addr; | ||||||
|  |  | ||||||
|  |     if (mr->global_locking) { | ||||||
|  |         qemu_mutex_lock_iothread(); | ||||||
|  |         locked = true; | ||||||
|  |     } | ||||||
|     memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs); |     memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs); | ||||||
|  |     if (locked) { | ||||||
|  |         qemu_mutex_unlock_iothread(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return val; |     return val; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -514,15 +817,23 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | |||||||
|     CPUState *cpu = ENV_GET_CPU(env); |     CPUState *cpu = ENV_GET_CPU(env); | ||||||
|     hwaddr physaddr = iotlbentry->addr; |     hwaddr physaddr = iotlbentry->addr; | ||||||
|     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); |     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); | ||||||
|  |     bool locked = false; | ||||||
|  |  | ||||||
|     physaddr = (physaddr & TARGET_PAGE_MASK) + addr; |     physaddr = (physaddr & TARGET_PAGE_MASK) + addr; | ||||||
|     if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { |     if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { | ||||||
|         cpu_io_recompile(cpu, retaddr); |         cpu_io_recompile(cpu, retaddr); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     cpu->mem_io_vaddr = addr; |     cpu->mem_io_vaddr = addr; | ||||||
|     cpu->mem_io_pc = retaddr; |     cpu->mem_io_pc = retaddr; | ||||||
|  |  | ||||||
|  |     if (mr->global_locking) { | ||||||
|  |         qemu_mutex_lock_iothread(); | ||||||
|  |         locked = true; | ||||||
|  |     } | ||||||
|     memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs); |     memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs); | ||||||
|  |     if (locked) { | ||||||
|  |         qemu_mutex_unlock_iothread(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Return true if ADDR is present in the victim tlb, and has been copied | /* Return true if ADDR is present in the victim tlb, and has been copied | ||||||
| @@ -538,10 +849,13 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, | |||||||
|         if (cmp == page) { |         if (cmp == page) { | ||||||
|             /* Found entry in victim tlb, swap tlb and iotlb.  */ |             /* Found entry in victim tlb, swap tlb and iotlb.  */ | ||||||
|             CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index]; |             CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index]; | ||||||
|  |  | ||||||
|  |             copy_tlb_helper(&tmptlb, tlb, false); | ||||||
|  |             copy_tlb_helper(tlb, vtlb, true); | ||||||
|  |             copy_tlb_helper(vtlb, &tmptlb, true); | ||||||
|  |  | ||||||
|             CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index]; |             CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index]; | ||||||
|             CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx]; |             CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx]; | ||||||
|  |  | ||||||
|             tmptlb = *tlb; *tlb = *vtlb; *vtlb = tmptlb; |  | ||||||
|             tmpio = *io; *io = *vio; *vio = tmpio; |             tmpio = *io; *io = *vio; *vio = tmpio; | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -63,18 +63,14 @@ static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { | |||||||
|  |  | ||||||
| size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg) | size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg) | ||||||
| { | { | ||||||
|     if (alg >= G_N_ELEMENTS(alg_key_len)) { |     assert(alg < G_N_ELEMENTS(alg_key_len)); | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|     return alg_block_len[alg]; |     return alg_block_len[alg]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg) | size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg) | ||||||
| { | { | ||||||
|     if (alg >= G_N_ELEMENTS(alg_key_len)) { |     assert(alg < G_N_ELEMENTS(alg_key_len)); | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|     return alg_key_len[alg]; |     return alg_key_len[alg]; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -48,6 +48,7 @@ static int qcrypto_ivgen_essiv_init(QCryptoIVGen *ivgen, | |||||||
|                            &salt, &nhash, |                            &salt, &nhash, | ||||||
|                            errp) < 0) { |                            errp) < 0) { | ||||||
|         g_free(essiv); |         g_free(essiv); | ||||||
|  |         g_free(salt); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,3 +10,6 @@ CONFIG_JAZZ=y | |||||||
| CONFIG_G364FB=y | CONFIG_G364FB=y | ||||||
| CONFIG_JAZZ_LED=y | CONFIG_JAZZ_LED=y | ||||||
| CONFIG_VT82C686=y | CONFIG_VT82C686=y | ||||||
|  | CONFIG_MIPS_BOSTON=y | ||||||
|  | CONFIG_FITLOADER=y | ||||||
|  | CONFIG_PCI_XILINX=y | ||||||
|   | |||||||
| @@ -166,8 +166,10 @@ static void dma_blk_cb(void *opaque, int ret) | |||||||
|                                 QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align)); |                                 QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     aio_context_acquire(dbs->ctx); | ||||||
|     dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, |     dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, | ||||||
|                             dma_blk_cb, dbs, dbs->io_func_opaque); |                             dma_blk_cb, dbs, dbs->io_func_opaque); | ||||||
|  |     aio_context_release(dbs->ctx); | ||||||
|     assert(dbs->acb); |     assert(dbs->acb); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										281
									
								
								docs/mach-virt-graphical.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								docs/mach-virt-graphical.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,281 @@ | |||||||
|  | # mach-virt - VirtIO guest (graphical console) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # Usage: | ||||||
|  | # | ||||||
|  | #   $ qemu-system-aarch64 \ | ||||||
|  | #     -nodefaults \ | ||||||
|  | #     -readconfig mach-virt-graphical.cfg \ | ||||||
|  | #     -cpu host | ||||||
|  | # | ||||||
|  | # You will probably need to tweak the lines marked as | ||||||
|  | # CHANGE ME before being able to use this configuration! | ||||||
|  | # | ||||||
|  | # The guest will have a selection of VirtIO devices | ||||||
|  | # tailored towards optimal performance with modern guests, | ||||||
|  | # and will be accessed through a graphical console. | ||||||
|  | # | ||||||
|  | # --------------------------------------------------------- | ||||||
|  | # | ||||||
|  | # Using -nodefaults is required to have full control over | ||||||
|  | # the virtual hardware: when it's specified, QEMU will | ||||||
|  | # populate the board with only the builtin peripherals, | ||||||
|  | # such as the PL011 UART, plus a PCI Express Root Bus; the | ||||||
|  | # user will then have to explicitly add further devices. | ||||||
|  | # | ||||||
|  | # The PCI Express Root Bus shows up in the guest as: | ||||||
|  | # | ||||||
|  | #   00:00.0 Host bridge | ||||||
|  | # | ||||||
|  | # This configuration file adds a number of other useful | ||||||
|  | # devices, more specifically: | ||||||
|  | # | ||||||
|  | #   00:01.0 Display controller | ||||||
|  | #   00.1c.* PCI bridge (PCI Express Root Ports) | ||||||
|  | #   01:00.0 SCSI storage controller | ||||||
|  | #   02:00.0 Ethernet controller | ||||||
|  | #   03:00.0 USB controller | ||||||
|  | # | ||||||
|  | # More information about these devices is available below. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Machine options | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use the virt machine type and enable KVM acceleration | ||||||
|  | # for better performance. | ||||||
|  | # | ||||||
|  | # Using less than 1 GiB of memory is probably not going to | ||||||
|  | # yield good performance in the guest, and might even lead | ||||||
|  | # to obscure boot issues in some cases. | ||||||
|  | # | ||||||
|  | # Unfortunately, there is no way to configure the CPU model | ||||||
|  | # in this file, so it will have to be provided on the | ||||||
|  | # command line, but we can configure the guest to use the | ||||||
|  | # same GIC version as the host. | ||||||
|  |  | ||||||
|  | [machine] | ||||||
|  |   type = "virt" | ||||||
|  |   accel = "kvm" | ||||||
|  |   gic-version = "host" | ||||||
|  |  | ||||||
|  | [memory] | ||||||
|  |   size = "1024" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Firmware configuration | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # There are two parts to the firmware: a read-only image | ||||||
|  | # containing the executable code, which is shared between | ||||||
|  | # guests, and a read/write variable store that is owned | ||||||
|  | # by one specific guest, exclusively, and is used to | ||||||
|  | # record information such as the UEFI boot order. | ||||||
|  | # | ||||||
|  | # For any new guest, its permanent, private variable store | ||||||
|  | # should initially be copied from the template file | ||||||
|  | # provided along with the firmware binary. | ||||||
|  | # | ||||||
|  | # Depending on the OS distribution you're using on the | ||||||
|  | # host, the name of the package containing the firmware | ||||||
|  | # binary and variable store template, as well as the paths | ||||||
|  | # to the files themselves, will be different. For example: | ||||||
|  | # | ||||||
|  | # Fedora | ||||||
|  | #   edk2-aarch64                                      (pkg) | ||||||
|  | #   /usr/share/edk2/aarch64/QEMU_EFI-pflash.raw       (bin) | ||||||
|  | #   /usr/share/edk2/aarch64/vars-template-pflash.raw  (var) | ||||||
|  | # | ||||||
|  | # RHEL | ||||||
|  | #   AAVMF                                             (pkg) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_CODE.fd                    (bin) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_VARS.fd                    (var) | ||||||
|  | # | ||||||
|  | # Debian/Ubuntu | ||||||
|  | #   qemu-efi                                          (pkg) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_CODE.fd                    (bin) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_VARS.fd                    (var) | ||||||
|  |  | ||||||
|  | [drive "uefi-binary"] | ||||||
|  |   file = "/usr/share/AAVMF/AAVMF_CODE.fd"       # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "pflash" | ||||||
|  |   unit = "0" | ||||||
|  |   readonly = "on" | ||||||
|  |  | ||||||
|  | [drive "uefi-varstore"] | ||||||
|  |   file = "guest_VARS.fd"                        # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "pflash" | ||||||
|  |   unit = "1" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # PCI bridge (PCI Express Root Ports) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We create eight PCI Express Root Ports, and we plug them | ||||||
|  | # all into separate functions of the same slot. Some of | ||||||
|  | # them will be used by devices, the rest will remain | ||||||
|  | # available for hotplug. | ||||||
|  |  | ||||||
|  | [device "pcie.1"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.0" | ||||||
|  |   port = "1" | ||||||
|  |   chassis = "1" | ||||||
|  |   multifunction = "on" | ||||||
|  |  | ||||||
|  | [device "pcie.2"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.1" | ||||||
|  |   port = "2" | ||||||
|  |   chassis = "2" | ||||||
|  |  | ||||||
|  | [device "pcie.3"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.2" | ||||||
|  |   port = "3" | ||||||
|  |   chassis = "3" | ||||||
|  |  | ||||||
|  | [device "pcie.4"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.3" | ||||||
|  |   port = "4" | ||||||
|  |   chassis = "4" | ||||||
|  |  | ||||||
|  | [device "pcie.5"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.4" | ||||||
|  |   port = "5" | ||||||
|  |   chassis = "5" | ||||||
|  |  | ||||||
|  | [device "pcie.6"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.5" | ||||||
|  |   port = "6" | ||||||
|  |   chassis = "6" | ||||||
|  |  | ||||||
|  | [device "pcie.7"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.6" | ||||||
|  |   port = "7" | ||||||
|  |   chassis = "7" | ||||||
|  |  | ||||||
|  | [device "pcie.8"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.7" | ||||||
|  |   port = "8" | ||||||
|  |   chassis = "8" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # SCSI storage controller (and storage) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-scsi here so that we can (hot)plug a large | ||||||
|  | # number of disks without running into issues; a SCSI disk, | ||||||
|  | # backed by a qcow2 disk image on the host's filesystem, is | ||||||
|  | # attached to it. | ||||||
|  | # | ||||||
|  | # We also create an optical disk, mostly for installation | ||||||
|  | # purposes: once the guest OS has been succesfully | ||||||
|  | # installed, the guest will no longer boot from optical | ||||||
|  | # media. If you don't want, or no longer want, to have an | ||||||
|  | # optical disk in the guest you can safely comment out | ||||||
|  | # all relevant sections below. | ||||||
|  |  | ||||||
|  | [device "scsi"] | ||||||
|  |   driver = "virtio-scsi-pci" | ||||||
|  |   bus = "pcie.1" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  | [device "scsi-disk"] | ||||||
|  |   driver = "scsi-hd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "disk" | ||||||
|  |   bootindex = "1" | ||||||
|  |  | ||||||
|  | [drive "disk"] | ||||||
|  |   file = "guest.qcow2"                          # CHANGE ME | ||||||
|  |   format = "qcow2" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  | [device "scsi-optical-disk"] | ||||||
|  |   driver = "scsi-cd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "optical-disk" | ||||||
|  |   bootindex = "2" | ||||||
|  |  | ||||||
|  | [drive "optical-disk"] | ||||||
|  |   file = "install.iso"                          # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Ethernet controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-net for improved performance over emulated | ||||||
|  | # hardware; on the host side, we take advantage of user | ||||||
|  | # networking so that the QEMU process doesn't require any | ||||||
|  | # additional privileges. | ||||||
|  |  | ||||||
|  | [netdev "hostnet"] | ||||||
|  |   type = "user" | ||||||
|  |  | ||||||
|  | [device "net"] | ||||||
|  |   driver = "virtio-net-pci" | ||||||
|  |   netdev = "hostnet" | ||||||
|  |   bus = "pcie.2" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # USB controller (and input devices) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We add a virtualization-friendly USB 3.0 controller and | ||||||
|  | # a USB keyboard / USB tablet combo so that graphical | ||||||
|  | # guests can be controlled appropriately. | ||||||
|  |  | ||||||
|  | [device "usb"] | ||||||
|  |   driver = "nec-usb-xhci" | ||||||
|  |   bus = "pcie.3" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  | [device "keyboard"] | ||||||
|  |   driver = "usb-kbd" | ||||||
|  |   bus = "usb.0" | ||||||
|  |  | ||||||
|  | [device "tablet"] | ||||||
|  |   driver = "usb-tablet" | ||||||
|  |   bus = "usb.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Display controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-gpu because the legacy VGA framebuffer is | ||||||
|  | # very troublesome on aarch64, and virtio-gpu is the only | ||||||
|  | # video device that doesn't implement it. | ||||||
|  | # | ||||||
|  | # If you're running the guest on a remote, potentially | ||||||
|  | # headless host, you will probably want to append something | ||||||
|  | # like | ||||||
|  | # | ||||||
|  | #   -display vnc=127.0.0.1:0 | ||||||
|  | # | ||||||
|  | # to the command line in order to prevent QEMU from | ||||||
|  | # creating a graphical display window on the host and | ||||||
|  | # enable remote access instead. | ||||||
|  |  | ||||||
|  | [device "video"] | ||||||
|  |   driver = "virtio-gpu" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "01.0" | ||||||
							
								
								
									
										243
									
								
								docs/mach-virt-serial.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								docs/mach-virt-serial.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | |||||||
|  | # mach-virt - VirtIO guest (serial console) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # Usage: | ||||||
|  | # | ||||||
|  | #   $ qemu-system-aarch64 \ | ||||||
|  | #     -nodefaults \ | ||||||
|  | #     -readconfig mach-virt-serial.cfg \ | ||||||
|  | #     -display none -serial mon:stdio \ | ||||||
|  | #     -cpu host | ||||||
|  | # | ||||||
|  | # You will probably need to tweak the lines marked as | ||||||
|  | # CHANGE ME before being able to use this configuration! | ||||||
|  | # | ||||||
|  | # The guest will have a selection of VirtIO devices | ||||||
|  | # tailored towards optimal performance with modern guests, | ||||||
|  | # and will be accessed through the serial console. | ||||||
|  | # | ||||||
|  | # --------------------------------------------------------- | ||||||
|  | # | ||||||
|  | # Using -nodefaults is required to have full control over | ||||||
|  | # the virtual hardware: when it's specified, QEMU will | ||||||
|  | # populate the board with only the builtin peripherals, | ||||||
|  | # such as the PL011 UART, plus a PCI Express Root Bus; the | ||||||
|  | # user will then have to explicitly add further devices. | ||||||
|  | # | ||||||
|  | # The PCI Express Root Bus shows up in the guest as: | ||||||
|  | # | ||||||
|  | #   00:00.0 Host bridge | ||||||
|  | # | ||||||
|  | # This configuration file adds a number of other useful | ||||||
|  | # devices, more specifically: | ||||||
|  | # | ||||||
|  | #   00.1c.* PCI bridge (PCI Express Root Ports) | ||||||
|  | #   01:00.0 SCSI storage controller | ||||||
|  | #   02:00.0 Ethernet controller | ||||||
|  | # | ||||||
|  | # More information about these devices is available below. | ||||||
|  | # | ||||||
|  | # We use '-display none' to prevent QEMU from creating a | ||||||
|  | # graphical display window, which would serve no use in | ||||||
|  | # this specific configuration, and '-serial mon:stdio' to | ||||||
|  | # multiplex the guest's serial console and the QEMU monitor | ||||||
|  | # to the host's stdio; use 'Ctrl+A h' to learn how to | ||||||
|  | # switch between the two and more. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Machine options | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use the virt machine type and enable KVM acceleration | ||||||
|  | # for better performance. | ||||||
|  | # | ||||||
|  | # Using less than 1 GiB of memory is probably not going to | ||||||
|  | # yield good performance in the guest, and might even lead | ||||||
|  | # to obscure boot issues in some cases. | ||||||
|  | # | ||||||
|  | # Unfortunately, there is no way to configure the CPU model | ||||||
|  | # in this file, so it will have to be provided on the | ||||||
|  | # command line, but we can configure the guest to use the | ||||||
|  | # same GIC version as the host. | ||||||
|  |  | ||||||
|  | [machine] | ||||||
|  |   type = "virt" | ||||||
|  |   accel = "kvm" | ||||||
|  |   gic-version = "host" | ||||||
|  |  | ||||||
|  | [memory] | ||||||
|  |   size = "1024" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Firmware configuration | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # There are two parts to the firmware: a read-only image | ||||||
|  | # containing the executable code, which is shared between | ||||||
|  | # guests, and a read/write variable store that is owned | ||||||
|  | # by one specific guest, exclusively, and is used to | ||||||
|  | # record information such as the UEFI boot order. | ||||||
|  | # | ||||||
|  | # For any new guest, its permanent, private variable store | ||||||
|  | # should initially be copied from the template file | ||||||
|  | # provided along with the firmware binary. | ||||||
|  | # | ||||||
|  | # Depending on the OS distribution you're using on the | ||||||
|  | # host, the name of the package containing the firmware | ||||||
|  | # binary and variable store template, as well as the paths | ||||||
|  | # to the files themselves, will be different. For example: | ||||||
|  | # | ||||||
|  | # Fedora | ||||||
|  | #   edk2-aarch64                                      (pkg) | ||||||
|  | #   /usr/share/edk2/aarch64/QEMU_EFI-pflash.raw       (bin) | ||||||
|  | #   /usr/share/edk2/aarch64/vars-template-pflash.raw  (var) | ||||||
|  | # | ||||||
|  | # RHEL | ||||||
|  | #   AAVMF                                             (pkg) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_CODE.fd                    (bin) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_VARS.fd                    (var) | ||||||
|  | # | ||||||
|  | # Debian/Ubuntu | ||||||
|  | #   qemu-efi                                          (pkg) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_CODE.fd                    (bin) | ||||||
|  | #   /usr/share/AAVMF/AAVMF_VARS.fd                    (var) | ||||||
|  |  | ||||||
|  | [drive "uefi-binary"] | ||||||
|  |   file = "/usr/share/AAVMF/AAVMF_CODE.fd"       # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "pflash" | ||||||
|  |   unit = "0" | ||||||
|  |   readonly = "on" | ||||||
|  |  | ||||||
|  | [drive "uefi-varstore"] | ||||||
|  |   file = "guest_VARS.fd"                        # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "pflash" | ||||||
|  |   unit = "1" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # PCI bridge (PCI Express Root Ports) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We create eight PCI Express Root Ports, and we plug them | ||||||
|  | # all into separate functions of the same slot. Some of | ||||||
|  | # them will be used by devices, the rest will remain | ||||||
|  | # available for hotplug. | ||||||
|  |  | ||||||
|  | [device "pcie.1"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.0" | ||||||
|  |   port = "1" | ||||||
|  |   chassis = "1" | ||||||
|  |   multifunction = "on" | ||||||
|  |  | ||||||
|  | [device "pcie.2"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.1" | ||||||
|  |   port = "2" | ||||||
|  |   chassis = "2" | ||||||
|  |  | ||||||
|  | [device "pcie.3"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.2" | ||||||
|  |   port = "3" | ||||||
|  |   chassis = "3" | ||||||
|  |  | ||||||
|  | [device "pcie.4"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.3" | ||||||
|  |   port = "4" | ||||||
|  |   chassis = "4" | ||||||
|  |  | ||||||
|  | [device "pcie.5"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.4" | ||||||
|  |   port = "5" | ||||||
|  |   chassis = "5" | ||||||
|  |  | ||||||
|  | [device "pcie.6"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.5" | ||||||
|  |   port = "6" | ||||||
|  |   chassis = "6" | ||||||
|  |  | ||||||
|  | [device "pcie.7"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.6" | ||||||
|  |   port = "7" | ||||||
|  |   chassis = "7" | ||||||
|  |  | ||||||
|  | [device "pcie.8"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.7" | ||||||
|  |   port = "8" | ||||||
|  |   chassis = "8" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # SCSI storage controller (and storage) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-scsi here so that we can (hot)plug a large | ||||||
|  | # number of disks without running into issues; a SCSI disk, | ||||||
|  | # backed by a qcow2 disk image on the host's filesystem, is | ||||||
|  | # attached to it. | ||||||
|  | # | ||||||
|  | # We also create an optical disk, mostly for installation | ||||||
|  | # purposes: once the guest OS has been succesfully | ||||||
|  | # installed, the guest will no longer boot from optical | ||||||
|  | # media. If you don't want, or no longer want, to have an | ||||||
|  | # optical disk in the guest you can safely comment out | ||||||
|  | # all relevant sections below. | ||||||
|  |  | ||||||
|  | [device "scsi"] | ||||||
|  |   driver = "virtio-scsi-pci" | ||||||
|  |   bus = "pcie.1" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  | [device "scsi-disk"] | ||||||
|  |   driver = "scsi-hd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "disk" | ||||||
|  |   bootindex = "1" | ||||||
|  |  | ||||||
|  | [drive "disk"] | ||||||
|  |   file = "guest.qcow2"                          # CHANGE ME | ||||||
|  |   format = "qcow2" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  | [device "scsi-optical-disk"] | ||||||
|  |   driver = "scsi-cd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "optical-disk" | ||||||
|  |   bootindex = "2" | ||||||
|  |  | ||||||
|  | [drive "optical-disk"] | ||||||
|  |   file = "install.iso"                          # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Ethernet controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-net for improved performance over emulated | ||||||
|  | # hardware; on the host side, we take advantage of user | ||||||
|  | # networking so that the QEMU process doesn't require any | ||||||
|  | # additional privileges. | ||||||
|  |  | ||||||
|  | [netdev "hostnet"] | ||||||
|  |   type = "user" | ||||||
|  |  | ||||||
|  | [device "net"] | ||||||
|  |   driver = "virtio-net-pci" | ||||||
|  |   netdev = "hostnet" | ||||||
|  |   bus = "pcie.2" | ||||||
|  |   addr = "00.0" | ||||||
							
								
								
									
										350
									
								
								docs/multi-thread-tcg.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								docs/multi-thread-tcg.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,350 @@ | |||||||
|  | Copyright (c) 2015-2016 Linaro Ltd. | ||||||
|  |  | ||||||
|  | This work is licensed under the terms of the GNU GPL, version 2 or | ||||||
|  | later. See the COPYING file in the top-level directory. | ||||||
|  |  | ||||||
|  | Introduction | ||||||
|  | ============ | ||||||
|  |  | ||||||
|  | This document outlines the design for multi-threaded TCG system-mode | ||||||
|  | emulation. The current user-mode emulation mirrors the thread | ||||||
|  | structure of the translated executable. Some of the work will be | ||||||
|  | applicable to both system and linux-user emulation. | ||||||
|  |  | ||||||
|  | The original system-mode TCG implementation was single threaded and | ||||||
|  | dealt with multiple CPUs with simple round-robin scheduling. This | ||||||
|  | simplified a lot of things but became increasingly limited as systems | ||||||
|  | being emulated gained additional cores and per-core performance gains | ||||||
|  | for host systems started to level off. | ||||||
|  |  | ||||||
|  | vCPU Scheduling | ||||||
|  | =============== | ||||||
|  |  | ||||||
|  | We introduce a new running mode where each vCPU will run on its own | ||||||
|  | user-space thread. This will be enabled by default for all FE/BE | ||||||
|  | combinations that have had the required work done to support this | ||||||
|  | safely. | ||||||
|  |  | ||||||
|  | In the general case of running translated code there should be no | ||||||
|  | inter-vCPU dependencies and all vCPUs should be able to run at full | ||||||
|  | speed. Synchronisation will only be required while accessing internal | ||||||
|  | shared data structures or when the emulated architecture requires a | ||||||
|  | coherent representation of the emulated machine state. | ||||||
|  |  | ||||||
|  | Shared Data Structures | ||||||
|  | ====================== | ||||||
|  |  | ||||||
|  | Main Run Loop | ||||||
|  | ------------- | ||||||
|  |  | ||||||
|  | Even when there is no code being generated there are a number of | ||||||
|  | structures associated with the hot-path through the main run-loop. | ||||||
|  | These are associated with looking up the next translation block to | ||||||
|  | execute. These include: | ||||||
|  |  | ||||||
|  |     tb_jmp_cache (per-vCPU, cache of recent jumps) | ||||||
|  |     tb_ctx.htable (global hash table, phys address->tb lookup) | ||||||
|  |  | ||||||
|  | As TB linking only occurs when blocks are in the same page this code | ||||||
|  | is critical to performance as looking up the next TB to execute is the | ||||||
|  | most common reason to exit the generated code. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENT: Make access to lookup structures safe with | ||||||
|  | multiple reader/writer threads. Minimise any lock contention to do it. | ||||||
|  |  | ||||||
|  | The hot-path avoids using locks where possible. The tb_jmp_cache is | ||||||
|  | updated with atomic accesses to ensure consistent results. The fall | ||||||
|  | back QHT based hash table is also designed for lockless lookups. Locks | ||||||
|  | are only taken when code generation is required or TranslationBlocks | ||||||
|  | have their block-to-block jumps patched. | ||||||
|  |  | ||||||
|  | Global TCG State | ||||||
|  | ---------------- | ||||||
|  |  | ||||||
|  | We need to protect the entire code generation cycle including any post | ||||||
|  | generation patching of the translated code. This also implies a shared | ||||||
|  | translation buffer which contains code running on all cores. Any | ||||||
|  | execution path that comes to the main run loop will need to hold a | ||||||
|  | mutex for code generation. This also includes times when we need flush | ||||||
|  | code or entries from any shared lookups/caches. Structures held on a | ||||||
|  | per-vCPU basis won't need locking unless other vCPUs will need to | ||||||
|  | modify them. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENT: Add locking around all code generation and TB | ||||||
|  | patching. | ||||||
|  |  | ||||||
|  | (Current solution) | ||||||
|  |  | ||||||
|  | Mainly as part of the linux-user work all code generation is | ||||||
|  | serialised with a tb_lock(). For the SoftMMU tb_lock() also takes the | ||||||
|  | place of mmap_lock() in linux-user. | ||||||
|  |  | ||||||
|  | Translation Blocks | ||||||
|  | ------------------ | ||||||
|  |  | ||||||
|  | Currently the whole system shares a single code generation buffer | ||||||
|  | which when full will force a flush of all translations and start from | ||||||
|  | scratch again. Some operations also force a full flush of translations | ||||||
|  | including: | ||||||
|  |  | ||||||
|  |   - debugging operations (breakpoint insertion/removal) | ||||||
|  |   - some CPU helper functions | ||||||
|  |  | ||||||
|  | This is done with the async_safe_run_on_cpu() mechanism to ensure all | ||||||
|  | vCPUs are quiescent when changes are being made to shared global | ||||||
|  | structures. | ||||||
|  |  | ||||||
|  | More granular translation invalidation events are typically due | ||||||
|  | to a change of the state of a physical page: | ||||||
|  |  | ||||||
|  |   - code modification (self modify code, patching code) | ||||||
|  |   - page changes (new page mapping in linux-user mode) | ||||||
|  |  | ||||||
|  | While setting the invalid flag in a TranslationBlock will stop it | ||||||
|  | being used when looked up in the hot-path there are a number of other | ||||||
|  | book-keeping structures that need to be safely cleared. | ||||||
|  |  | ||||||
|  | Any TranslationBlocks which have been patched to jump directly to the | ||||||
|  | now invalid blocks need the jump patches reversing so they will return | ||||||
|  | to the C code. | ||||||
|  |  | ||||||
|  | There are a number of look-up caches that need to be properly updated | ||||||
|  | including the: | ||||||
|  |  | ||||||
|  |   - jump lookup cache | ||||||
|  |   - the physical-to-tb lookup hash table | ||||||
|  |   - the global page table | ||||||
|  |  | ||||||
|  | The global page table (l1_map) which provides a multi-level look-up | ||||||
|  | for PageDesc structures which contain pointers to the start of a | ||||||
|  | linked list of all Translation Blocks in that page (see page_next). | ||||||
|  |  | ||||||
|  | Both the jump patching and the page cache involve linked lists that | ||||||
|  | the invalidated TranslationBlock needs to be removed from. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENT: Safely handle invalidation of TBs | ||||||
|  |                       - safely patch/revert direct jumps | ||||||
|  |                       - remove central PageDesc lookup entries | ||||||
|  |                       - ensure lookup caches/hashes are safely updated | ||||||
|  |  | ||||||
|  | (Current solution) | ||||||
|  |  | ||||||
|  | The direct jump themselves are updated atomically by the TCG | ||||||
|  | tb_set_jmp_target() code. Modification to the linked lists that allow | ||||||
|  | searching for linked pages are done under the protect of the | ||||||
|  | tb_lock(). | ||||||
|  |  | ||||||
|  | The global page table is protected by the tb_lock() in system-mode and | ||||||
|  | mmap_lock() in linux-user mode. | ||||||
|  |  | ||||||
|  | The lookup caches are updated atomically and the lookup hash uses QHT | ||||||
|  | which is designed for concurrent safe lookup. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Memory maps and TLBs | ||||||
|  | -------------------- | ||||||
|  |  | ||||||
|  | The memory handling code is fairly critical to the speed of memory | ||||||
|  | access in the emulated system. The SoftMMU code is designed so the | ||||||
|  | hot-path can be handled entirely within translated code. This is | ||||||
|  | handled with a per-vCPU TLB structure which once populated will allow | ||||||
|  | a series of accesses to the page to occur without exiting the | ||||||
|  | translated code. It is possible to set flags in the TLB address which | ||||||
|  | will ensure the slow-path is taken for each access. This can be done | ||||||
|  | to support: | ||||||
|  |  | ||||||
|  |   - Memory regions (dividing up access to PIO, MMIO and RAM) | ||||||
|  |   - Dirty page tracking (for code gen, SMC detection, migration and display) | ||||||
|  |   - Virtual TLB (for translating guest address->real address) | ||||||
|  |  | ||||||
|  | When the TLB tables are updated by a vCPU thread other than their own | ||||||
|  | we need to ensure it is done in a safe way so no inconsistent state is | ||||||
|  | seen by the vCPU thread. | ||||||
|  |  | ||||||
|  | Some operations require updating a number of vCPUs TLBs at the same | ||||||
|  | time in a synchronised manner. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENTS: | ||||||
|  |  | ||||||
|  |   - TLB Flush All/Page | ||||||
|  |     - can be across-vCPUs | ||||||
|  |     - cross vCPU TLB flush may need other vCPU brought to halt | ||||||
|  |     - change may need to be visible to the calling vCPU immediately | ||||||
|  |   - TLB Flag Update | ||||||
|  |     - usually cross-vCPU | ||||||
|  |     - want change to be visible as soon as possible | ||||||
|  |   - TLB Update (update a CPUTLBEntry, via tlb_set_page_with_attrs) | ||||||
|  |     - This is a per-vCPU table - by definition can't race | ||||||
|  |     - updated by its own thread when the slow-path is forced | ||||||
|  |  | ||||||
|  | (Current solution) | ||||||
|  |  | ||||||
|  | We have updated cputlb.c to defer operations when a cross-vCPU | ||||||
|  | operation with async_run_on_cpu() which ensures each vCPU sees a | ||||||
|  | coherent state when it next runs its work (in a few instructions | ||||||
|  | time). | ||||||
|  |  | ||||||
|  | A new set up operations (tlb_flush_*_all_cpus) take an additional flag | ||||||
|  | which when set will force synchronisation by setting the source vCPUs | ||||||
|  | work as "safe work" and exiting the cpu run loop. This ensure by the | ||||||
|  | time execution restarts all flush operations have completed. | ||||||
|  |  | ||||||
|  | TLB flag updates are all done atomically and are also protected by the | ||||||
|  | tb_lock() which is used by the functions that update the TLB in bulk. | ||||||
|  |  | ||||||
|  | (Known limitation) | ||||||
|  |  | ||||||
|  | Not really a limitation but the wait mechanism is overly strict for | ||||||
|  | some architectures which only need flushes completed by a barrier | ||||||
|  | instruction. This could be a future optimisation. | ||||||
|  |  | ||||||
|  | Emulated hardware state | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | Currently thanks to KVM work any access to IO memory is automatically | ||||||
|  | protected by the global iothread mutex, also known as the BQL (Big | ||||||
|  | Qemu Lock). Any IO region that doesn't use global mutex is expected to | ||||||
|  | do its own locking. | ||||||
|  |  | ||||||
|  | However IO memory isn't the only way emulated hardware state can be | ||||||
|  | modified. Some architectures have model specific registers that | ||||||
|  | trigger hardware emulation features. Generally any translation helper | ||||||
|  | that needs to update more than a single vCPUs of state should take the | ||||||
|  | BQL. | ||||||
|  |  | ||||||
|  | As the BQL, or global iothread mutex is shared across the system we | ||||||
|  | push the use of the lock as far down into the TCG code as possible to | ||||||
|  | minimise contention. | ||||||
|  |  | ||||||
|  | (Current solution) | ||||||
|  |  | ||||||
|  | MMIO access automatically serialises hardware emulation by way of the | ||||||
|  | BQL. Currently ARM targets serialise all ARM_CP_IO register accesses | ||||||
|  | and also defer the reset/startup of vCPUs to the vCPU context by way | ||||||
|  | of async_run_on_cpu(). | ||||||
|  |  | ||||||
|  | Updates to interrupt state are also protected by the BQL as they can | ||||||
|  | often be cross vCPU. | ||||||
|  |  | ||||||
|  | Memory Consistency | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  | Between emulated guests and host systems there are a range of memory | ||||||
|  | consistency models. Even emulating weakly ordered systems on strongly | ||||||
|  | ordered hosts needs to ensure things like store-after-load re-ordering | ||||||
|  | can be prevented when the guest wants to. | ||||||
|  |  | ||||||
|  | Memory Barriers | ||||||
|  | --------------- | ||||||
|  |  | ||||||
|  | Barriers (sometimes known as fences) provide a mechanism for software | ||||||
|  | to enforce a particular ordering of memory operations from the point | ||||||
|  | of view of external observers (e.g. another processor core). They can | ||||||
|  | apply to any memory operations as well as just loads or stores. | ||||||
|  |  | ||||||
|  | The Linux kernel has an excellent write-up on the various forms of | ||||||
|  | memory barrier and the guarantees they can provide [1]. | ||||||
|  |  | ||||||
|  | Barriers are often wrapped around synchronisation primitives to | ||||||
|  | provide explicit memory ordering semantics. However they can be used | ||||||
|  | by themselves to provide safe lockless access by ensuring for example | ||||||
|  | a change to a signal flag will only be visible once the changes to | ||||||
|  | payload are. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENT: Add a new tcg_memory_barrier op | ||||||
|  |  | ||||||
|  | This would enforce a strong load/store ordering so all loads/stores | ||||||
|  | complete at the memory barrier. On single-core non-SMP strongly | ||||||
|  | ordered backends this could become a NOP. | ||||||
|  |  | ||||||
|  | Aside from explicit standalone memory barrier instructions there are | ||||||
|  | also implicit memory ordering semantics which comes with each guest | ||||||
|  | memory access instruction. For example all x86 load/stores come with | ||||||
|  | fairly strong guarantees of sequential consistency where as ARM has | ||||||
|  | special variants of load/store instructions that imply acquire/release | ||||||
|  | semantics. | ||||||
|  |  | ||||||
|  | In the case of a strongly ordered guest architecture being emulated on | ||||||
|  | a weakly ordered host the scope for a heavy performance impact is | ||||||
|  | quite high. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENTS: Be efficient with use of memory barriers | ||||||
|  |        - host systems with stronger implied guarantees can skip some barriers | ||||||
|  |        - merge consecutive barriers to the strongest one | ||||||
|  |  | ||||||
|  | (Current solution) | ||||||
|  |  | ||||||
|  | The system currently has a tcg_gen_mb() which will add memory barrier | ||||||
|  | operations if code generation is being done in a parallel context. The | ||||||
|  | tcg_optimize() function attempts to merge barriers up to their | ||||||
|  | strongest form before any load/store operations. The solution was | ||||||
|  | originally developed and tested for linux-user based systems. All | ||||||
|  | backends have been converted to emit fences when required. So far the | ||||||
|  | following front-ends have been updated to emit fences when required: | ||||||
|  |  | ||||||
|  |     - target-i386 | ||||||
|  |     - target-arm | ||||||
|  |     - target-aarch64 | ||||||
|  |     - target-alpha | ||||||
|  |     - target-mips | ||||||
|  |  | ||||||
|  | Memory Control and Maintenance | ||||||
|  | ------------------------------ | ||||||
|  |  | ||||||
|  | This includes a class of instructions for controlling system cache | ||||||
|  | behaviour. While QEMU doesn't model cache behaviour these instructions | ||||||
|  | are often seen when code modification has taken place to ensure the | ||||||
|  | changes take effect. | ||||||
|  |  | ||||||
|  | Synchronisation Primitives | ||||||
|  | -------------------------- | ||||||
|  |  | ||||||
|  | There are two broad types of synchronisation primitives found in | ||||||
|  | modern ISAs: atomic instructions and exclusive regions. | ||||||
|  |  | ||||||
|  | The first type offer a simple atomic instruction which will guarantee | ||||||
|  | some sort of test and conditional store will be truly atomic w.r.t. | ||||||
|  | other cores sharing access to the memory. The classic example is the | ||||||
|  | x86 cmpxchg instruction. | ||||||
|  |  | ||||||
|  | The second type offer a pair of load/store instructions which offer a | ||||||
|  | guarantee that an region of memory has not been touched between the | ||||||
|  | load and store instructions. An example of this is ARM's ldrex/strex | ||||||
|  | pair where the strex instruction will return a flag indicating a | ||||||
|  | successful store only if no other CPU has accessed the memory region | ||||||
|  | since the ldrex. | ||||||
|  |  | ||||||
|  | Traditionally TCG has generated a series of operations that work | ||||||
|  | because they are within the context of a single translation block so | ||||||
|  | will have completed before another CPU is scheduled. However with | ||||||
|  | the ability to have multiple threads running to emulate multiple CPUs | ||||||
|  | we will need to explicitly expose these semantics. | ||||||
|  |  | ||||||
|  | DESIGN REQUIREMENTS: | ||||||
|  |   - Support classic atomic instructions | ||||||
|  |   - Support load/store exclusive (or load link/store conditional) pairs | ||||||
|  |   - Generic enough infrastructure to support all guest architectures | ||||||
|  | CURRENT OPEN QUESTIONS: | ||||||
|  |   - How problematic is the ABA problem in general? | ||||||
|  |  | ||||||
|  | (Current solution) | ||||||
|  |  | ||||||
|  | The TCG provides a number of atomic helpers (tcg_gen_atomic_*) which | ||||||
|  | can be used directly or combined to emulate other instructions like | ||||||
|  | ARM's ldrex/strex instructions. While they are susceptible to the ABA | ||||||
|  | problem so far common guests have not implemented patterns where | ||||||
|  | this may be a problem - typically presenting a locking ABI which | ||||||
|  | assumes cmpxchg like semantics. | ||||||
|  |  | ||||||
|  | The code also includes a fall-back for cases where multi-threaded TCG | ||||||
|  | ops can't work (e.g. guest atomic width > host atomic width). In this | ||||||
|  | case an EXCP_ATOMIC exit occurs and the instruction is emulated with | ||||||
|  | an exclusive lock which ensures all emulation is serialised. | ||||||
|  |  | ||||||
|  | While the atomic helpers look good enough for now there may be a need | ||||||
|  | to look at solutions that can more closely model the guest | ||||||
|  | architectures semantics. | ||||||
|  |  | ||||||
|  | ========== | ||||||
|  |  | ||||||
|  | [1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/memory-barriers.txt | ||||||
							
								
								
									
										124
									
								
								docs/nvdimm.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								docs/nvdimm.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | QEMU Virtual NVDIMM | ||||||
|  | =================== | ||||||
|  |  | ||||||
|  | This document explains the usage of virtual NVDIMM (vNVDIMM) feature | ||||||
|  | which is available since QEMU v2.6.0. | ||||||
|  |  | ||||||
|  | The current QEMU only implements the persistent memory mode of vNVDIMM | ||||||
|  | device and not the block window mode. | ||||||
|  |  | ||||||
|  | Basic Usage | ||||||
|  | ----------- | ||||||
|  |  | ||||||
|  | The storage of a vNVDIMM device in QEMU is provided by the memory | ||||||
|  | backend (i.e. memory-backend-file and memory-backend-ram). A simple | ||||||
|  | way to create a vNVDIMM device at startup time is done via the | ||||||
|  | following command line options: | ||||||
|  |  | ||||||
|  |  -machine pc,nvdimm | ||||||
|  |  -m $RAM_SIZE,slots=$N,maxmem=$MAX_SIZE | ||||||
|  |  -object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE | ||||||
|  |  -device nvdimm,id=nvdimm1,memdev=mem1 | ||||||
|  |  | ||||||
|  | Where, | ||||||
|  |  | ||||||
|  |  - the "nvdimm" machine option enables vNVDIMM feature. | ||||||
|  |  | ||||||
|  |  - "slots=$N" should be equal to or larger than the total amount of | ||||||
|  |    normal RAM devices and vNVDIMM devices, e.g. $N should be >= 2 here. | ||||||
|  |  | ||||||
|  |  - "maxmem=$MAX_SIZE" should be equal to or larger than the total size | ||||||
|  |    of normal RAM devices and vNVDIMM devices, e.g. $MAX_SIZE should be | ||||||
|  |    >= $RAM_SIZE + $NVDIMM_SIZE here. | ||||||
|  |  | ||||||
|  |  - "object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE" | ||||||
|  |    creates a backend storage of size $NVDIMM_SIZE on a file $PATH. All | ||||||
|  |    accesses to the virtual NVDIMM device go to the file $PATH. | ||||||
|  |  | ||||||
|  |    "share=on/off" controls the visibility of guest writes. If | ||||||
|  |    "share=on", then guest writes will be applied to the backend | ||||||
|  |    file. If another guest uses the same backend file with option | ||||||
|  |    "share=on", then above writes will be visible to it as well. If | ||||||
|  |    "share=off", then guest writes won't be applied to the backend | ||||||
|  |    file and thus will be invisible to other guests. | ||||||
|  |  | ||||||
|  |  - "device nvdimm,id=nvdimm1,memdev=mem1" creates a virtual NVDIMM | ||||||
|  |    device whose storage is provided by above memory backend device. | ||||||
|  |  | ||||||
|  | Multiple vNVDIMM devices can be created if multiple pairs of "-object" | ||||||
|  | and "-device" are provided. | ||||||
|  |  | ||||||
|  | For above command line options, if the guest OS has the proper NVDIMM | ||||||
|  | driver, it should be able to detect a NVDIMM device which is in the | ||||||
|  | persistent memory mode and whose size is $NVDIMM_SIZE. | ||||||
|  |  | ||||||
|  | Note: | ||||||
|  |  | ||||||
|  | 1. Prior to QEMU v2.8.0, if memory-backend-file is used and the actual | ||||||
|  |    backend file size is not equal to the size given by "size" option, | ||||||
|  |    QEMU will truncate the backend file by ftruncate(2), which will | ||||||
|  |    corrupt the existing data in the backend file, especially for the | ||||||
|  |    shrink case. | ||||||
|  |  | ||||||
|  |    QEMU v2.8.0 and later check the backend file size and the "size" | ||||||
|  |    option. If they do not match, QEMU will report errors and abort in | ||||||
|  |    order to avoid the data corruption. | ||||||
|  |  | ||||||
|  | 2. QEMU v2.6.0 only puts a basic alignment requirement on the "size" | ||||||
|  |    option of memory-backend-file, e.g. 4KB alignment on x86.  However, | ||||||
|  |    QEMU v.2.7.0 puts an additional alignment requirement, which may | ||||||
|  |    require a larger value than the basic one, e.g. 2MB on x86. This | ||||||
|  |    change breaks the usage of memory-backend-file that only satisfies | ||||||
|  |    the basic alignment. | ||||||
|  |  | ||||||
|  |    QEMU v2.8.0 and later remove the additional alignment on non-s390x | ||||||
|  |    architectures, so the broken memory-backend-file can work again. | ||||||
|  |  | ||||||
|  | Label | ||||||
|  | ----- | ||||||
|  |  | ||||||
|  | QEMU v2.7.0 and later implement the label support for vNVDIMM devices. | ||||||
|  | To enable label on vNVDIMM devices, users can simply add | ||||||
|  | "label-size=$SZ" option to "-device nvdimm", e.g. | ||||||
|  |  | ||||||
|  |  -device nvdimm,id=nvdimm1,memdev=mem1,label-size=128K | ||||||
|  |  | ||||||
|  | Note: | ||||||
|  |  | ||||||
|  | 1. The minimal label size is 128KB. | ||||||
|  |  | ||||||
|  | 2. QEMU v2.7.0 and later store labels at the end of backend storage. | ||||||
|  |    If a memory backend file, which was previously used as the backend | ||||||
|  |    of a vNVDIMM device without labels, is now used for a vNVDIMM | ||||||
|  |    device with label, the data in the label area at the end of file | ||||||
|  |    will be inaccessible to the guest. If any useful data (e.g. the | ||||||
|  |    meta-data of the file system) was stored there, the latter usage | ||||||
|  |    may result guest data corruption (e.g. breakage of guest file | ||||||
|  |    system). | ||||||
|  |  | ||||||
|  | Hotplug | ||||||
|  | ------- | ||||||
|  |  | ||||||
|  | QEMU v2.8.0 and later implement the hotplug support for vNVDIMM | ||||||
|  | devices. Similarly to the RAM hotplug, the vNVDIMM hotplug is | ||||||
|  | accomplished by two monitor commands "object_add" and "device_add". | ||||||
|  |  | ||||||
|  | For example, the following commands add another 4GB vNVDIMM device to | ||||||
|  | the guest: | ||||||
|  |  | ||||||
|  |  (qemu) object_add memory-backend-file,id=mem2,share=on,mem-path=new_nvdimm.img,size=4G | ||||||
|  |  (qemu) device_add nvdimm,id=nvdimm2,memdev=mem2 | ||||||
|  |  | ||||||
|  | Note: | ||||||
|  |  | ||||||
|  | 1. Each hotplugged vNVDIMM device consumes one memory slot. Users | ||||||
|  |    should always ensure the memory option "-m ...,slots=N" specifies | ||||||
|  |    enough number of slots, i.e. | ||||||
|  |      N >= number of RAM devices + | ||||||
|  |           number of statically plugged vNVDIMM devices + | ||||||
|  |           number of hotplugged vNVDIMM devices | ||||||
|  |  | ||||||
|  | 2. The similar is required for the memory option "-m ...,maxmem=M", i.e. | ||||||
|  |      M >= size of RAM devices + | ||||||
|  |           size of statically plugged vNVDIMM devices + | ||||||
|  |           size of hotplugged vNVDIMM devices | ||||||
| @@ -1,152 +0,0 @@ | |||||||
| ################################################################ |  | ||||||
| # |  | ||||||
| # qemu -M q35 creates a bare machine with just the very essential |  | ||||||
| # chipset devices being present: |  | ||||||
| # |  | ||||||
| #     00.0 - Host bridge |  | ||||||
| #     1f.0 - ISA bridge / LPC |  | ||||||
| #     1f.2 - SATA (AHCI) controller |  | ||||||
| #     1f.3 - SMBus controller |  | ||||||
| # |  | ||||||
| # This config file documents the other devices and how they are |  | ||||||
| # created.  You can simply use "-readconfig $thisfile" to create |  | ||||||
| # them all.  Here is a overview: |  | ||||||
| # |  | ||||||
| #     19.0 - Ethernet controller (not created, our e1000 emulation |  | ||||||
| #                                 doesn't emulate the ich9 device). |  | ||||||
| #     1a.* - USB Controller #2 (ehci + uhci companions) |  | ||||||
| #     1b.0 - HD Audio Controller |  | ||||||
| #     1c.* - PCI Express Ports |  | ||||||
| #     1d.* - USB Controller #1 (ehci + uhci companions, |  | ||||||
| #                               "qemu -M q35 -usb" creates these too) |  | ||||||
| #     1e.0 - PCI Bridge |  | ||||||
| # |  | ||||||
|  |  | ||||||
| [device "ich9-ehci-2"] |  | ||||||
|   driver = "ich9-usb-ehci2" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1a.7" |  | ||||||
|  |  | ||||||
| [device "ich9-uhci-4"] |  | ||||||
|   driver = "ich9-usb-uhci4" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1a.0" |  | ||||||
|   masterbus = "ich9-ehci-2.0" |  | ||||||
|   firstport = "0" |  | ||||||
|  |  | ||||||
| [device "ich9-uhci-5"] |  | ||||||
|   driver = "ich9-usb-uhci5" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1a.1" |  | ||||||
|   masterbus = "ich9-ehci-2.0" |  | ||||||
|   firstport = "2" |  | ||||||
|  |  | ||||||
| [device "ich9-uhci-6"] |  | ||||||
|   driver = "ich9-usb-uhci6" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1a.2" |  | ||||||
|   masterbus = "ich9-ehci-2.0" |  | ||||||
|   firstport = "4" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| [device "ich9-hda-audio"] |  | ||||||
|   driver = "ich9-intel-hda" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1b.0" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| [device "ich9-pcie-port-1"] |  | ||||||
|   driver = "ioh3420" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1c.0" |  | ||||||
|   port = "1" |  | ||||||
|   chassis = "1" |  | ||||||
|  |  | ||||||
| [device "ich9-pcie-port-2"] |  | ||||||
|   driver = "ioh3420" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1c.1" |  | ||||||
|   port = "2" |  | ||||||
|   chassis = "2" |  | ||||||
|  |  | ||||||
| [device "ich9-pcie-port-3"] |  | ||||||
|   driver = "ioh3420" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1c.2" |  | ||||||
|   port = "3" |  | ||||||
|   chassis = "3" |  | ||||||
|  |  | ||||||
| [device "ich9-pcie-port-4"] |  | ||||||
|   driver = "ioh3420" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1c.3" |  | ||||||
|   port = "4" |  | ||||||
|   chassis = "4" |  | ||||||
|  |  | ||||||
| ## |  | ||||||
| # Example PCIe switch with two downstream ports |  | ||||||
| # |  | ||||||
| #[device "pcie-switch-upstream-port-1"] |  | ||||||
| #  driver = "x3130-upstream" |  | ||||||
| #  bus = "ich9-pcie-port-4" |  | ||||||
| #  addr = "00.0" |  | ||||||
| # |  | ||||||
| #[device "pcie-switch-downstream-port-1-1"] |  | ||||||
| #  driver = "xio3130-downstream" |  | ||||||
| #  multifunction = "on" |  | ||||||
| #  bus = "pcie-switch-upstream-port-1" |  | ||||||
| #  addr = "00.0" |  | ||||||
| #  port = "1" |  | ||||||
| #  chassis = "5" |  | ||||||
| # |  | ||||||
| #[device "pcie-switch-downstream-port-1-2"] |  | ||||||
| #  driver = "xio3130-downstream" |  | ||||||
| #  multifunction = "on" |  | ||||||
| #  bus = "pcie-switch-upstream-port-1" |  | ||||||
| #  addr = "00.1" |  | ||||||
| #  port = "1" |  | ||||||
| #  chassis = "6" |  | ||||||
|  |  | ||||||
| [device "ich9-ehci-1"] |  | ||||||
|   driver = "ich9-usb-ehci1" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1d.7" |  | ||||||
|  |  | ||||||
| [device "ich9-uhci-1"] |  | ||||||
|   driver = "ich9-usb-uhci1" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1d.0" |  | ||||||
|   masterbus = "ich9-ehci-1.0" |  | ||||||
|   firstport = "0" |  | ||||||
|  |  | ||||||
| [device "ich9-uhci-2"] |  | ||||||
|   driver = "ich9-usb-uhci2" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1d.1" |  | ||||||
|   masterbus = "ich9-ehci-1.0" |  | ||||||
|   firstport = "2" |  | ||||||
|  |  | ||||||
| [device "ich9-uhci-3"] |  | ||||||
|   driver = "ich9-usb-uhci3" |  | ||||||
|   multifunction = "on" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1d.2" |  | ||||||
|   masterbus = "ich9-ehci-1.0" |  | ||||||
|   firstport = "4" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| [device "ich9-pci-bridge"] |  | ||||||
|   driver = "i82801b11-bridge" |  | ||||||
|   bus = "pcie.0" |  | ||||||
|   addr = "1e.0" |  | ||||||
							
								
								
									
										288
									
								
								docs/q35-emulated.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								docs/q35-emulated.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | # q35 - Emulated guest (graphical console) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # Usage: | ||||||
|  | # | ||||||
|  | #   $ qemu-system-x86_64 \ | ||||||
|  | #     -nodefaults \ | ||||||
|  | #     -readconfig q35-emulated.cfg | ||||||
|  | # | ||||||
|  | # You will probably need to tweak the lines marked as | ||||||
|  | # CHANGE ME before being able to use this configuration! | ||||||
|  | # | ||||||
|  | # The guest will have a selection of emulated devices that | ||||||
|  | # closely resembles that of a physical machine, and will be | ||||||
|  | # accessed through a graphical console. | ||||||
|  | # | ||||||
|  | # --------------------------------------------------------- | ||||||
|  | # | ||||||
|  | # Using -nodefaults is required to have full control over | ||||||
|  | # the virtual hardware: when it's specified, QEMU will | ||||||
|  | # populate the board with only the builtin peripherals | ||||||
|  | # plus a small selection of core PCI devices and | ||||||
|  | # controllers; the user will then have to explicitly add | ||||||
|  | # further devices. | ||||||
|  | # | ||||||
|  | # The core PCI devices show up in the guest as: | ||||||
|  | # | ||||||
|  | #   00:00.0 Host bridge | ||||||
|  | #   00:1f.0 ISA bridge / LPC | ||||||
|  | #   00:1f.2 SATA (AHCI) controller | ||||||
|  | #   00:1f.3 SMBus controller | ||||||
|  | # | ||||||
|  | # This configuration file adds a number of devices that | ||||||
|  | # are pretty much guaranteed to be present in every single | ||||||
|  | # physical machine based on q35, more specifically: | ||||||
|  | # | ||||||
|  | #   00:01.0 VGA compatible controller | ||||||
|  | #   00:19.0 Ethernet controller | ||||||
|  | #   00:1a.* USB controller (#2) | ||||||
|  | #   00:1b.0 Audio device | ||||||
|  | #   00:1c.* PCI bridge (PCI Express Root Ports) | ||||||
|  | #   00:1d.* USB Controller (#1) | ||||||
|  | #   00:1e.0 PCI bridge (legacy PCI bridge) | ||||||
|  | # | ||||||
|  | # More information about these devices is available below. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Machine options | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use the q35 machine type and enable KVM acceleration | ||||||
|  | # for better performance. | ||||||
|  | # | ||||||
|  | # Using less than 1 GiB of memory is probably not going to | ||||||
|  | # yield good performance in the guest, and might even lead | ||||||
|  | # to obscure boot issues in some cases. | ||||||
|  | # | ||||||
|  | # Unfortunately, there is no way to configure the CPU model | ||||||
|  | # in this file, so it will have to be provided on the | ||||||
|  | # command line. | ||||||
|  |  | ||||||
|  | [machine] | ||||||
|  |   type = "q35" | ||||||
|  |   accel = "kvm" | ||||||
|  |  | ||||||
|  | [memory] | ||||||
|  |   size = "1024" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # PCI bridge (PCI Express Root Ports) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We add four PCI Express Root Ports, all sharing the same | ||||||
|  | # slot on the PCI Express  Root Bus. These ports support | ||||||
|  | # hotplug. | ||||||
|  |  | ||||||
|  | [device "ich9-pcie-port-1"] | ||||||
|  |   driver = "ioh3420" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.0" | ||||||
|  |   port = "1" | ||||||
|  |   chassis = "1" | ||||||
|  |  | ||||||
|  | [device "ich9-pcie-port-2"] | ||||||
|  |   driver = "ioh3420" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.1" | ||||||
|  |   port = "2" | ||||||
|  |   chassis = "2" | ||||||
|  |  | ||||||
|  | [device "ich9-pcie-port-3"] | ||||||
|  |   driver = "ioh3420" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.2" | ||||||
|  |   port = "3" | ||||||
|  |   chassis = "3" | ||||||
|  |  | ||||||
|  | [device "ich9-pcie-port-4"] | ||||||
|  |   driver = "ioh3420" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.3" | ||||||
|  |   port = "4" | ||||||
|  |   chassis = "4" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # PCI bridge (legacy PCI bridge) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # This bridge can be used to build an independent topology | ||||||
|  | # for legacy PCI devices. PCI Express devices should be | ||||||
|  | # plugged into PCI Express slots instead, so ideally there | ||||||
|  | # will be no devices connected to this bridge. | ||||||
|  |  | ||||||
|  | [device "ich9-pci-bridge"] | ||||||
|  |   driver = "i82801b11-bridge" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1e.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # SATA storage | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # An implicit SATA controller is created automatically for | ||||||
|  | # every single q35 guest; here we create a disk, backed by | ||||||
|  | # a qcow2 disk image on the host's filesystem, and attach | ||||||
|  | # it to that controller so that the guest can use it. | ||||||
|  | # | ||||||
|  | # We also create an optical disk, mostly for installation | ||||||
|  | # purposes: once the guest OS has been succesfully | ||||||
|  | # installed, the guest will no longer boot from optical | ||||||
|  | # media. If you don't want, or no longer want, to have an | ||||||
|  | # optical disk in the guest you can safely comment out | ||||||
|  | # all relevant sections below. | ||||||
|  |  | ||||||
|  | [device "sata-disk"] | ||||||
|  |   driver = "ide-hd" | ||||||
|  |   bus = "ide.0" | ||||||
|  |   drive = "disk" | ||||||
|  |   bootindex = "1" | ||||||
|  |  | ||||||
|  | [drive "disk"] | ||||||
|  |   file = "guest.qcow2"                          # CHANGE ME | ||||||
|  |   format = "qcow2" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  | [device "sata-optical-disk"] | ||||||
|  |   driver = "ide-cd" | ||||||
|  |   bus = "ide.1" | ||||||
|  |   drive = "optical-disk" | ||||||
|  |   bootindex = "2" | ||||||
|  |  | ||||||
|  | [drive "optical-disk"] | ||||||
|  |   file = "install.iso"                          # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # USB controller (#1) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # EHCI controller + UHCI companion controllers. | ||||||
|  |  | ||||||
|  | [device "ich9-ehci-1"] | ||||||
|  |   driver = "ich9-usb-ehci1" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1d.7" | ||||||
|  |  | ||||||
|  | [device "ich9-uhci-1"] | ||||||
|  |   driver = "ich9-usb-uhci1" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1d.0" | ||||||
|  |   masterbus = "ich9-ehci-1.0" | ||||||
|  |   firstport = "0" | ||||||
|  |  | ||||||
|  | [device "ich9-uhci-2"] | ||||||
|  |   driver = "ich9-usb-uhci2" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1d.1" | ||||||
|  |   masterbus = "ich9-ehci-1.0" | ||||||
|  |   firstport = "2" | ||||||
|  |  | ||||||
|  | [device "ich9-uhci-3"] | ||||||
|  |   driver = "ich9-usb-uhci3" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1d.2" | ||||||
|  |   masterbus = "ich9-ehci-1.0" | ||||||
|  |   firstport = "4" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # USB controller (#2) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # EHCI controller + UHCI companion controllers. | ||||||
|  |  | ||||||
|  | [device "ich9-ehci-2"] | ||||||
|  |   driver = "ich9-usb-ehci2" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1a.7" | ||||||
|  |  | ||||||
|  | [device "ich9-uhci-4"] | ||||||
|  |   driver = "ich9-usb-uhci4" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1a.0" | ||||||
|  |   masterbus = "ich9-ehci-2.0" | ||||||
|  |   firstport = "0" | ||||||
|  |  | ||||||
|  | [device "ich9-uhci-5"] | ||||||
|  |   driver = "ich9-usb-uhci5" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1a.1" | ||||||
|  |   masterbus = "ich9-ehci-2.0" | ||||||
|  |   firstport = "2" | ||||||
|  |  | ||||||
|  | [device "ich9-uhci-6"] | ||||||
|  |   driver = "ich9-usb-uhci6" | ||||||
|  |   multifunction = "on" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1a.2" | ||||||
|  |   masterbus = "ich9-ehci-2.0" | ||||||
|  |   firstport = "4" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Ethernet controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We add a Gigabit Ethernet interface to the guest; on the | ||||||
|  | # host side, we take advantage of user networking so that | ||||||
|  | # the QEMU process doesn't require any additional | ||||||
|  | # privileges. | ||||||
|  |  | ||||||
|  | [netdev "hostnet"] | ||||||
|  |   type = "user" | ||||||
|  |  | ||||||
|  | [device "net"] | ||||||
|  |   driver = "e1000" | ||||||
|  |   netdev = "hostnet" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "19.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # VGA compatible controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use stdvga instead of Cirrus as it supports more video | ||||||
|  | # modes and is closer to what actual hardware looks like. | ||||||
|  | # | ||||||
|  | # If you're running the guest on a remote, potentially | ||||||
|  | # headless host, you will probably want to append something | ||||||
|  | # like | ||||||
|  | # | ||||||
|  | #   -display vnc=127.0.0.1:0 | ||||||
|  | # | ||||||
|  | # to the command line in order to prevent QEMU from | ||||||
|  | # creating a graphical display window on the host and | ||||||
|  | # enable remote access instead. | ||||||
|  |  | ||||||
|  | [device "video"] | ||||||
|  |   driver = "VGA" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "01.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Audio device | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # The sound card is a legacy PCI device that is plugged | ||||||
|  | # directly into the PCI Express Root Bus. | ||||||
|  |  | ||||||
|  | [device "ich9-hda-audio"] | ||||||
|  |   driver = "ich9-intel-hda" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1b.0" | ||||||
|  |  | ||||||
|  | [device "ich9-hda-duplex"] | ||||||
|  |   driver = "hda-duplex" | ||||||
|  |   bus = "ich9-hda-audio.0" | ||||||
|  |   cad = "0" | ||||||
							
								
								
									
										248
									
								
								docs/q35-virtio-graphical.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								docs/q35-virtio-graphical.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,248 @@ | |||||||
|  | # q35 - VirtIO guest (graphical console) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # Usage: | ||||||
|  | # | ||||||
|  | #   $ qemu-system-x86_64 \ | ||||||
|  | #     -nodefaults \ | ||||||
|  | #     -readconfig q35-virtio-graphical.cfg | ||||||
|  | # | ||||||
|  | # You will probably need to tweak the lines marked as | ||||||
|  | # CHANGE ME before being able to use this configuration! | ||||||
|  | # | ||||||
|  | # The guest will have a selection of VirtIO devices | ||||||
|  | # tailored towards optimal performance with modern guests, | ||||||
|  | # and will be accessed through a graphical console. | ||||||
|  | # | ||||||
|  | # --------------------------------------------------------- | ||||||
|  | # | ||||||
|  | # Using -nodefaults is required to have full control over | ||||||
|  | # the virtual hardware: when it's specified, QEMU will | ||||||
|  | # populate the board with only the builtin peripherals | ||||||
|  | # plus a small selection of core PCI devices and | ||||||
|  | # controllers; the user will then have to explicitly add | ||||||
|  | # further devices. | ||||||
|  | # | ||||||
|  | # The core PCI devices show up in the guest as: | ||||||
|  | # | ||||||
|  | #   00:00.0 Host bridge | ||||||
|  | #   00:1f.0 ISA bridge / LPC | ||||||
|  | #   00:1f.2 SATA (AHCI) controller | ||||||
|  | #   00:1f.3 SMBus controller | ||||||
|  | # | ||||||
|  | # This configuration file adds a number of other useful | ||||||
|  | # devices, more specifically: | ||||||
|  | # | ||||||
|  | #   00:01.0 VGA compatible controller | ||||||
|  | #   00:1b.0 Audio device | ||||||
|  | #   00.1c.* PCI bridge (PCI Express Root Ports) | ||||||
|  | #   01:00.0 SCSI storage controller | ||||||
|  | #   02:00.0 Ethernet controller | ||||||
|  | #   03:00.0 USB controller | ||||||
|  | # | ||||||
|  | # More information about these devices is available below. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Machine options | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use the q35 machine type and enable KVM acceleration | ||||||
|  | # for better performance. | ||||||
|  | # | ||||||
|  | # Using less than 1 GiB of memory is probably not going to | ||||||
|  | # yield good performance in the guest, and might even lead | ||||||
|  | # to obscure boot issues in some cases. | ||||||
|  |  | ||||||
|  | [machine] | ||||||
|  |   type = "q35" | ||||||
|  |   accel = "kvm" | ||||||
|  |  | ||||||
|  | [memory] | ||||||
|  |   size = "1024" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # PCI bridge (PCI Express Root Ports) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We create eight PCI Express Root Ports, and we plug them | ||||||
|  | # all into separate functions of the same slot. Some of | ||||||
|  | # them will be used by devices, the rest will remain | ||||||
|  | # available for hotplug. | ||||||
|  |  | ||||||
|  | [device "pcie.1"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.0" | ||||||
|  |   port = "1" | ||||||
|  |   chassis = "1" | ||||||
|  |   multifunction = "on" | ||||||
|  |  | ||||||
|  | [device "pcie.2"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.1" | ||||||
|  |   port = "2" | ||||||
|  |   chassis = "2" | ||||||
|  |  | ||||||
|  | [device "pcie.3"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.2" | ||||||
|  |   port = "3" | ||||||
|  |   chassis = "3" | ||||||
|  |  | ||||||
|  | [device "pcie.4"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.3" | ||||||
|  |   port = "4" | ||||||
|  |   chassis = "4" | ||||||
|  |  | ||||||
|  | [device "pcie.5"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.4" | ||||||
|  |   port = "5" | ||||||
|  |   chassis = "5" | ||||||
|  |  | ||||||
|  | [device "pcie.6"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.5" | ||||||
|  |   port = "6" | ||||||
|  |   chassis = "6" | ||||||
|  |  | ||||||
|  | [device "pcie.7"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.6" | ||||||
|  |   port = "7" | ||||||
|  |   chassis = "7" | ||||||
|  |  | ||||||
|  | [device "pcie.8"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.7" | ||||||
|  |   port = "8" | ||||||
|  |   chassis = "8" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # SCSI storage controller (and storage) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-scsi here so that we can (hot)plug a large | ||||||
|  | # number of disks without running into issues; a SCSI disk, | ||||||
|  | # backed by a qcow2 disk image on the host's filesystem, is | ||||||
|  | # attached to it. | ||||||
|  | # | ||||||
|  | # We also create an optical disk, mostly for installation | ||||||
|  | # purposes: once the guest OS has been succesfully | ||||||
|  | # installed, the guest will no longer boot from optical | ||||||
|  | # media. If you don't want, or no longer want, to have an | ||||||
|  | # optical disk in the guest you can safely comment out | ||||||
|  | # all relevant sections below. | ||||||
|  |  | ||||||
|  | [device "scsi"] | ||||||
|  |   driver = "virtio-scsi-pci" | ||||||
|  |   bus = "pcie.1" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  | [device "scsi-disk"] | ||||||
|  |   driver = "scsi-hd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "disk" | ||||||
|  |   bootindex = "1" | ||||||
|  |  | ||||||
|  | [drive "disk"] | ||||||
|  |   file = "guest.qcow2"                          # CHANGE ME | ||||||
|  |   format = "qcow2" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  | [device "scsi-optical-disk"] | ||||||
|  |   driver = "scsi-cd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "optical-disk" | ||||||
|  |   bootindex = "2" | ||||||
|  |  | ||||||
|  | [drive "optical-disk"] | ||||||
|  |   file = "install.iso"                          # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Ethernet controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-net for improved performance over emulated | ||||||
|  | # hardware; on the host side, we take advantage of user | ||||||
|  | # networking so that the QEMU process doesn't require any | ||||||
|  | # additional privileges. | ||||||
|  |  | ||||||
|  | [netdev "hostnet"] | ||||||
|  |   type = "user" | ||||||
|  |  | ||||||
|  | [device "net"] | ||||||
|  |   driver = "virtio-net-pci" | ||||||
|  |   netdev = "hostnet" | ||||||
|  |   bus = "pcie.2" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # USB controller (and input devices) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We add a virtualization-friendly USB 3.0 controller and | ||||||
|  | # a USB tablet so that graphical guests can be controlled | ||||||
|  | # appropriately. A USB keyboard is not needed, as q35 | ||||||
|  | # guests get a PS/2 one added automatically. | ||||||
|  |  | ||||||
|  | [device "usb"] | ||||||
|  |   driver = "nec-usb-xhci" | ||||||
|  |   bus = "pcie.3" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  | [device "tablet"] | ||||||
|  |   driver = "usb-tablet" | ||||||
|  |   bus = "usb.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # VGA compatible controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We plug the QXL video card directly into the PCI Express | ||||||
|  | # Root Bus as it is a legacy PCI device; this way, we can | ||||||
|  | # reduce the number of PCI Express controllers in the | ||||||
|  | # guest. | ||||||
|  | # | ||||||
|  | # If you're running the guest on a remote, potentially | ||||||
|  | # headless host, you will probably want to append something | ||||||
|  | # like | ||||||
|  | # | ||||||
|  | #   -display vnc=127.0.0.1:0 | ||||||
|  | # | ||||||
|  | # to the command line in order to prevent QEMU from | ||||||
|  | # creating a graphical display window on the host and | ||||||
|  | # enable remote access instead. | ||||||
|  |  | ||||||
|  | [device "video"] | ||||||
|  |   driver = "qxl-vga" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "01.0" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Audio device | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # Like the video card, the sound card is a legacy PCI | ||||||
|  | # device and as such can be plugged directly into the PCI | ||||||
|  | # Express Root Bus. | ||||||
|  |  | ||||||
|  | [device "sound"] | ||||||
|  |   driver = "ich9-intel-hda" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1b.0" | ||||||
|  |  | ||||||
|  | [device "duplex"] | ||||||
|  |   driver = "hda-duplex" | ||||||
|  |   bus = "sound.0" | ||||||
|  |   cad = "0" | ||||||
							
								
								
									
										193
									
								
								docs/q35-virtio-serial.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								docs/q35-virtio-serial.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | |||||||
|  | # q35 - VirtIO guest (serial console) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # Usage: | ||||||
|  | # | ||||||
|  | #   $ qemu-system-x86_64 \ | ||||||
|  | #     -nodefaults \ | ||||||
|  | #     -readconfig q35-virtio-serial.cfg \ | ||||||
|  | #     -display none -serial mon:stdio | ||||||
|  | # | ||||||
|  | # You will probably need to tweak the lines marked as | ||||||
|  | # CHANGE ME before being able to use this configuration! | ||||||
|  | # | ||||||
|  | # The guest will have a selection of VirtIO devices | ||||||
|  | # tailored towards optimal performance with modern guests, | ||||||
|  | # and will be accessed through the serial console. | ||||||
|  | # | ||||||
|  | # --------------------------------------------------------- | ||||||
|  | # | ||||||
|  | # Using -nodefaults is required to have full control over | ||||||
|  | # the virtual hardware: when it's specified, QEMU will | ||||||
|  | # populate the board with only the builtin peripherals | ||||||
|  | # plus a small selection of core PCI devices and | ||||||
|  | # controllers; the user will then have to explicitly add | ||||||
|  | # further devices. | ||||||
|  | # | ||||||
|  | # The core PCI devices show up in the guest as: | ||||||
|  | # | ||||||
|  | #   00:00.0 Host bridge | ||||||
|  | #   00:1f.0 ISA bridge / LPC | ||||||
|  | #   00:1f.2 SATA (AHCI) controller | ||||||
|  | #   00:1f.3 SMBus controller | ||||||
|  | # | ||||||
|  | # This configuration file adds a number of other useful | ||||||
|  | # devices, more specifically: | ||||||
|  | # | ||||||
|  | #   00.1c.* PCI bridge (PCI Express Root Ports) | ||||||
|  | #   01:00.0 SCSI storage controller | ||||||
|  | #   02:00.0 Ethernet controller | ||||||
|  | # | ||||||
|  | # More information about these devices is available below. | ||||||
|  | # | ||||||
|  | # We use '-display none' to prevent QEMU from creating a | ||||||
|  | # graphical display window, which would serve no use in | ||||||
|  | # this specific configuration, and '-serial mon:stdio' to | ||||||
|  | # multiplex the guest's serial console and the QEMU monitor | ||||||
|  | # to the host's stdio; use 'Ctrl+A h' to learn how to | ||||||
|  | # switch between the two and more. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Machine options | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use the q35 machine type and enable KVM acceleration | ||||||
|  | # for better performance. | ||||||
|  | # | ||||||
|  | # Using less than 1 GiB of memory is probably not going to | ||||||
|  | # yield good performance in the guest, and might even lead | ||||||
|  | # to obscure boot issues in some cases. | ||||||
|  |  | ||||||
|  | [machine] | ||||||
|  |   type = "q35" | ||||||
|  |   accel = "kvm" | ||||||
|  |  | ||||||
|  | [memory] | ||||||
|  |   size = "1024" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # PCI bridge (PCI Express Root Ports) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We create eight PCI Express Root Ports, and we plug them | ||||||
|  | # all into separate functions of the same slot. Some of | ||||||
|  | # them will be used by devices, the rest will remain | ||||||
|  | # available for hotplug. | ||||||
|  |  | ||||||
|  | [device "pcie.1"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.0" | ||||||
|  |   port = "1" | ||||||
|  |   chassis = "1" | ||||||
|  |   multifunction = "on" | ||||||
|  |  | ||||||
|  | [device "pcie.2"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.1" | ||||||
|  |   port = "2" | ||||||
|  |   chassis = "2" | ||||||
|  |  | ||||||
|  | [device "pcie.3"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.2" | ||||||
|  |   port = "3" | ||||||
|  |   chassis = "3" | ||||||
|  |  | ||||||
|  | [device "pcie.4"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.3" | ||||||
|  |   port = "4" | ||||||
|  |   chassis = "4" | ||||||
|  |  | ||||||
|  | [device "pcie.5"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.4" | ||||||
|  |   port = "5" | ||||||
|  |   chassis = "5" | ||||||
|  |  | ||||||
|  | [device "pcie.6"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.5" | ||||||
|  |   port = "6" | ||||||
|  |   chassis = "6" | ||||||
|  |  | ||||||
|  | [device "pcie.7"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.6" | ||||||
|  |   port = "7" | ||||||
|  |   chassis = "7" | ||||||
|  |  | ||||||
|  | [device "pcie.8"] | ||||||
|  |   driver = "pcie-root-port" | ||||||
|  |   bus = "pcie.0" | ||||||
|  |   addr = "1c.7" | ||||||
|  |   port = "8" | ||||||
|  |   chassis = "8" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # SCSI storage controller (and storage) | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-scsi here so that we can (hot)plug a large | ||||||
|  | # number of disks without running into issues; a SCSI disk, | ||||||
|  | # backed by a qcow2 disk image on the host's filesystem, is | ||||||
|  | # attached to it. | ||||||
|  | # | ||||||
|  | # We also create an optical disk, mostly for installation | ||||||
|  | # purposes: once the guest OS has been succesfully | ||||||
|  | # installed, the guest will no longer boot from optical | ||||||
|  | # media. If you don't want, or no longer want, to have an | ||||||
|  | # optical disk in the guest you can safely comment out | ||||||
|  | # all relevant sections below. | ||||||
|  |  | ||||||
|  | [device "scsi"] | ||||||
|  |   driver = "virtio-scsi-pci" | ||||||
|  |   bus = "pcie.1" | ||||||
|  |   addr = "00.0" | ||||||
|  |  | ||||||
|  | [device "scsi-disk"] | ||||||
|  |   driver = "scsi-hd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "disk" | ||||||
|  |   bootindex = "1" | ||||||
|  |  | ||||||
|  | [drive "disk"] | ||||||
|  |   file = "guest.qcow2"                          # CHANGE ME | ||||||
|  |   format = "qcow2" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  | [device "scsi-optical-disk"] | ||||||
|  |   driver = "scsi-cd" | ||||||
|  |   bus = "scsi.0" | ||||||
|  |   drive = "optical-disk" | ||||||
|  |   bootindex = "2" | ||||||
|  |  | ||||||
|  | [drive "optical-disk"] | ||||||
|  |   file = "install.iso"                          # CHANGE ME | ||||||
|  |   format = "raw" | ||||||
|  |   if = "none" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Ethernet controller | ||||||
|  | # ========================================================= | ||||||
|  | # | ||||||
|  | # We use virtio-net for improved performance over emulated | ||||||
|  | # hardware; on the host side, we take advantage of user | ||||||
|  | # networking so that the QEMU process doesn't require any | ||||||
|  | # additional privileges. | ||||||
|  |  | ||||||
|  | [netdev "hostnet"] | ||||||
|  |   type = "user" | ||||||
|  |  | ||||||
|  | [device "net"] | ||||||
|  |   driver = "virtio-net-pci" | ||||||
|  |   netdev = "hostnet" | ||||||
|  |   bus = "pcie.2" | ||||||
|  |   addr = "00.0" | ||||||
| @@ -200,7 +200,7 @@ LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows: | |||||||
|  |  | ||||||
| * null becomes -chardev null | * null becomes -chardev null | ||||||
|  |  | ||||||
| * pty, msmouse, braille, stdio likewise | * pty, msmouse, wctablet, braille, stdio likewise | ||||||
|  |  | ||||||
| * vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT | * vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| \input texinfo | \input texinfo | ||||||
| @setfilename qemu-ga-ref.info | @setfilename qemu-ga-ref.info | ||||||
|  |  | ||||||
|  | @include version.texi | ||||||
|  |  | ||||||
| @exampleindent 0 | @exampleindent 0 | ||||||
| @paragraphindent 0 | @paragraphindent 0 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| \input texinfo | \input texinfo | ||||||
| @setfilename qemu-qmp-ref.info | @setfilename qemu-qmp-ref.info | ||||||
|  |  | ||||||
|  | @include version.texi | ||||||
|  |  | ||||||
| @exampleindent 0 | @exampleindent 0 | ||||||
| @paragraphindent 0 | @paragraphindent 0 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,6 +61,7 @@ PCI devices (other than virtio): | |||||||
| 1b36:0009  PCI Expander Bridge (-device pxb) | 1b36:0009  PCI Expander Bridge (-device pxb) | ||||||
| 1b36:000a  PCI-PCI bridge (multiseat) | 1b36:000a  PCI-PCI bridge (multiseat) | ||||||
| 1b36:000b  PCIe Expander Bridge (-device pxb-pcie) | 1b36:000b  PCIe Expander Bridge (-device pxb-pcie) | ||||||
|  | 1b36:000d  PCI xhci usb host adapter | ||||||
|  |  | ||||||
| All these devices are documented in docs/specs. | All these devices are documented in docs/specs. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								dtc
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								dtc
									
									
									
									
									
								
							 Submodule dtc updated: 65cc4d2748...ec02b34c05
									
								
							
							
								
								
									
										13
									
								
								exec.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								exec.c
									
									
									
									
									
								
							| @@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) | |||||||
|                 } |                 } | ||||||
|                 cpu->watchpoint_hit = wp; |                 cpu->watchpoint_hit = wp; | ||||||
|  |  | ||||||
|                 /* The tb_lock will be reset when cpu_loop_exit or |                 /* Both tb_lock and iothread_mutex will be reset when | ||||||
|                  * cpu_loop_exit_noexc longjmp back into the cpu_exec |                  * cpu_loop_exit or cpu_loop_exit_noexc longjmp | ||||||
|                  * main loop. |                  * back into the cpu_exec main loop. | ||||||
|                  */ |                  */ | ||||||
|                 tb_lock(); |                 tb_lock(); | ||||||
|                 tb_check_watchpoint(cpu); |                 tb_check_watchpoint(cpu); | ||||||
| @@ -2371,8 +2371,14 @@ static void io_mem_init(void) | |||||||
|     memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX); |     memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX); | ||||||
|     memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL, |     memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL, | ||||||
|                           NULL, UINT64_MAX); |                           NULL, UINT64_MAX); | ||||||
|  |  | ||||||
|  |     /* io_mem_notdirty calls tb_invalidate_phys_page_fast, | ||||||
|  |      * which can be called without the iothread mutex. | ||||||
|  |      */ | ||||||
|     memory_region_init_io(&io_mem_notdirty, NULL, ¬dirty_mem_ops, NULL, |     memory_region_init_io(&io_mem_notdirty, NULL, ¬dirty_mem_ops, NULL, | ||||||
|                           NULL, UINT64_MAX); |                           NULL, UINT64_MAX); | ||||||
|  |     memory_region_clear_global_locking(&io_mem_notdirty); | ||||||
|  |  | ||||||
|     memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL, |     memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL, | ||||||
|                           NULL, UINT64_MAX); |                           NULL, UINT64_MAX); | ||||||
| } | } | ||||||
| @@ -3166,6 +3172,7 @@ void address_space_cache_destroy(MemoryRegionCache *cache) | |||||||
|         xen_invalidate_map_cache_entry(cache->ptr); |         xen_invalidate_map_cache_entry(cache->ptr); | ||||||
|     } |     } | ||||||
|     memory_region_unref(cache->mr); |     memory_region_unref(cache->mr); | ||||||
|  |     cache->mr = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called from RCU critical section.  This function has the same | /* Called from RCU critical section.  This function has the same | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								fpu/softfloat.c
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								fpu/softfloat.c
									
									
									
									
									
								
							| @@ -623,6 +623,9 @@ static float64 roundAndPackFloat64(flag zSign, int zExp, uint64_t zSig, | |||||||
|     case float_round_down: |     case float_round_down: | ||||||
|         roundIncrement = zSign ? 0x3ff : 0; |         roundIncrement = zSign ? 0x3ff : 0; | ||||||
|         break; |         break; | ||||||
|  |     case float_round_to_odd: | ||||||
|  |         roundIncrement = (zSig & 0x400) ? 0 : 0x3ff; | ||||||
|  |         break; | ||||||
|     default: |     default: | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
| @@ -632,8 +635,10 @@ static float64 roundAndPackFloat64(flag zSign, int zExp, uint64_t zSig, | |||||||
|              || (    ( zExp == 0x7FD ) |              || (    ( zExp == 0x7FD ) | ||||||
|                   && ( (int64_t) ( zSig + roundIncrement ) < 0 ) ) |                   && ( (int64_t) ( zSig + roundIncrement ) < 0 ) ) | ||||||
|            ) { |            ) { | ||||||
|  |             bool overflow_to_inf = roundingMode != float_round_to_odd && | ||||||
|  |                                    roundIncrement != 0; | ||||||
|             float_raise(float_flag_overflow | float_flag_inexact, status); |             float_raise(float_flag_overflow | float_flag_inexact, status); | ||||||
|             return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 )); |             return packFloat64(zSign, 0x7FF, -(!overflow_to_inf)); | ||||||
|         } |         } | ||||||
|         if ( zExp < 0 ) { |         if ( zExp < 0 ) { | ||||||
|             if (status->flush_to_zero) { |             if (status->flush_to_zero) { | ||||||
| @@ -651,6 +656,13 @@ static float64 roundAndPackFloat64(flag zSign, int zExp, uint64_t zSig, | |||||||
|             if (isTiny && roundBits) { |             if (isTiny && roundBits) { | ||||||
|                 float_raise(float_flag_underflow, status); |                 float_raise(float_flag_underflow, status); | ||||||
|             } |             } | ||||||
|  |             if (roundingMode == float_round_to_odd) { | ||||||
|  |                 /* | ||||||
|  |                  * For round-to-odd case, the roundIncrement depends on | ||||||
|  |                  * zSig which just changed. | ||||||
|  |                  */ | ||||||
|  |                 roundIncrement = (zSig & 0x400) ? 0 : 0x3ff; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if (roundBits) { |     if (roundBits) { | ||||||
| @@ -1149,6 +1161,9 @@ static float128 roundAndPackFloat128(flag zSign, int32_t zExp, | |||||||
|     case float_round_down: |     case float_round_down: | ||||||
|         increment = zSign && zSig2; |         increment = zSign && zSig2; | ||||||
|         break; |         break; | ||||||
|  |     case float_round_to_odd: | ||||||
|  |         increment = !(zSig1 & 0x1) && zSig2; | ||||||
|  |         break; | ||||||
|     default: |     default: | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
| @@ -1168,6 +1183,7 @@ static float128 roundAndPackFloat128(flag zSign, int32_t zExp, | |||||||
|             if (    ( roundingMode == float_round_to_zero ) |             if (    ( roundingMode == float_round_to_zero ) | ||||||
|                  || ( zSign && ( roundingMode == float_round_up ) ) |                  || ( zSign && ( roundingMode == float_round_up ) ) | ||||||
|                  || ( ! zSign && ( roundingMode == float_round_down ) ) |                  || ( ! zSign && ( roundingMode == float_round_down ) ) | ||||||
|  |                  || (roundingMode == float_round_to_odd) | ||||||
|                ) { |                ) { | ||||||
|                 return |                 return | ||||||
|                     packFloat128( |                     packFloat128( | ||||||
| @@ -1215,6 +1231,9 @@ static float128 roundAndPackFloat128(flag zSign, int32_t zExp, | |||||||
|             case float_round_down: |             case float_round_down: | ||||||
|                 increment = zSign && zSig2; |                 increment = zSign && zSig2; | ||||||
|                 break; |                 break; | ||||||
|  |             case float_round_to_odd: | ||||||
|  |                 increment = !(zSig1 & 0x1) && zSig2; | ||||||
|  |                 break; | ||||||
|             default: |             default: | ||||||
|                 abort(); |                 abort(); | ||||||
|             } |             } | ||||||
| @@ -6108,6 +6127,93 @@ int64_t float128_to_int64_round_to_zero(float128 a, float_status *status) | |||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /*---------------------------------------------------------------------------- | ||||||
|  | | Returns the result of converting the quadruple-precision floating-point value | ||||||
|  | | `a' to the 64-bit unsigned integer format.  The conversion is | ||||||
|  | | performed according to the IEC/IEEE Standard for Binary Floating-Point | ||||||
|  | | Arithmetic---which means in particular that the conversion is rounded | ||||||
|  | | according to the current rounding mode.  If `a' is a NaN, the largest | ||||||
|  | | positive integer is returned.  If the conversion overflows, the | ||||||
|  | | largest unsigned integer is returned.  If 'a' is negative, the value is | ||||||
|  | | rounded and zero is returned; negative values that do not round to zero | ||||||
|  | | will raise the inexact exception. | ||||||
|  | *----------------------------------------------------------------------------*/ | ||||||
|  |  | ||||||
|  | uint64_t float128_to_uint64(float128 a, float_status *status) | ||||||
|  | { | ||||||
|  |     flag aSign; | ||||||
|  |     int aExp; | ||||||
|  |     int shiftCount; | ||||||
|  |     uint64_t aSig0, aSig1; | ||||||
|  |  | ||||||
|  |     aSig0 = extractFloat128Frac0(a); | ||||||
|  |     aSig1 = extractFloat128Frac1(a); | ||||||
|  |     aExp = extractFloat128Exp(a); | ||||||
|  |     aSign = extractFloat128Sign(a); | ||||||
|  |     if (aSign && (aExp > 0x3FFE)) { | ||||||
|  |         float_raise(float_flag_invalid, status); | ||||||
|  |         if (float128_is_any_nan(a)) { | ||||||
|  |             return LIT64(0xFFFFFFFFFFFFFFFF); | ||||||
|  |         } else { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (aExp) { | ||||||
|  |         aSig0 |= LIT64(0x0001000000000000); | ||||||
|  |     } | ||||||
|  |     shiftCount = 0x402F - aExp; | ||||||
|  |     if (shiftCount <= 0) { | ||||||
|  |         if (0x403E < aExp) { | ||||||
|  |             float_raise(float_flag_invalid, status); | ||||||
|  |             return LIT64(0xFFFFFFFFFFFFFFFF); | ||||||
|  |         } | ||||||
|  |         shortShift128Left(aSig0, aSig1, -shiftCount, &aSig0, &aSig1); | ||||||
|  |     } else { | ||||||
|  |         shift64ExtraRightJamming(aSig0, aSig1, shiftCount, &aSig0, &aSig1); | ||||||
|  |     } | ||||||
|  |     return roundAndPackUint64(aSign, aSig0, aSig1, status); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint64_t float128_to_uint64_round_to_zero(float128 a, float_status *status) | ||||||
|  | { | ||||||
|  |     uint64_t v; | ||||||
|  |     signed char current_rounding_mode = status->float_rounding_mode; | ||||||
|  |  | ||||||
|  |     set_float_rounding_mode(float_round_to_zero, status); | ||||||
|  |     v = float128_to_uint64(a, status); | ||||||
|  |     set_float_rounding_mode(current_rounding_mode, status); | ||||||
|  |  | ||||||
|  |     return v; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*---------------------------------------------------------------------------- | ||||||
|  | | Returns the result of converting the quadruple-precision floating-point | ||||||
|  | | value `a' to the 32-bit unsigned integer format.  The conversion | ||||||
|  | | is performed according to the IEC/IEEE Standard for Binary Floating-Point | ||||||
|  | | Arithmetic except that the conversion is always rounded toward zero. | ||||||
|  | | If `a' is a NaN, the largest positive integer is returned.  Otherwise, | ||||||
|  | | if the conversion overflows, the largest unsigned integer is returned. | ||||||
|  | | If 'a' is negative, the value is rounded and zero is returned; negative | ||||||
|  | | values that do not round to zero will raise the inexact exception. | ||||||
|  | *----------------------------------------------------------------------------*/ | ||||||
|  |  | ||||||
|  | uint32_t float128_to_uint32_round_to_zero(float128 a, float_status *status) | ||||||
|  | { | ||||||
|  |     uint64_t v; | ||||||
|  |     uint32_t res; | ||||||
|  |     int old_exc_flags = get_float_exception_flags(status); | ||||||
|  |  | ||||||
|  |     v = float128_to_uint64_round_to_zero(a, status); | ||||||
|  |     if (v > 0xffffffff) { | ||||||
|  |         res = 0xffffffff; | ||||||
|  |     } else { | ||||||
|  |         return v; | ||||||
|  |     } | ||||||
|  |     set_float_exception_flags(old_exc_flags, status); | ||||||
|  |     float_raise(float_flag_invalid, status); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  |  | ||||||
| /*---------------------------------------------------------------------------- | /*---------------------------------------------------------------------------- | ||||||
| | Returns the result of converting the quadruple-precision floating-point | | Returns the result of converting the quadruple-precision floating-point | ||||||
| | value `a' to the single-precision floating-point format.  The conversion | | value `a' to the single-precision floating-point format.  The conversion | ||||||
| @@ -7386,7 +7492,7 @@ uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *status) | |||||||
| { | { | ||||||
|     signed char current_rounding_mode = status->float_rounding_mode; |     signed char current_rounding_mode = status->float_rounding_mode; | ||||||
|     set_float_rounding_mode(float_round_to_zero, status); |     set_float_rounding_mode(float_round_to_zero, status); | ||||||
|     int64_t v = float64_to_uint64(a, status); |     uint64_t v = float64_to_uint64(a, status); | ||||||
|     set_float_rounding_mode(current_rounding_mode, status); |     set_float_rounding_mode(current_rounding_mode, status); | ||||||
|     return v; |     return v; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ common-obj-y = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o | |||||||
| else | else | ||||||
| common-obj-y = qemu-fsdev-dummy.o | common-obj-y = qemu-fsdev-dummy.o | ||||||
| endif | endif | ||||||
| common-obj-y += qemu-fsdev-opts.o | common-obj-y += qemu-fsdev-opts.o qemu-fsdev-throttle.o | ||||||
|  |  | ||||||
| # Toplevel always builds this; targets without virtio will put it in | # Toplevel always builds this; targets without virtio will put it in | ||||||
| # common-obj-y | # common-obj-y | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ | |||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
| #include <utime.h> | #include <utime.h> | ||||||
| #include <sys/vfs.h> | #include <sys/vfs.h> | ||||||
|  | #include "qemu-fsdev-throttle.h" | ||||||
|  |  | ||||||
| #define SM_LOCAL_MODE_BITS    0600 | #define SM_LOCAL_MODE_BITS    0600 | ||||||
| #define SM_LOCAL_DIR_MODE_BITS    0700 | #define SM_LOCAL_DIR_MODE_BITS    0700 | ||||||
| @@ -74,6 +75,7 @@ typedef struct FsDriverEntry { | |||||||
|     char *path; |     char *path; | ||||||
|     int export_flags; |     int export_flags; | ||||||
|     FileOperations *ops; |     FileOperations *ops; | ||||||
|  |     FsThrottle fst; | ||||||
| } FsDriverEntry; | } FsDriverEntry; | ||||||
|  |  | ||||||
| typedef struct FsContext | typedef struct FsContext | ||||||
| @@ -83,6 +85,7 @@ typedef struct FsContext | |||||||
|     int export_flags; |     int export_flags; | ||||||
|     struct xattr_operations **xops; |     struct xattr_operations **xops; | ||||||
|     struct extended_ops exops; |     struct extended_ops exops; | ||||||
|  |     FsThrottle *fst; | ||||||
|     /* fs driver specific data */ |     /* fs driver specific data */ | ||||||
|     void *private; |     void *private; | ||||||
| } FsContext; | } FsContext; | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "qemu/module.h" | #include "qemu/module.h" | ||||||
|  | #include "qemu/throttle-options.h" | ||||||
|  |  | ||||||
| static QemuOptsList qemu_fsdev_opts = { | static QemuOptsList qemu_fsdev_opts = { | ||||||
|     .name = "fsdev", |     .name = "fsdev", | ||||||
| @@ -39,6 +40,8 @@ static QemuOptsList qemu_fsdev_opts = { | |||||||
|             .type = QEMU_OPT_NUMBER, |             .type = QEMU_OPT_NUMBER, | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|  |         THROTTLE_OPTS, | ||||||
|  |  | ||||||
|         { /*End of list */ } |         { /*End of list */ } | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										118
									
								
								fsdev/qemu-fsdev-throttle.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								fsdev/qemu-fsdev-throttle.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | /* | ||||||
|  |  * Fsdev Throttle | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2016 Huawei Technologies Duesseldorf GmbH | ||||||
|  |  * | ||||||
|  |  * Author: Pradeep Jagadeesh <pradeep.jagadeesh@huawei.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * See the COPYING file in the top-level directory for details. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "qemu/osdep.h" | ||||||
|  | #include "qemu/error-report.h" | ||||||
|  | #include "qemu-fsdev-throttle.h" | ||||||
|  | #include "qemu/iov.h" | ||||||
|  |  | ||||||
|  | static void fsdev_throttle_read_timer_cb(void *opaque) | ||||||
|  | { | ||||||
|  |     FsThrottle *fst = opaque; | ||||||
|  |     qemu_co_enter_next(&fst->throttled_reqs[false]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fsdev_throttle_write_timer_cb(void *opaque) | ||||||
|  | { | ||||||
|  |     FsThrottle *fst = opaque; | ||||||
|  |     qemu_co_enter_next(&fst->throttled_reqs[true]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp) | ||||||
|  | { | ||||||
|  |     throttle_config_init(&fst->cfg); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_TOTAL].avg = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-total", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_READ].avg  = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-read", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_WRITE].avg = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-write", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_TOTAL].avg = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-total", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_READ].avg = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-read", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_WRITE].avg = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-write", 0); | ||||||
|  |  | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_TOTAL].max = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-total-max", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_READ].max  = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-read-max", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_WRITE].max = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-write-max", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_TOTAL].max = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-total-max", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_READ].max = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-read-max", 0); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_WRITE].max = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-write-max", 0); | ||||||
|  |  | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_READ].burst_length  = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1); | ||||||
|  |     fst->cfg.buckets[THROTTLE_BPS_WRITE].burst_length = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_READ].burst_length = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1); | ||||||
|  |     fst->cfg.buckets[THROTTLE_OPS_WRITE].burst_length = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1); | ||||||
|  |     fst->cfg.op_size = | ||||||
|  |         qemu_opt_get_number(opts, "throttling.iops-size", 0); | ||||||
|  |  | ||||||
|  |     throttle_is_valid(&fst->cfg, errp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void fsdev_throttle_init(FsThrottle *fst) | ||||||
|  | { | ||||||
|  |     if (throttle_enabled(&fst->cfg)) { | ||||||
|  |         throttle_init(&fst->ts); | ||||||
|  |         throttle_timers_init(&fst->tt, | ||||||
|  |                              qemu_get_aio_context(), | ||||||
|  |                              QEMU_CLOCK_REALTIME, | ||||||
|  |                              fsdev_throttle_read_timer_cb, | ||||||
|  |                              fsdev_throttle_write_timer_cb, | ||||||
|  |                              fst); | ||||||
|  |         throttle_config(&fst->ts, &fst->tt, &fst->cfg); | ||||||
|  |         qemu_co_queue_init(&fst->throttled_reqs[0]); | ||||||
|  |         qemu_co_queue_init(&fst->throttled_reqs[1]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst, bool is_write, | ||||||
|  |                                             struct iovec *iov, int iovcnt) | ||||||
|  | { | ||||||
|  |     if (throttle_enabled(&fst->cfg)) { | ||||||
|  |         if (throttle_schedule_timer(&fst->ts, &fst->tt, is_write) || | ||||||
|  |             !qemu_co_queue_empty(&fst->throttled_reqs[is_write])) { | ||||||
|  |             qemu_co_queue_wait(&fst->throttled_reqs[is_write], NULL); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         throttle_account(&fst->ts, is_write, iov_size(iov, iovcnt)); | ||||||
|  |  | ||||||
|  |         if (!qemu_co_queue_empty(&fst->throttled_reqs[is_write]) && | ||||||
|  |             !throttle_schedule_timer(&fst->ts, &fst->tt, is_write)) { | ||||||
|  |             qemu_co_queue_next(&fst->throttled_reqs[is_write]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void fsdev_throttle_cleanup(FsThrottle *fst) | ||||||
|  | { | ||||||
|  |     if (throttle_enabled(&fst->cfg)) { | ||||||
|  |         throttle_timers_destroy(&fst->tt); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								fsdev/qemu-fsdev-throttle.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								fsdev/qemu-fsdev-throttle.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * Fsdev Throttle | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2016 Huawei Technologies Duesseldorf GmbH | ||||||
|  |  * | ||||||
|  |  * Author: Pradeep Jagadeesh <pradeep.jagadeesh@huawei.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * See the COPYING file in the top-level directory for details. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _FSDEV_THROTTLE_H | ||||||
|  | #define _FSDEV_THROTTLE_H | ||||||
|  |  | ||||||
|  | #include "block/aio.h" | ||||||
|  | #include "qemu/main-loop.h" | ||||||
|  | #include "qemu/coroutine.h" | ||||||
|  | #include "qapi/error.h" | ||||||
|  | #include "qemu/throttle.h" | ||||||
|  |  | ||||||
|  | typedef struct FsThrottle { | ||||||
|  |     ThrottleState ts; | ||||||
|  |     ThrottleTimers tt; | ||||||
|  |     ThrottleConfig cfg; | ||||||
|  |     CoQueue      throttled_reqs[2]; | ||||||
|  | } FsThrottle; | ||||||
|  |  | ||||||
|  | void fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **); | ||||||
|  |  | ||||||
|  | void fsdev_throttle_init(FsThrottle *); | ||||||
|  |  | ||||||
|  | void coroutine_fn fsdev_co_throttle_request(FsThrottle *, bool , | ||||||
|  |                                             struct iovec *, int); | ||||||
|  |  | ||||||
|  | void fsdev_throttle_cleanup(FsThrottle *); | ||||||
|  | #endif /* _FSDEV_THROTTLE_H */ | ||||||
							
								
								
									
										29
									
								
								hmp.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								hmp.c
									
									
									
									
									
								
							| @@ -1014,8 +1014,14 @@ void hmp_memsave(Monitor *mon, const QDict *qdict) | |||||||
|     const char *filename = qdict_get_str(qdict, "filename"); |     const char *filename = qdict_get_str(qdict, "filename"); | ||||||
|     uint64_t addr = qdict_get_int(qdict, "val"); |     uint64_t addr = qdict_get_int(qdict, "val"); | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|  |     int cpu_index = monitor_get_cpu_index(); | ||||||
|  |  | ||||||
|     qmp_memsave(addr, size, filename, true, monitor_get_cpu_index(), &err); |     if (cpu_index < 0) { | ||||||
|  |         monitor_printf(mon, "No CPU available\n"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     qmp_memsave(addr, size, filename, true, cpu_index, &err); | ||||||
|     hmp_handle_error(mon, &err); |     hmp_handle_error(mon, &err); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1338,12 +1344,11 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) | |||||||
| { | { | ||||||
|     const char *param = qdict_get_str(qdict, "parameter"); |     const char *param = qdict_get_str(qdict, "parameter"); | ||||||
|     const char *valuestr = qdict_get_str(qdict, "value"); |     const char *valuestr = qdict_get_str(qdict, "value"); | ||||||
|     int64_t valuebw = 0; |     uint64_t valuebw = 0; | ||||||
|     long valueint = 0; |     long valueint = 0; | ||||||
|     char *endp; |  | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|     bool use_int_value = false; |     bool use_int_value = false; | ||||||
|     int i; |     int i, ret; | ||||||
|  |  | ||||||
|     for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { |     for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { | ||||||
|         if (strcmp(param, MigrationParameter_lookup[i]) == 0) { |         if (strcmp(param, MigrationParameter_lookup[i]) == 0) { | ||||||
| @@ -1379,9 +1384,9 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) | |||||||
|                 break; |                 break; | ||||||
|             case MIGRATION_PARAMETER_MAX_BANDWIDTH: |             case MIGRATION_PARAMETER_MAX_BANDWIDTH: | ||||||
|                 p.has_max_bandwidth = true; |                 p.has_max_bandwidth = true; | ||||||
|                 valuebw = qemu_strtosz(valuestr, &endp); |                 ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); | ||||||
|                 if (valuebw < 0 || (size_t)valuebw != valuebw |                 if (ret < 0 || valuebw > INT64_MAX | ||||||
|                     || *endp != '\0') { |                     || (size_t)valuebw != valuebw) { | ||||||
|                     error_setg(&err, "Invalid size %s", valuestr); |                     error_setg(&err, "Invalid size %s", valuestr); | ||||||
|                     goto cleanup; |                     goto cleanup; | ||||||
|                 } |                 } | ||||||
| @@ -1552,6 +1557,7 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) | |||||||
| { | { | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|     BlockIOThrottle throttle = { |     BlockIOThrottle throttle = { | ||||||
|  |         .has_device = true, | ||||||
|         .device = (char *) qdict_get_str(qdict, "device"), |         .device = (char *) qdict_get_str(qdict, "device"), | ||||||
|         .bps = qdict_get_int(qdict, "bps"), |         .bps = qdict_get_int(qdict, "bps"), | ||||||
|         .bps_rd = qdict_get_int(qdict, "bps_rd"), |         .bps_rd = qdict_get_int(qdict, "bps_rd"), | ||||||
| @@ -2148,10 +2154,15 @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict) | |||||||
| { | { | ||||||
|     IOThreadInfoList *info_list = qmp_query_iothreads(NULL); |     IOThreadInfoList *info_list = qmp_query_iothreads(NULL); | ||||||
|     IOThreadInfoList *info; |     IOThreadInfoList *info; | ||||||
|  |     IOThreadInfo *value; | ||||||
|  |  | ||||||
|     for (info = info_list; info; info = info->next) { |     for (info = info_list; info; info = info->next) { | ||||||
|         monitor_printf(mon, "%s: thread_id=%" PRId64 "\n", |         value = info->value; | ||||||
|                        info->value->id, info->value->thread_id); |         monitor_printf(mon, "%s:\n", value->id); | ||||||
|  |         monitor_printf(mon, "  thread_id=%" PRId64 "\n", value->thread_id); | ||||||
|  |         monitor_printf(mon, "  poll-max-ns=%" PRId64 "\n", value->poll_max_ns); | ||||||
|  |         monitor_printf(mon, "  poll-grow=%" PRId64 "\n", value->poll_grow); | ||||||
|  |         monitor_printf(mon, "  poll-shrink=%" PRId64 "\n", value->poll_shrink); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qapi_free_IOThreadInfoList(info_list); |     qapi_free_IOThreadInfoList(info_list); | ||||||
|   | |||||||
| @@ -1208,6 +1208,7 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) | |||||||
| { | { | ||||||
|     const char *sec_model = qemu_opt_get(opts, "security_model"); |     const char *sec_model = qemu_opt_get(opts, "security_model"); | ||||||
|     const char *path = qemu_opt_get(opts, "path"); |     const char *path = qemu_opt_get(opts, "path"); | ||||||
|  |     Error *err = NULL; | ||||||
|  |  | ||||||
|     if (!sec_model) { |     if (!sec_model) { | ||||||
|         error_report("Security model not specified, local fs needs security model"); |         error_report("Security model not specified, local fs needs security model"); | ||||||
| @@ -1236,6 +1237,13 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) | |||||||
|         error_report("fsdev: No path specified"); |         error_report("fsdev: No path specified"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fsdev_throttle_parse_opts(opts, &fse->fst, &err); | ||||||
|  |     if (err) { | ||||||
|  |         error_reportf_err(err, "Throttle configuration is not valid: "); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     fse->path = g_strdup(path); |     fse->path = g_strdup(path); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								hw/9pfs/9p.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								hw/9pfs/9p.c
									
									
									
									
									
								
							| @@ -2374,7 +2374,7 @@ static void coroutine_fn v9fs_flush(void *opaque) | |||||||
|         /* |         /* | ||||||
|          * Wait for pdu to complete. |          * Wait for pdu to complete. | ||||||
|          */ |          */ | ||||||
|         qemu_co_queue_wait(&cancel_pdu->complete); |         qemu_co_queue_wait(&cancel_pdu->complete, NULL); | ||||||
|         cancel_pdu->cancelled = 0; |         cancel_pdu->cancelled = 0; | ||||||
|         pdu_free(cancel_pdu); |         pdu_free(cancel_pdu); | ||||||
|     } |     } | ||||||
| @@ -3010,7 +3010,6 @@ out_nofid: | |||||||
|  */ |  */ | ||||||
| static void coroutine_fn v9fs_lock(void *opaque) | static void coroutine_fn v9fs_lock(void *opaque) | ||||||
| { | { | ||||||
|     int8_t status; |  | ||||||
|     V9fsFlock flock; |     V9fsFlock flock; | ||||||
|     size_t offset = 7; |     size_t offset = 7; | ||||||
|     struct stat stbuf; |     struct stat stbuf; | ||||||
| @@ -3018,7 +3017,6 @@ static void coroutine_fn v9fs_lock(void *opaque) | |||||||
|     int32_t fid, err = 0; |     int32_t fid, err = 0; | ||||||
|     V9fsPDU *pdu = opaque; |     V9fsPDU *pdu = opaque; | ||||||
|  |  | ||||||
|     status = P9_LOCK_ERROR; |  | ||||||
|     v9fs_string_init(&flock.client_id); |     v9fs_string_init(&flock.client_id); | ||||||
|     err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type, |     err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type, | ||||||
|                         &flock.flags, &flock.start, &flock.length, |                         &flock.flags, &flock.start, &flock.length, | ||||||
| @@ -3044,15 +3042,15 @@ static void coroutine_fn v9fs_lock(void *opaque) | |||||||
|     if (err < 0) { |     if (err < 0) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     status = P9_LOCK_SUCCESS; |     err = pdu_marshal(pdu, offset, "b", P9_LOCK_SUCCESS); | ||||||
|  |     if (err < 0) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     err += offset; | ||||||
|  |     trace_v9fs_lock_return(pdu->tag, pdu->id, P9_LOCK_SUCCESS); | ||||||
| out: | out: | ||||||
|     put_fid(pdu, fidp); |     put_fid(pdu, fidp); | ||||||
| out_nofid: | out_nofid: | ||||||
|     err = pdu_marshal(pdu, offset, "b", status); |  | ||||||
|     if (err > 0) { |  | ||||||
|         err += offset; |  | ||||||
|     } |  | ||||||
|     trace_v9fs_lock_return(pdu->tag, pdu->id, status); |  | ||||||
|     pdu_complete(pdu, err); |     pdu_complete(pdu, err); | ||||||
|     v9fs_string_free(&flock.client_id); |     v9fs_string_free(&flock.client_id); | ||||||
| } | } | ||||||
| @@ -3531,6 +3529,10 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp) | |||||||
|         error_setg(errp, "share path %s is not a directory", fse->path); |         error_setg(errp, "share path %s is not a directory", fse->path); | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     s->ctx.fst = &fse->fst; | ||||||
|  |     fsdev_throttle_init(s->ctx.fst); | ||||||
|  |  | ||||||
|     v9fs_path_free(&path); |     v9fs_path_free(&path); | ||||||
|  |  | ||||||
|     rc = 0; |     rc = 0; | ||||||
| @@ -3551,6 +3553,7 @@ void v9fs_device_unrealize_common(V9fsState *s, Error **errp) | |||||||
|     if (s->ops->cleanup) { |     if (s->ops->cleanup) { | ||||||
|         s->ops->cleanup(&s->ctx); |         s->ops->cleanup(&s->ctx); | ||||||
|     } |     } | ||||||
|  |     fsdev_throttle_cleanup(s->ctx.fst); | ||||||
|     g_free(s->tag); |     g_free(s->tag); | ||||||
|     g_free(s->ctx.fs_root); |     g_free(s->ctx.fs_root); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -247,6 +247,7 @@ int coroutine_fn v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp, | |||||||
|     if (v9fs_request_cancelled(pdu)) { |     if (v9fs_request_cancelled(pdu)) { | ||||||
|         return -EINTR; |         return -EINTR; | ||||||
|     } |     } | ||||||
|  |     fsdev_co_throttle_request(s->ctx.fst, true, iov, iovcnt); | ||||||
|     v9fs_co_run_in_worker( |     v9fs_co_run_in_worker( | ||||||
|         { |         { | ||||||
|             err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset); |             err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset); | ||||||
| @@ -266,6 +267,7 @@ int coroutine_fn v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp, | |||||||
|     if (v9fs_request_cancelled(pdu)) { |     if (v9fs_request_cancelled(pdu)) { | ||||||
|         return -EINTR; |         return -EINTR; | ||||||
|     } |     } | ||||||
|  |     fsdev_co_throttle_request(s->ctx.fst, false, iov, iovcnt); | ||||||
|     v9fs_co_run_in_worker( |     v9fs_co_run_in_worker( | ||||||
|         { |         { | ||||||
|             err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset); |             err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset); | ||||||
|   | |||||||
| @@ -198,7 +198,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, | |||||||
|     state->dev_count = id_list->len; |     state->dev_count = id_list->len; | ||||||
|     state->devs = g_new0(typeof(*state->devs), state->dev_count); |     state->devs = g_new0(typeof(*state->devs), state->dev_count); | ||||||
|     for (i = 0; i < id_list->len; i++) { |     for (i = 0; i < id_list->len; i++) { | ||||||
|         state->devs[i].cpu =  id_list->cpus[i].cpu; |         state->devs[i].cpu =  CPU(id_list->cpus[i].cpu); | ||||||
|         state->devs[i].arch_id = id_list->cpus[i].arch_id; |         state->devs[i].arch_id = id_list->cpus[i].arch_id; | ||||||
|     } |     } | ||||||
|     memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state, |     memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state, | ||||||
|   | |||||||
| @@ -49,6 +49,7 @@ static inline void tco_timer_reload(TCOIORegs *tr) | |||||||
| static inline void tco_timer_stop(TCOIORegs *tr) | static inline void tco_timer_stop(TCOIORegs *tr) | ||||||
| { | { | ||||||
|     tr->expire_time = -1; |     tr->expire_time = -1; | ||||||
|  |     timer_del(tr->tco_timer); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void tco_timer_expired(void *opaque) | static void tco_timer_expired(void *opaque) | ||||||
|   | |||||||
| @@ -177,6 +177,7 @@ static void clipper_machine_init(MachineClass *mc) | |||||||
| { | { | ||||||
|     mc->desc = "Alpha DP264/CLIPPER"; |     mc->desc = "Alpha DP264/CLIPPER"; | ||||||
|     mc->init = clipper_init; |     mc->init = clipper_init; | ||||||
|  |     mc->block_default_type = IF_IDE; | ||||||
|     mc->max_cpus = 4; |     mc->max_cpus = 4; | ||||||
|     mc->is_default = 1; |     mc->is_default = 1; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -86,6 +86,11 @@ static void bcm2835_peripherals_init(Object *obj) | |||||||
|     object_property_add_const_link(OBJECT(&s->property), "dma-mr", |     object_property_add_const_link(OBJECT(&s->property), "dma-mr", | ||||||
|                                    OBJECT(&s->gpu_bus_mr), &error_abort); |                                    OBJECT(&s->gpu_bus_mr), &error_abort); | ||||||
|  |  | ||||||
|  |     /* Random Number Generator */ | ||||||
|  |     object_initialize(&s->rng, sizeof(s->rng), TYPE_BCM2835_RNG); | ||||||
|  |     object_property_add_child(obj, "rng", OBJECT(&s->rng), NULL); | ||||||
|  |     qdev_set_parent_bus(DEVICE(&s->rng), sysbus_get_default()); | ||||||
|  |  | ||||||
|     /* Extended Mass Media Controller */ |     /* Extended Mass Media Controller */ | ||||||
|     object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI); |     object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI); | ||||||
|     object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL); |     object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL); | ||||||
| @@ -226,6 +231,16 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) | |||||||
|     sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0, |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0, | ||||||
|                       qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY)); |                       qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY)); | ||||||
|  |  | ||||||
|  |     /* Random Number Generator */ | ||||||
|  |     object_property_set_bool(OBJECT(&s->rng), true, "realized", &err); | ||||||
|  |     if (err) { | ||||||
|  |         error_propagate(errp, err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memory_region_add_subregion(&s->peri_mr, RNG_OFFSET, | ||||||
|  |                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0)); | ||||||
|  |  | ||||||
|     /* Extended Mass Media Controller */ |     /* Extended Mass Media Controller */ | ||||||
|     object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg", |     object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg", | ||||||
|                             &err); |                             &err); | ||||||
|   | |||||||
| @@ -71,6 +71,8 @@ static void cubieboard_init(MachineState *machine) | |||||||
|     memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, |     memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, | ||||||
|                                 &s->sdram); |                                 &s->sdram); | ||||||
|  |  | ||||||
|  |     /* TODO create and connect IDE devices for ide_drive_get() */ | ||||||
|  |  | ||||||
|     cubieboard_binfo.ram_size = machine->ram_size; |     cubieboard_binfo.ram_size = machine->ram_size; | ||||||
|     cubieboard_binfo.kernel_filename = machine->kernel_filename; |     cubieboard_binfo.kernel_filename = machine->kernel_filename; | ||||||
|     cubieboard_binfo.kernel_cmdline = machine->kernel_cmdline; |     cubieboard_binfo.kernel_cmdline = machine->kernel_cmdline; | ||||||
| @@ -82,6 +84,8 @@ static void cubieboard_machine_init(MachineClass *mc) | |||||||
| { | { | ||||||
|     mc->desc = "cubietech cubieboard"; |     mc->desc = "cubietech cubieboard"; | ||||||
|     mc->init = cubieboard_init; |     mc->init = cubieboard_init; | ||||||
|  |     mc->block_default_type = IF_IDE; | ||||||
|  |     mc->units_per_default_bus = 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| DEFINE_MACHINE("cubieboard", cubieboard_machine_init) | DEFINE_MACHINE("cubieboard", cubieboard_machine_init) | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
|  | #include "qemu/log.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "hw/boards.h" | #include "hw/boards.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| @@ -74,6 +75,9 @@ | |||||||
| /* PMU SFR base address */ | /* PMU SFR base address */ | ||||||
| #define EXYNOS4210_PMU_BASE_ADDR            0x10020000 | #define EXYNOS4210_PMU_BASE_ADDR            0x10020000 | ||||||
|  |  | ||||||
|  | /* Clock controller SFR base address */ | ||||||
|  | #define EXYNOS4210_CLK_BASE_ADDR            0x10030000 | ||||||
|  |  | ||||||
| /* Display controllers (FIMD) */ | /* Display controllers (FIMD) */ | ||||||
| #define EXYNOS4210_FIMD0_BASE_ADDR          0x11C00000 | #define EXYNOS4210_FIMD0_BASE_ADDR          0x11C00000 | ||||||
|  |  | ||||||
| @@ -138,6 +142,16 @@ void exynos4210_write_secondary(ARMCPU *cpu, | |||||||
|                        info->smp_loader_start); |                        info->smp_loader_start); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static uint64_t exynos4210_calc_affinity(int cpu) | ||||||
|  | { | ||||||
|  |     uint64_t mp_affinity; | ||||||
|  |  | ||||||
|  |     /* Exynos4210 has 0x9 as cluster ID */ | ||||||
|  |     mp_affinity = (0x9 << ARM_AFF1_SHIFT) | cpu; | ||||||
|  |  | ||||||
|  |     return mp_affinity; | ||||||
|  | } | ||||||
|  |  | ||||||
| Exynos4210State *exynos4210_init(MemoryRegion *system_mem, | Exynos4210State *exynos4210_init(MemoryRegion *system_mem, | ||||||
|         unsigned long ram_size) |         unsigned long ram_size) | ||||||
| { | { | ||||||
| @@ -163,6 +177,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         s->cpu[n] = ARM_CPU(cpuobj); |         s->cpu[n] = ARM_CPU(cpuobj); | ||||||
|  |         object_property_set_int(cpuobj, exynos4210_calc_affinity(n), | ||||||
|  |                                 "mp-affinity", &error_abort); | ||||||
|         object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR, |         object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR, | ||||||
|                                 "reset-cbar", &error_abort); |                                 "reset-cbar", &error_abort); | ||||||
|         object_property_set_bool(cpuobj, true, "realized", &error_fatal); |         object_property_set_bool(cpuobj, true, "realized", &error_fatal); | ||||||
| @@ -297,6 +313,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, | |||||||
|     */ |     */ | ||||||
|     sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL); |     sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL); | ||||||
|  |  | ||||||
|  |     sysbus_create_simple("exynos4210.clk", EXYNOS4210_CLK_BASE_ADDR, NULL); | ||||||
|  |  | ||||||
|     /* PWM */ |     /* PWM */ | ||||||
|     sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR, |     sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR, | ||||||
|                           s->irq_table[exynos4210_get_irq(22, 0)], |                           s->irq_table[exynos4210_get_irq(22, 0)], | ||||||
|   | |||||||
| @@ -363,6 +363,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) | |||||||
|         sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]); |         sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /* TODO create and connect IDE devices for ide_drive_get() */ | ||||||
|  |  | ||||||
|     highbank_binfo.ram_size = ram_size; |     highbank_binfo.ram_size = ram_size; | ||||||
|     highbank_binfo.kernel_filename = kernel_filename; |     highbank_binfo.kernel_filename = kernel_filename; | ||||||
|     highbank_binfo.kernel_cmdline = kernel_cmdline; |     highbank_binfo.kernel_cmdline = kernel_cmdline; | ||||||
| @@ -405,7 +407,8 @@ static void highbank_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "Calxeda Highbank (ECX-1000)"; |     mc->desc = "Calxeda Highbank (ECX-1000)"; | ||||||
|     mc->init = highbank_init; |     mc->init = highbank_init; | ||||||
|     mc->block_default_type = IF_SCSI; |     mc->block_default_type = IF_IDE; | ||||||
|  |     mc->units_per_default_bus = 1; | ||||||
|     mc->max_cpus = 4; |     mc->max_cpus = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -421,7 +424,8 @@ static void midway_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "Calxeda Midway (ECX-2000)"; |     mc->desc = "Calxeda Midway (ECX-2000)"; | ||||||
|     mc->init = midway_init; |     mc->init = midway_init; | ||||||
|     mc->block_default_type = IF_SCSI; |     mc->block_default_type = IF_IDE; | ||||||
|  |     mc->units_per_default_bus = 1; | ||||||
|     mc->max_cpus = 4; |     mc->max_cpus = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -259,7 +259,7 @@ static void realview_init(MachineState *machine, | |||||||
|         } |         } | ||||||
|         n = drive_get_max_bus(IF_SCSI); |         n = drive_get_max_bus(IF_SCSI); | ||||||
|         while (n >= 0) { |         while (n >= 0) { | ||||||
|             pci_create_simple(pci_bus, -1, "lsi53c895a"); |             lsi53c895a_create(pci_bus); | ||||||
|             n--; |             n--; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -443,7 +443,6 @@ static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "ARM RealView Platform Baseboard Explore for Cortex-A9"; |     mc->desc = "ARM RealView Platform Baseboard Explore for Cortex-A9"; | ||||||
|     mc->init = realview_pbx_a9_init; |     mc->init = realview_pbx_a9_init; | ||||||
|     mc->block_default_type = IF_SCSI; |  | ||||||
|     mc->max_cpus = 4; |     mc->max_cpus = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -998,6 +998,7 @@ static void spitzpda_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)"; |     mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)"; | ||||||
|     mc->init = spitz_init; |     mc->init = spitz_init; | ||||||
|  |     mc->block_default_type = IF_IDE; | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo spitzpda_type = { | static const TypeInfo spitzpda_type = { | ||||||
| @@ -1012,6 +1013,7 @@ static void borzoipda_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)"; |     mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)"; | ||||||
|     mc->init = borzoi_init; |     mc->init = borzoi_init; | ||||||
|  |     mc->block_default_type = IF_IDE; | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo borzoipda_type = { | static const TypeInfo borzoipda_type = { | ||||||
| @@ -1026,6 +1028,7 @@ static void terrierpda_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)"; |     mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)"; | ||||||
|     mc->init = terrier_init; |     mc->init = terrier_init; | ||||||
|  |     mc->block_default_type = IF_IDE; | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo terrierpda_type = { | static const TypeInfo terrierpda_type = { | ||||||
|   | |||||||
| @@ -263,6 +263,7 @@ static void tosapda_machine_init(MachineClass *mc) | |||||||
| { | { | ||||||
|     mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)"; |     mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)"; | ||||||
|     mc->init = tosa_init; |     mc->init = tosa_init; | ||||||
|  |     mc->block_default_type = IF_IDE; | ||||||
| } | } | ||||||
|  |  | ||||||
| DEFINE_MACHINE("tosa", tosapda_machine_init) | DEFINE_MACHINE("tosa", tosapda_machine_init) | ||||||
|   | |||||||
| @@ -290,7 +290,7 @@ static void versatile_init(MachineState *machine, int board_id) | |||||||
|     } |     } | ||||||
|     n = drive_get_max_bus(IF_SCSI); |     n = drive_get_max_bus(IF_SCSI); | ||||||
|     while (n >= 0) { |     while (n >= 0) { | ||||||
|         pci_create_simple(pci_bus, -1, "lsi53c895a"); |         lsi53c895a_create(pci_bus); | ||||||
|         n--; |         n--; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -752,7 +752,6 @@ static void vexpress_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     mc->desc = "ARM Versatile Express"; |     mc->desc = "ARM Versatile Express"; | ||||||
|     mc->init = vexpress_common_init; |     mc->init = vexpress_common_init; | ||||||
|     mc->block_default_type = IF_SCSI; |  | ||||||
|     mc->max_cpus = 4; |     mc->max_cpus = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -535,7 +535,6 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic) | |||||||
| static void create_gic(VirtMachineState *vms, qemu_irq *pic) | static void create_gic(VirtMachineState *vms, qemu_irq *pic) | ||||||
| { | { | ||||||
|     /* We create a standalone GIC */ |     /* We create a standalone GIC */ | ||||||
|     VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); |  | ||||||
|     DeviceState *gicdev; |     DeviceState *gicdev; | ||||||
|     SysBusDevice *gicbusdev; |     SysBusDevice *gicbusdev; | ||||||
|     const char *gictype; |     const char *gictype; | ||||||
| @@ -605,7 +604,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) | |||||||
|  |  | ||||||
|     fdt_add_gic_node(vms); |     fdt_add_gic_node(vms); | ||||||
|  |  | ||||||
|     if (type == 3 && !vmc->no_its) { |     if (type == 3 && vms->its) { | ||||||
|         create_its(vms, gicdev); |         create_its(vms, gicdev); | ||||||
|     } else if (type == 2) { |     } else if (type == 2) { | ||||||
|         create_v2m(vms, pic); |         create_v2m(vms, pic); | ||||||
| @@ -1378,6 +1377,7 @@ static void machvirt_init(MachineState *machine) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         object_property_set_bool(cpuobj, true, "realized", NULL); |         object_property_set_bool(cpuobj, true, "realized", NULL); | ||||||
|  |         object_unref(cpuobj); | ||||||
|     } |     } | ||||||
|     fdt_add_timer_nodes(vms); |     fdt_add_timer_nodes(vms); | ||||||
|     fdt_add_cpu_nodes(vms); |     fdt_add_cpu_nodes(vms); | ||||||
| @@ -1480,6 +1480,20 @@ static void virt_set_highmem(Object *obj, bool value, Error **errp) | |||||||
|     vms->highmem = value; |     vms->highmem = value; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static bool virt_get_its(Object *obj, Error **errp) | ||||||
|  | { | ||||||
|  |     VirtMachineState *vms = VIRT_MACHINE(obj); | ||||||
|  |  | ||||||
|  |     return vms->its; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void virt_set_its(Object *obj, bool value, Error **errp) | ||||||
|  | { | ||||||
|  |     VirtMachineState *vms = VIRT_MACHINE(obj); | ||||||
|  |  | ||||||
|  |     vms->its = value; | ||||||
|  | } | ||||||
|  |  | ||||||
| static char *virt_get_gic_version(Object *obj, Error **errp) | static char *virt_get_gic_version(Object *obj, Error **errp) | ||||||
| { | { | ||||||
|     VirtMachineState *vms = VIRT_MACHINE(obj); |     VirtMachineState *vms = VIRT_MACHINE(obj); | ||||||
| @@ -1540,6 +1554,7 @@ type_init(machvirt_machine_init); | |||||||
| static void virt_2_9_instance_init(Object *obj) | static void virt_2_9_instance_init(Object *obj) | ||||||
| { | { | ||||||
|     VirtMachineState *vms = VIRT_MACHINE(obj); |     VirtMachineState *vms = VIRT_MACHINE(obj); | ||||||
|  |     VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); | ||||||
|  |  | ||||||
|     /* EL3 is disabled by default on virt: this makes us consistent |     /* EL3 is disabled by default on virt: this makes us consistent | ||||||
|      * between KVM and TCG for this board, and it also allows us to |      * between KVM and TCG for this board, and it also allows us to | ||||||
| @@ -1579,6 +1594,19 @@ static void virt_2_9_instance_init(Object *obj) | |||||||
|                                     "Set GIC version. " |                                     "Set GIC version. " | ||||||
|                                     "Valid values are 2, 3 and host", NULL); |                                     "Valid values are 2, 3 and host", NULL); | ||||||
|  |  | ||||||
|  |     if (vmc->no_its) { | ||||||
|  |         vms->its = false; | ||||||
|  |     } else { | ||||||
|  |         /* Default allows ITS instantiation */ | ||||||
|  |         vms->its = true; | ||||||
|  |         object_property_add_bool(obj, "its", virt_get_its, | ||||||
|  |                                  virt_set_its, NULL); | ||||||
|  |         object_property_set_description(obj, "its", | ||||||
|  |                                         "Set on/off to enable/disable " | ||||||
|  |                                         "ITS instantiation", | ||||||
|  |                                         NULL); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     vms->memmap = a15memmap; |     vms->memmap = a15memmap; | ||||||
|     vms->irqmap = a15irqmap; |     vms->irqmap = a15irqmap; | ||||||
| } | } | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user